0%

隐式intent

隐式intent

Intent 分为两种类型:

  • 显式 Intent:通过提供目标应用的软件包名称或完全限定的组件类名来指定可处理 Intent 的应用。通常,您会在自己的应用中使用显式 Intent 来启动组件,这是因为您知道要启动的 Activity 或服务的类名。例如,您可能会启动您应用内的新 Activity 以响应用户操作,或者启动服务以在后台下载文件。
  • 隐式 Intent :不会指定特定的组件,而是声明要执行的常规操作,从而允许其他应用中的组件来处理。例如,如需在地图上向用户显示位置,则可以使用隐式 Intent,请求另一具有此功能的应用在地图上显示指定的位置。

启动Activity时使用Intent,当使用显示Intent时,Intent对象是显式命名的某个具体的Activity组件时,系统立即启动该组件。

当使用隐式Intent时,Android 系统通过将 Intent 的内容与在设备上其他应用AndroidManifest中申明的Intent filter进行比较,从而找到要启动的相应组件。如果 Intent 与 Intent 过滤器匹配,则系统将启动该组件,并向其传递Intent对象。如果多个 Intent 过滤器兼容,则系统会显示一个对话框,支持用户选取要使用的应用。

具体过程如图所示:

img

  1. Activity A 创建包含操作描述的 Intent,并将其传递给 startActivity()
  2. Android 系统搜索所有应用中与 Intent 匹配的 Intent 过滤器。寻找到匹配项
  3. 该系统通过调用匹配 Activity (Activity B) 的 onCreate() 方法并将其传递给 Intent,以此启动匹配 Activity。

简单理解显示和隐式的区别就类似于租房子,显示Intent就是自己知道自己需要什么样的房子,并且已经确认自己想租的房子在那个小区那个单元那个房间(具体类名),而隐式Intent是只知道自己想租房,然后条件是什么,比如:房间大小、是否是独卫、是否能养宠物,然后把这些条件交给中介(Android系统),最终由中介挑选出一些合适的房间,由自己挑选是否租房。

显示Intent

显示Intent调用比较简单,只需要指定完整的组件类名即可。

比如:

1
2
3
// 显式Intent调用——构造方法传入Component
Intent intent = new Intent(this, TestActivity.class);
startActivity(intent);

当然还有一些其他方法,大致都是差不多都需要指定类名。

目前我有一个疑问就是显示intent是否能够跨应用启动Activity

后面根据自己的实验得来显示Intent是可以跨应用启动Activity,就是说A应用可以启动B应用的Activity

比如我创建一个应用,然后在里面加入一个按钮,在onCreate()中写下下面的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button mButton = findViewById(R.id.button);
mButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 启动其他应用的Activity,目标Activity不做任何配置,会报SecurityException错误
Intent intent = new Intent();
//String, String
intent.setClassName("com.example.testactivity", "com.example.testactivity.MainActivity");
startActivity(intent);

}
});
}

然后我们创建对应的以TestActivity为应用名的应用,他的包名为com.example.testactivity,其中MainActivity的类名是com.example.testactivity.MainActivity

然后我们在TestActivity应用的AndroidManifest.xml中,对应的目标Activity中加入android:exported="true"属性如下:

1
2
3
4
5
6
7
8
9
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>

然后运行第一个应用按下按钮就会发现跳转到了第二个应用之中。

举上面的例子说明其他应用的组件我们是可以通过显示的Intent进行调用,但是好像官方是不推荐使用显示Intent启动其他的Activity,因为我们写的包名和具体类名都是硬编码,一旦目标Activity修改了类名、修改了包名或者移动了位置,那么我们之前写的启动代码都会失败,这明显不符合我们的代码规范。

所以说,启动其他应用的组件时,应该使用隐式Intent,具体来说就是使用Intent-Filter进行匹配。

隐式Intent

隐式Intent不会指定特定的组件,而是声明要执行的常规操作,系统会根据Intent的内容去匹配对应的Activity并启动。

隐式Intent指定一些操作后会把这个Intent传给Android系统,然后由Android系统进行匹配,挑选出出合适的Activity出来然后进行启动。而Intent挑选的规则是是通过Intent filter

举个简单的例子:

我们将TestActivity应用中的AndroidManifest.xml的内容进行更改

1
2
3
4
5
6
7
8
9
10
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<action android:name="Test" />
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>

其中action元素告诉操作系统,activity能够胜的任指定任务,我们设置了一个自定义的actionTest

然后category设置谁可以访问,DEFAULT类别告诉操作系统(问谁可以做时),activity愿意处理某项任务。DEFAULT 类别实际隐含于所有隐式intent中

然后我们设置第一个应用当中的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button mButton = findViewById(R.id.button);
mButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//隐式启动
Intent intent = new Intent();
intent.setAction("Test");
startActivity(intent);

}
});
}

同样的我们启动该应用点击按钮就能启动TestActivity应用

匹配规则

具体来说隐式Intent有其固定的匹配规则,匹配时会对Activity的过滤列表进行对比,对比过滤列表当中的action、category、data信息,

action

action是一个字符串,该字符串区分大小写。系统预定义了一些action,同时我们也可以在应用中定义自己的action。

一个中可以有多个action,此时Intent中的action能够和中的任何一个action相同即可匹配成功。

category

category也是一个字符串,也区分大小写。系统预定义了一些category,同时我们也可以在应用中定义自己的category。

一般来说Intent的category有默认值,是由于系统在调用startActivity或者startActivityForResult的时候会默认为Intent加上“android.intent.category.DEFAULT”这个category。

因此,我们的配置中必须添加对应的配置,不然会匹配失败。

1
2
3
4
<intent-filter>
...
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>

Intent中我们可以不设置category,因为系统默认给我们添加了“android.intent.category.DEFAULT”。如果我们要添加category的话,这个category就必须跟的任意一个匹配,否则会匹配失败。

data

data设置接收数据类型,主要由两部分组成mimeType和URI。

mimeType指媒体类型,比如image/jpegaudio/mpeg4-genericvideo/*等,可以表示图片、文本、视频等不同的媒体格式。

URI包含的数据比较多,结构如下所示:

1
<scheme>://<host>:<port>[<path>|<pathPrefix>|<pathPattern>]

比如:

1
2
content://com.example.project:200/folder/subfolder/etc
http://www.baidu.com:80/search/info

data的匹配规则

data是非必须的,可以不设置。但是如果在定义了data,那么Intent中也必须设置可匹配的data。URI有默认值file和content,如果设置了URI,则默认值就失效,mimeType没用默认值,并且可以不设置。data的匹配意味着mimeType和URI同时匹配。

总结

Intent有显示和隐式两种,显示的内容不多,显示主要用于应用内,而隐式Intent比较灵活,内容也比较多

对于隐式Intent而言,必不可少的是action,因为默认的category会添加。

如果定义了data,不管mimeType是否设置,Intent中都必须设置uri,因为uri有默认值。

参考: