启动 Activity 分为两种,显式调用和隐式调用。显式调用需要明确指出被启动组件的包名类名。隐式调用需要 Intent 能够匹配目标组件的 IntentFilter 中所设置的过滤信息,如果不匹配将无法启动目标 Activity
一、IntentFilter 简介
- 意图过滤器的意思
- manifest文件中activity标签的子标签。
- IntentFilter 的过滤信息(子标签)有 action、category、data
1、IntentFilter 注意点
1、一个activity下可能有多个intentfilter,一个intent只要能匹配任何一组intentFilter下的信息便可成功启动activity。
2、一个intentFilter中action 、category、data都可以有多个。
3、想要隐式开启activity 必须在intentFilter添加 <category android:name=“android.intent.category.DEFAULT” />
二、Action规则
1、系统定义了一些Action字符串
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
2、我们也可以自定义Action字符串
<activity android:name=".ActivityC">
<intent-filter>
<action android:name="com.example.test" />//添加自定义字符串即可
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
3、Action匹配规则
1、intentFilter中的action可以有很多,只要intent添加的action和intentFilter中其中一个action匹配即可
2、action区分大小写,大小写不同的字符串是不同的action。
3、intentFilter 至少要有一个action
4、栗子
Intent intent = new Intent();
intent.setAction("com.example.test"); //intent 添加action。匹配intent-filter 下的一个action即可成功打开activity
startActivity(intent);
<activity android:name=".ActivityC">
<intent-filter>
<action android:name="com.example.test" />
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
三、Category规则
1、匹配规则
1、intentFilter中的category可以有很多,只要intent设置的category和intentFilter的匹配即可。2、系统在调用startActivity或者startActivityForResult时,会默认为intent加上了android.intent.category.DEFAULT这个category。所以为了activity能够接受隐式调用必须在intentFilter下设置android.intent.category.DEFAULT。
2、栗子
mainactivity开启main2Activity。
#MainActivity
#main2activity的配置
#点击按钮(直接崩溃 如下)
找不到activity?我明明添加了action还和intentFilter下的其中一个action匹配,并且我添加了category也匹配了。这是为啥???还记得我们之前说过得系统在调用startActivity或者startActivityForResult时,默认为intent加上了android.intent.category.DEFAULT这个categor所以要想隐式开启activity这个categrory必须添加待intent-filter中。
Intent intent = new Intent();
intent.setAction("com.example.test1");
intent.addCategory(Intent.CATEGORY_DEFAULT); // 实际还有这个 系统封装在startActivity中了
intent.addCategory("com.example.category2");
startActivity(intent);
四、 data匹配规则
1、data简介
data由两部分组成mimeType和URI,语法如下。
<data android:scheme="string"
android:host="string"
android:port="string"
android:path="string"
android:pathPattern="string"
android:pathPrefix="string"
android:mimeType="string"/>
2、mimeType
1、媒体类型,如image/jpeg 、audio/mpeng4-generic、vodeo/* 可以表示图片文本视屏等不同媒体格式。
2、data 中可以不指定这个属性。
3、假如data指定了mimeType,未制定scheme,scheme默认为content或file
4、scheme必须为小写字母
3、URI结构
<scheme>://<host>:<port>/[<path>|<pathPrefix>|<pathPattern>]
(1)scheme:URI的模式,常见为http,https,file,content这些字符串,当然也可以为其他任意字符串
(2) Host:URI的主机名比如 “www.baidu.com”
(3)Port:URI中的端口号,比如80
(4)parth ,pathPattern,pathPrefix 这三个参数表示路径信息。
4、栗子
(1)简单使用
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data
android:host="com.test"
android:scheme="test" />
</intent-filter>
如上就是最简单的url匹配了,此时只要是 uri的scheme为test,host为test://com.text 就能正确匹配。
startActivity(Intent().apply {
action = Intent.ACTION_VIEW
addCategory(Intent.CATEGORY_DEFAULT)
data = Uri.parse("test://com.test")
// 如下都满足
// data = Uri.parse("test://com.test/b")
// data = Uri.parse("test://com.test/b/a.txt")
// data = Uri.parse("test://com.test/b/?articleId=1")
})
(2)path属性
这个代表指定精确的path ,错一点就匹配失败如
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data
<!-- path 这里必须以/开头 -->
android:path="/a"
android:host="com.test"
android:scheme="test" />
</intent-filter>
startActivity(Intent().apply {
action = Intent.ACTION_VIEW
addCategory(Intent.CATEGORY_DEFAULT)
data = Uri.parse("test://com.test/a")
// data = Uri.parse("test://com.test/a/b") // 匹配失败
})
(3)pathPattern
上面介绍了path的用法可是弊端太多如我想支持 b路径下的所有怎么办呢?我们可以如下配置
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data
android:pathPattern="/b/.*"
android:host="com.test"
android:scheme="test" />
</intent-filter>
pathPattern这个类似正则的概念,但是系统支持的正则符号太少:
. 表示任意字符
* 表示0个或者多个
[] 表示范围。如[0-9]、[a-z] 。[] 可结合^使用标识否定
+ 表示零次或多次
{number} 表示number次
5、tips
1、URI是有默认值的。URI默认值为content或file。
2、如果需要为intent指定完整的data需要调用intent.setDataAndType(uri,type);这个方法,
3、setData 设置data的uri,setType 设置data的miniType但是 setData setType这两个方法彼此会清除对方值。(参考两个方法源码)
这样匹配规则就总结完了,另外注意intentFilter的匹配规则对Service,broadcastReceiver同样适用,不过系统对于Service的建议尽量使用显示意图来启动服务。
当组件设置了intent filter后便可支持隐式开启,这时exported属性默认会为true,组件支持外部应用开启。无intent filtre时组件的exported默认为false。
五、activity相关的常用方法
1、onNewIntent 触发条件有哪些?
1、activity 设置为SingleTop 且位于栈顶:再次开启activity会走 onNewIntent
2、activity设置为SingleTask、singleinstance任务栈中存在实例:再次开启activity会走onNewIntent
2、resolveActivity
避免找不到隐式activity而crash。注意参数PackageManager.MATH_DEFAULT_ONLY的使用。
if (getPackageManager().resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY) != null) {
startActivity(intent);
} else {
Toast.makeText(this, "Activity Not Found", Toast.LENGTH_SHORT).show();
}
4、异常状态下的数据恢复
onSaveInstanceState()
onRestoreInstanceState()
#思想:委托思想
首先 Activity 被意外终止时,Activity 会调用 onSaveInstanceState 去保存数据,然后 Activity 会委托 Window 去保存数据,接着 Window 在委托它上面的顶级容器再去一一通知它的子元素来保存数据,这样整个数据保存过程就完成了。
5、startActivityForResult使用
#mainActivity
...
...
Intent intent = new Intent(this, SecondActivity.class);
startActivityForResult(intent, 0x01);
...
...
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == 0x01 && resultCode == 0x02
&& data != null) {
// todo
}
}
#secondActivity
Intent intent = new Intent();
intent.putExtra("name", "i am second");
setResult(0x02, intent);
finish();