Intent,意图,一般可以被用于启动活动、启动服务以及发送广播等场景,现在先说一下启动活动这部分。
Intent分为显式Intent和隐式Intent。
一、显式Intent的使用
Intent有多个构造函数的重载,常用的一个有Intent(Context packageContext, Class<?> cls)。这个构造函数需要接受两个参数,第一个参数Context要求提供一个启动活动的上下文,一般就是自身的活动类名,而第二个参数Class则是需要指定想要启动的目标活动类名。
例子:
新建一个命名为FirstActivity的活动,在其界面布局中添加一个Button,现在需要实现点击Button后跳转到另一个活动:SecondActivity,那么可以在FirstActivity中添加如下代码
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.firstlayout);
Button button1 = (Button) findViewById(R.id.button1);
button1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(FirstActivity.this, SecondActivity.class);
startActivity(intent);
}
});
}
如以上例子,需要在点击FirstActivity中的Button控件后跳转到SecondActivity,这时就可以通过Intent(意图)来实现。
注册Button的点击事件为Intent。Intent构造方法中的第一个参数为:FirstActivity.this,指的是需要启动跳转操作的是当前的活动;第二个参数为:SecondActivity.class,指的是需要跳转到的目标活动是SecondActivity。在构件好Intent后将该Intent对象传入startActivity()方法即可启动目标活动了。
二、隐式Intent的使用
相比显示Intent,隐式Intent稍微复杂一点。隐式Intent需要指定action、category等信息,交由系统分析这个Intent,然后才能找到相应的活动去启动。
首先,需要配置AndroidManifest.xml文件中<activity>标签下<intent-filter>标签的内容,可以指定当前活动能够响应的action和category。
<activity android:name=".SecondActivity">
<intent-filter>
<action android:name="MyApplication.intent.action.START" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.Mycategory" />
</intent-filter>
</activity>
这里的<action>标签指明了当前活动可以响应的action为MyApplication.intent.action.START,而<category>标签则包含了一些附加信息,可以更精确地指明当前活动能响应的Intent中还可能带有的category。
然后,修改需要通过Intent来启动该活动的FirstActivity中按钮的点击事件:
button1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent();
intent.setAction("MyApplication.intent.action.START");
//Intent intent = new Intent("MyApplication.intent.action.START");//设置Action另一种更加便捷的写法
//intent.addCategory("android.intent.category.DEFAULT");//这句省略也行,省略时在startActivity时会自动添加
intent.addCategory("android.intent.Mycategory");
startActivity(intent);
}
});
每个Intent只能指定一个action,但是可以指定多个category,只有当<action>和<category>中的内容和Intent中指定的action和category的内容一致时,这个活动才能响应Intent。
要注意的是,Activity中的intent.addCategory("android.intent.category.DEFAULT")可以省略不写,因为在startActivity方法运行的时候系统会自动添加这句,但是在AndroidManifest中的<Activity>中
<category android:name="android.intent.category.DEFAULT" />
是不可以省略的,因为在运行startActivity方法时,如果没有显示写intent.addCategory("android.intent.category.DEFAULT")这句,系统一定会自动添加,所以如果<Activity>中省略了
<category android:name="android.intent.category.DEFAULT" />
那么系统会找不到对应相匹配的活动来启动,从而报错:
android.content.ActivityNotFoundException: No Activity found to handle Intent { act=MyApplication.intent.action.START cat=[android.intent.Mycategory] }
(也就是说在设置<Activity>中的<action>标签时,<category android:name="android.intent.category.DEFAULT" />必须配套出现)
三、隐式Intent其他用法
隐式Intent还可以用来调用系统浏览器来展示页面、调用拨号程序、打开地图定位等。
在Activity中添加按钮控件,修改Activity中按钮的点击事件:
//调用系统默认浏览器打开网页
Button button1 = (Button) findViewById(R.id.button1);
button1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//Uri传值第一种方法
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse("https://www.baidu.com"));
startActivity(intent);
}
});
//调用拨号程序
Button button2 = (Button) findViewById(R.id.button2);
button2.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View v) {
//Uri传值第二种方法
Uri uri = Uri.parse("tel:10086");
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
startActivity(intent);
}
});
//打开地图定位
Button button3 = (Button) findViewById(R.id.button3);
button3.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View v) {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse("geo:39.868433,116.013426"));
startActivity(intent);
}
});
首先我们指定了Intent的Action为Intent.ACTION_VIEW,这是一个Android系统内置的动作,其常量值为android.intent.action.VIEW。然后通过Uri.parse()方法,将一段字符串解析成一个Uri对象,并将其通过setData方法传递给Intent(代码中的方法一,方法二则是通过Intent的构造方法直接将Uri对象传递进去)
四、向下一个活动传递数据
Intent还可以在启动活动的时候传递数据:我们可以通过Intent中提供的putExtra()方法,将我们想要传递的数据暂时存储在Intent中,并在另一个活动启动后,再从Intent中取出来。
示例:
在FirstActivity中需要将一个字符串传递给SecondActivity。
首先修改FirstActivity中的按钮点击事件
button1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String data = "I am FirstAvtivity";
Intent intent = new Intent(FirstActivity.this, SecondActivity.class);
intent.putExtra("key",data);
startActivity(intent);
}
});
这里的putExtra()方法接收了两个参数,第一个为键名,第二个才是想要传递的数据。SecondActivity需要通过键名取出FirstActivity从Intent传递过来的数据。
SecondActivity:
public class SecondActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.secondlayout);
Intent intent = getIntent();
String data = intent.getStringExtra("key");
Log.d("SecondActivity", data);
}
}
在SecondActivity中,首先通过getIntent()方法获取到用于启动SecondActivity的Intent,然后使用intent.getStringExtra()方法来获取键名为key的数据(由于传递的数据为String类型,所以使用的是getStringExtra()方法,若数据为整形类型和布尔类型,则相应地使用getIntExtra()方法和getBooleanExtra()方法)。
在运行FirstActivity后,点击button1按钮,跳转到了SecondActivity,查看logcat,可以看到SecondActivity成功取得了FirstActivity中传递过来的信息。
五、返回数据给上一个活动
数据可以从上一个活动流向下一个活动,那么如果需要下一个活动给予上一个活动一个反馈的数据呢?这时候就需要用到startActivityForResult()方法了。查看文档可以知道startActivityForResult()方法也是用于启动活动的,但这个方法能够在活动销毁时返回一个结果给上一个活动。
startActivityForResult()接收两个参数,第一个参数是Intent,第二个参数是请求码,请求码用于判断数据来源,因此必须具有唯一性。
示例:现在需要实现退出SecondActivity后SecondActivity给FirstActivity回传一个数据。
首先修改FirstActivity中button1点击事件,不使用startActivity而是使用startActivityForResult()方法来启动SecondActivity,同时传入一个唯一的请求码1。
button1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(FirstActivity.this, SecondActivity.class);
startActivityForResult(intent,1);
}
});
然后修改SecondActivity中button2的点击事件,添加传递数据的逻辑。
Button button2 = (Button) findViewById(R.id.button2);
button2.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View v) {
Intent intent = new Intent();
intent.putExtra("data_result", "I am SecondAcitivity");
setResult(RESULT_OK, intent);
finish();
}
});
从代码中可以看出,SecondActivity新建了一个Intent用于传递数据,同时也使用了一个setResult的方法,该方法接收了Intent对象,可以推测数据的传递就是通过该方法来进行的。
setResult()方法接收两个参数,第一个参数一般用于反馈当前活动对数据的处理结果,一般使用RESULT_OK或者RESULT_CANCELED这两个值,第二个参数则为需要传递的数据。
由于我们使用了startActivityForResult()方法来启动SecondActivity,与此对应的,在SecondActivity被销毁后会回调上一个活动的onActivityResult()方法,因此我们还需要在FirstActivity中重写该方法来获取数据。
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
case 1:
if (resultCode == RESULT_OK) {
String returnedData = data.getStringExtra("data_result");
Log.i("FirstActivity", returnedData);
}
break;
default:
}
}
onActivityResult()方法接收三个参数,第一个requestCode就是我们在通过startActivityForResult()方法启动活动的时候传入的唯一的请求码,在该方法中会根据这个请求码进行相应的逻辑处理;第二个参数resultCode就是我们在返回数据时调用的setResult()方法传入的处理结果;第三个参数则为用于传递数据的Intent对象。
在启动FirstActivity后先点击button1启动SecondActivity,再点击button2,销毁SecondActivity,返回到FirstActivity,看logcat打印
SecondActivity的数据成功传递回FirstActivity。
需要注意的是,这是通过点击Activity中的按钮进行对SecondActivity进行销毁的方法,若用户没有点击该按钮,而是点击手机中的返回键,那么这时数据就传递不回上一个活动了。
但是,我们可以通过在SecondActivity中重写onBackPressed()方法来解决这个问题:
@Override
public void onBackPressed() {
Intent intent = new Intent();
intent.putExtra("data_result", "I am SecondAcitivity");
setResult(RESULT_OK, intent);
finish();
}
这时在SecondActivity中点击Back键,logcat中依然会有打印,说明数据已成功传递到FirstActivity中。