1、何为 Intent
Intent(意图)主要用于 Android 各组件之间的交互,它可以指明当前组件想要执行的动作,也可以传递数据。一般来说,Intent 常被用来启动 Activity、Service 以及 BroadCastReceiver。
Intent 分为显式 Intent 和隐式 Intent,先来了解一下显示 Intent。
2、显式 Intent
在 Intent 的参数中明确的设置要跳转的组件的包名和类名并跳转,叫显式 Intent。
具体使用方法如下:
mBtnIntent.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View pView) {
Intent lIntent = new Intent(MainActivity.this, SecondActivity.class);
startActivity(lIntent);
}
});
效果:
3、隐式 Intent
相比显示 Intent 明确指明想要启动的组件包名,隐式 Intent 恰好相反,它不明确指明想要启动哪一个组件,而是在清单文件下相应组件标签下配置
那我们现在就去修改一下示例中的 SecondActivity 的
我们配置了 action 和 category 两个标签,来了解一下这两个标签的含义:
action
这个标签是必须添加的,其内容有系统内置的(详见 Android Intent Action 大全),也可以自定义。-
category
表示组件的类别,一个组件可以配置多个 category 标签。常用的 category 标签有3个:DEFAULT —— 默认动作
HOME —— 设置为本地桌面应用
-
LAUNCHER —— 项目主 Activity
其中 DEFAULT 是最常用的标签,本例中也是使用的 DEFAULT。
然后再修改一下代码中启动组件的逻辑,改为隐式 Intent 启动:
mBtnIntent.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View pView) {
Intent lIntent = new Intent("com.ginkwang.activitysample.ACTION_START");
startActivity(lIntent);
}
});
这里有个问题,隐式 Intent 不是要 action 和 category 两个标签都要一一对应才能启动组件的吗?
对,是这样的没错。但是咱们在 category 标签下设置的是默认动作 —— DEFAULT,它会在 startActivity() 方法执行时,自动将 category 添加到 Intent 中。
同样,如果 category 设置的是别的自定义的类别的话,就需要我们自己在启动组件时填写 category 信息了。
现在我们重新启动一下应用:
data 代表数据检测,用于精确的指定当前活动能够相应什么类型的数据。data 标签可配置如下内容:
android:host:指定主机名,例如:google.com
android:port:制定主机端口,例如: 80
android:path:指定URL的有效路径值,例如: /index/examples
android:mimeType:指定组件可以执行的数据类型,例如:image/jpeg,video/*
android:scheme:指定特定的模式,例如:content,http
和 action、category 一样,只有 data 标签中指定的内容与 Intent 中携带的 data 内容相同时,当前活动才能响应 Intent。
接下来展示,调用系统浏览器并打开指定网址:
mBtnBrowser.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View pView) {
Intent intent = new Intent();
intent.setAction("android.intent.action.VIEW");
Uri url = Uri.parse("https://www.baidu.com/");
intent.setData(url);
startActivity(intent);
}
});
我们先定义了一个系统内置的 action 常量,然后通过 Uri.parse 方法将百度网址解析成一个 Uri 对象,最后通过 Intent 的 setData 方法将 Uri 对象传递进去。
看一下效果:
调用本地浏览器的操作并不难,其实我们也可以新建一个活动,并让它响应打开网页的操作(此处的场景可以想象一下在某 APP 中点击一段网址,系统会自动筛选可以打开网页的 APP,然后给你一个选择,用哪个 APP 打开这段网址)。
上代码:
新建一个 ThirdActivity,代码不表。然后在清单文件中配置
注意,最重要的一句就是
,这表明此活动接收的数据协议为 https(应为我们测试的百度网址是 https 协议的),这样就表明 ThirdActivity 可以和浏览器一样,可以打开网页了。
注意:
这个操作我只在原生 Android 系统上面测试成功了,在 MIUI 和 FLYME 上面则没有选择打开操作的过程,直接就在系统浏览器上面打开网址了。
4、Intent 传递数据
使用 Intent 可以打开活动,那能不能打开活动的同时,给下一个活动传递数据呢?答案当然是可以的。使用 Intent 的 putExtra 方法即可,看代码;
Intent lIntent = new Intent(MainActivity.this, SecondActivity.class);
lIntent.putExtra("intent_data", "hello_second_activity");
startActivity(lIntent);
通过 Intent 打开 SecondActivity,然后通过 putExtra 方法给 SecondActivity 传递数据。putExtra 接收两个参数,第一个参数是本次传值的 key,第二个参数才是传的值 value。
然后 SecondActivity 接收传递值的代码:
//接收 MAinActivity 传递来的数据
String intentStr = getIntent().getStringExtra("intent_data");
Toast.makeText(SecondActivity.this, intentStr, Toast.LENGTH_LONG).show();
这里的逻辑也十分简单,通过 getIntent 获取到 Intent,然后再调用 getStringExtra 方法,传入 MainActivity 里约定好的 key 值,就接收到 Intent 传递来的数据了。
刚才演示的 Intent 传递的是 String 型的数据,同理 Integer、Boolean 等基本数据类型数据,都可以按照上面的操作进行传递,只是在接收的时候按照相应的数据类型调用相应的方法进行接收就 OK 了。
那如果有一个实体类对象要进行传递呢?
总不能将其字段一一拆分出来然后按照基本数据类型的传递方法进行传递吧?这样不是不行,只是太繁琐了。如果这个类里面有50个字段的话,那工程量是有多大?
好了,不啰嗦。Intent 有一个方法 putExtras 可以实现我们的需求。直接上代码:
先新建一个实体类,取名 IntentTestBean
,并实现 Serializable 类,将其序列化:
public class IntentTestBean implements Serializable {
private String name;
private String age;
private String sex;
public String getName() {
return name;
}
public void setName(String pName) {
name = pName;
}
public String getAge() {
return age;
}
public void setAge(String pAge) {
age = pAge;
}
public String getSex() {
return sex;
}
public void setSex(String pSex) {
sex = pSex;
}
@Override
public String toString() {
return "IntentTestBean{" +
"name='" + name + '\'' +
", age='" + age + '\'' +
", sex='" + sex + '\'' +
'}';
}
}
我们从 SecondActivity 中往 ThirdActivity 传递实体类,那 SecondActivity 里面的逻辑为:
/**
* 向 ThirdActivity 发送实体类数据
*/
private void intentBean() {
IntentTestBean lIntentTestBean = new IntentTestBean();
lIntentTestBean.setName("ginkwang");
lIntentTestBean.setAge("24");
lIntentTestBean.setSex("男");
Intent lIntent = new Intent(SecondActivity.this, ThirdActivity.class);
Bundle lBundle = new Bundle();
lBundle.putSerializable("intent_bean", lIntentTestBean);
lIntent.putExtras(lBundle);
startActivity(lIntent);
}
先把 IntentTestBean 类赋值,然后新建 Intent,指定跳转目标,因为我们要传递的是对象,所以必须要使用 BundleBundle主要用于传递数据,它保存的数据,是以key-value(键值对)的形式存在的
。新建 Bundle 对象 ,接着将 IntentTestBean 类传入进 Bundle,然后调用 Intent 的 putExtras 方法,将实体类传递出去。
在 ThirdActivity 中接收数据的逻辑如下:
//接收 SecondActivity 传递的实体类数据
IntentTestBean lIntentTestBean = (IntentTestBean) getIntent().
getSerializableExtra("intent_bean");
if (lIntentTestBean != null) {
Toast.makeText(ThirdActivity.this, lIntentTestBean.toString(), Toast.LENGTH_LONG).show();
}
这里的逻辑很简单,和接收 String 类型数据的方法类似,就是这里需要使用 getSerializableExtra 方法获取实体类对象,另外还有一个类型强制转换。
最终效果:
5、Intent 传递数据
说完了传递数据,接下来讲一下 Intent 返回数据给上一个活动。
利用 Intent 返回数据,就不能用 StartActivity 启动活动了。Activity 中提供了一个 startActivityForResult 方法,此方法也可用于启动活动,并在活动销毁时传递数据给上一个活动。这正是我们想要的!
startActivityForResult 方法接收两个参数,第一个还是携带数据的 Intent,第二个是请求码,用于回调时对数据来源的判断。
看一下启动活动的代码:
Intent lIntent = new Intent(MainActivity.this, SecondActivity.class);
lIntent.putExtra("intent_data", "hello_second_activity");
startActivityForResult(lIntent, 1);
和之前几乎一样,就是把 startActivity 换成了 startActivityForResult,请求码设置为1。
然后看一下 SecondActivity 中返回数据的代码:
//向 MainActivity 返回数据
Intent lIntent = new Intent();
lIntent.putExtra("return_data", "hello_main_activity");
setResult(RESULT_OK, lIntent);
finish();
还是新建一个 Intent 对象,但不给它指定任何意图,仅仅用作传递数据。接着调用 setResult 方法,此方法专门用于向上一个活动传递数据,接收两个参数,第一个是 Intent,第二个是处理结果,一般使用 RESULT_OK 或 RESULT_CANCELED。最后调用 finish 方法销毁当前活动。
之后还要在 MainActivity 中重写一个 onActivityResult 用作回调:
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
case 1://判断请求码是否为1,缺失是否从 SecondActivity 返回的
if (resultCode == RESULT_OK) {//再判断结果值看是否返回结果成功
String resultData = data.getStringExtra("return_data");
Toast.makeText(MainActivity.this, resultData, Toast.LENGTH_LONG).show();
}
break;
}
}
相关逻辑在代码中都标注好了,另外实体类的数据返回也是如此,这里就不贴代码了。
最后,效果图: