走近 IntentFilter

举报
SHQ5785 发表于 2024/06/20 09:21:11 2024/06/20
【摘要】 一、什么是IntentFilter ?如果一个 Intent 请求执行一个动作, Android 如何知道由哪个应用程序(或组件)响应这个请求呢?IntentFilter就是用来注册 Activity 、 Service 和 Broadcast Receiver 具有能在某种数据上执行一个动作的能力。使用 IntentFilter ,应用程序组件告诉 Android ,它们能为其它程序组件...

一、什么是IntentFilter ?

如果一个 Intent 请求执行一个动作, Android 如何知道由哪个应用程序(或组件)响应这个请求呢?

IntentFilter就是用来注册 ActivityServiceBroadcast Receiver 具有能在某种数据上执行一个动作的能力。

使用 IntentFilter ,应用程序组件告诉 Android ,它们能为其它程序组件的动作请求提供服务,包括同一个程序组件、本地或第三方应用程序。

IntentFilter翻译成中文就是“意图过滤器”,主要用来过滤隐式意图。当用户进行一项操作的时候,Android系统会根据配置的 “意图过滤器” 来寻找可以响应该操作的组件,服务。

例如:当用户点击PDF文件的时候,Android系统就会通过设定好的意图过滤器,进行匹配测试。找到能够打开PDF文件的APP程序。

代码:

<activity android:name="com.example.testmain.ShowActivity" >
   <intent-filter>
	   <action android:name="test.update.mydata" />
	   <category android:name="my.test.show" />
	   <data android:pathPattern=".*\\.jpg" android:scheme="http" />
   </intent-filter>
</activity>

二、IntentFilter 如何过滤隐式意图?

Android系统会根据配置的IntentFilter意图过滤器),来进行匹配测试。匹配的时候,只会考虑三个方面:动作数据(URI以及数据类型)和类别。也就是说Android系统会进行“动作测试”,“数据测试”,“类别测试”,来寻找可以响应隐式意图的组件或服务。

另外,当对其他App程序开放组件和服务的时候也需要配置Intent Filter(意图过滤器),一个Activity可以配置多个<intent-filter>

2.1 动作测试

对应<intent-filter>中的<action/>标签;

  1. 如果<intent-filter>标签中有多个<action/>,那么Intent请求的Action,只要匹配其中的一条<action/>就可以通过了这条<intent-filter>的动作测试。

  2. 如果<intent-filter>中没有包含任何<action/>,那么无论什么Intent请求都无法和这条<intent-filter>匹配。

  3. 如果Intent请求中没有设定Action(动作),那么这个Intent请求就将顺利地通过<intent-filter>的动作测试(前提是<intent-filter>中必须包含有<action/>,否则与第二条冲突)。

2.2 类别测试

对应<intent-filter>中的<category />标签;

Intent中的类别必须全部匹配<intent-filter>中的<category />,但是<intent-filter>中多余的<category />将不会导致匹配失败。

例如:Intent中有3个类别,而意图过滤器中定义了5个,如果Intent中的3个类别都与过滤器中的匹配,那么过滤器中的另外2个,将不会导致类别测试失败。

注意:有一个例外,Android把所有传给startActivity()的隐式意图当作他们包含至少一个类别:"android.intent.category.DEFAULT" (CATEGORY_DEFAULT常量)。 因此,想要接收隐式意图的活动必须在它们的意图过滤器中包含"android.intent.category.DEFAULT"。(带"android.intent.action.MAIN“和”android.intent.category.LAUNCHER"设置的过滤器是例外)

2.3 数据测试

对应<intent-filter>中的<data>标签;

<data>元素指定了可以接受的Intent传过来的数据URI和数据类型,当一个意图对象中的URI被用来和一个过滤器中的URI比较时,比较的是URI的各个组成部分。

例如:

如果过滤器仅指定了一个scheme,所有该schemeURIs都能够和这个过滤器相匹配;

如果过滤器指定了一个scheme、主机名但没有路经部分,所有具有相同scheme和主机名的URIs都可以和这个过滤器相匹配,而不管它们的路经;

如果过滤器指定了一个scheme、主机名和路经,只有具有相同scheme、主机名和路经的URIs才可以和这个过滤器相匹配。当然,一个过滤器中的路径规格可以包含通配符,这样只需要部分匹配即可。
比较规则如下:

  1. 一个既不包含URI也不包含数据类型的意图对象,仅在过滤器也同样没有指定任何URI和数据类型的情况下才能通过测试。

  2. 一个包含URI但没有数据类型的意图对象,仅在它的URI和一个同样没有指定数据类型的,过滤器里的URI匹配时才能通过测试。这通常发生在类似于mailto:和tel:这样的URIs上:它们并不引用实际数据。

  3. 一个包含数据类型但不包含URI的意图对象,仅在这个过滤器列举了同样的数据类型,而且也没有指定一个URI的情况下才能通过测试。

  4. 一个同时包含URI和数据类型(或者可从URI推断出数据类型)的意图对象可以通过测试,如果它的类型和过滤器中列举的类型相匹配的话。如果它的URI和这个过滤器中的一个URI相匹配或者它有一个内容content:或者文件file: URI,而且这个过滤器没有指定一个URI,那么它也能通过测试。换句话说,一个组件被假定为支持”content: 数据“ 和 “file: 数据”,如果它的过滤器仅列举了一个数据类型。

例如AndroidManifest.xmlAndroidManifest.xml是安卓开发中主配置文件,程序执行首先浏览这个文件的内容)中有:

对于<intent-filter>中的action项可以有多个只要匹配其中一个就可以了

intent.setAction("com.nanlove.wangshiming");//中的action也可以为wangshiming

intent.addCategory("wangshiming.intent.category")// 代码中的addCategory并不用写因为android他有默认的category 只要配置清单中存在<category android:name="android.intent.category.DEFAULT" />就可以了.

没有 “数据参数” 的情况下只要意图对象中的设置动作和类别都出现在intent-filter就能跟filter匹配,但是有数据<data android:scheme="love" android:host="hao123.com" android:port="888" android:path="/MM" />数据项一定要完全匹配。

当数据和数据类型 android:mimeType="text/plain"同时存在的时候,不能使用intent.setData(Uri.parse("love://hao123.com:888/MM")) ;

因为setData的方法会自动清除前面的数据类型:This method automatically clears any type that was previously set by setType;

所以后面的setType就无法匹配,应该使用intent.setDataAndType(Uri.parse("love://hao123.com:888/MM"), "text/plain");

提示:在同一个应用内,能使用显示意图,就尽量使用显示意图,增加程序的效率,理论上隐式意图匹配规则是需要花时间寻找的。

三、IntentFilter 实践

3.1 从一个APP跳转到另外一个目标APP

假设由应用A跳转到应用B。前提是应用B存在,可以使用getPackageManager().getPackageInfo("应用B的包名", 0);方法判断应用B是否存在。

当前APP启动另外一个目标APP(非覆盖原来APP的方式),可以按照如下方案实施:

3.1.1 系统包管理器方式

  1. 当前APP加入获取权限声明:(不加入权限检查,没法启动目标app)
 <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"
        tools:ignore="QueryAllPackagesPermission" />
 <uses-permission android:name="android.permission.LAUNCH_APP" />
  1. 执行APP跳转
String packageName = "com.target.package"; // 目标应用的包名
PackageManager packageManager = getPackageManager();
Intent intent=new Intent();
intent = packageManager.getLaunchIntentForPackage(packageName);
 if(intent==null){
     Toast.makeText(MainActivity.this, "未安装", Toast.LENGTH_LONG).show();
 }else{
     startActivity(intent);
 }
  1. 目标APP处理
@Override
protected void onCreate(Bundle savedInstanceState) {
  	requestWindowFeature(Window.FEATURE_NO_TITLE);
  	initWindows(this);
  	super.onCreate(savedInstanceState);
	Intent intent = getIntent();
	if (intent != null && intent.hasExtra("message")) {
	  String message = intent.getStringExtra("message");
	  IntentModule.message = message;
	  IntentModule.getMessage();
	}   
}

3.1.2 Intent隐式调起方式

当前APP做如下处理:

Intent intent2 = new Intent();
intent2.setAction("定义的action,要与后面B中使用的保持一致");
intent2.putExtra("data","传递的数据");
startActivity(intent2);

目标APP AndroidManifest.xml配置文件做如下处理:

<activity android:name="要跳转的指定类">
    <intent-filter>
        <action android:name="定义的action,要与前面A中的保持一致"/>
        <category android:name="android.intent.category.DEFAULT"/>
    </intent-filter>
</activity>

目标APP参数处理逻辑如下:

Intent intent = this.getIntent();
if(null != intent.getStringExtra("data")){
    //处理数据
}

3.1.3 通过 ComponentName 类实现

当前APP做如下处理:

Intent intent1 = new Intent();
ComponentName name = new ComponentName("应用B的包名","应用B中跳转到的activity的全路径");
intent1.setComponent(name);
intent1.putExtra("data","传递的数据");
startActivity(intent1);

目标APP AndroidManifest.xml配置文件对应的Activity下添加 android:exported="true" 表示允许其它应用调用当前组件;

目标APP参数处理逻辑如下:

Intent intent=this.getIntent();
if(null != intent.getStringExtra("data")){
    //处理数据
}

3.2 从一个APP跳转到另外一个目标APP,并传参

  1. 执行APP AndroidManifest.xml 设置。
android:exported="true"
  1. 执行APP跳转
Intent intent = new Intent(Intent.ACTION_MAIN);
/**知道要跳转应用的包命与目标Activity*/
ComponentName componentName = new ComponentName("com.demo.demo", "com.demo.ui.main.DemoActivity");
intent.setComponent(componentName);
intent.putExtra("salesNo", "参数1");
intent.putExtra("code", "参数2");
startActivity(intent);
  1. 目标APP AndroidManifest.xml 设置。
android:exported="true"
android:launchMode="singleInstance"
  1. 目标APP在 onCreate 方法中接收传值:
Intent intent = getIntent();
Bundle bundle = intent.getExtras();
String salesNo = "";
String code = "";
if (bundle != null) {
    salesNo = bundle.getString("salesNo");
    code = bundle.getString("code");
}

3.2.1 为什么 intent.getStringExtra() 总是返回 null ?

实操过程中,有的同学可能会遇到应用intent.getStringExtra()返回null的情形。出现返回null的原因可能有以下几种:

  1. 没有正确设置Intent传递的数据

可能使用putExtra方法将数据存放到Intent中,而接收方在处理数据时没有正确取出来。也有可能是Intent中并没有存放所需要的数据。

  1. 在使用intent.getStringExtra()之前没有使用intent.hasExtra()检查是否存在所需的值。

在使用getStringExtra()方法之前,应该先通过hasExtra()方法检查是否存在所需的值。没有检查是否存在这个值时,就可能会出现返回null的情况。

  1. 使用键值不正确

在从Intent中取出数据时,需要用到getStringExtra()方法的参数键值(key),而有可能使用了错误的键值。需要确保键值正确,才能从Intent中正确取出所需的数据。

  1. 数据类型不匹配

在存放数据时,使用了一个错误的数据类型。当使用getStringExtra()方法去取出时,就会出现返回null的情况。需要检查所存放的数据的类型是否与要取出的类型一致。

3.2.2 Intent 对象被重用

多次使用同一个PendingIntent发送广播时,可能会出现Intent对象被重用的情况,导致获取到的数据不正确。

要解决这个问题,可以在创建PendingIntent时,使用PendingIntent.FLAG_UPDATE_CURRENT标志来更新当前的Intent,确保每次发送广播时都是使用新的Intent对象。示例如下:

// 创建Intent对象
Intent intent = new Intent(context, MyReceiver.class);
intent.putExtra("data", "hello world");
 
// 创建PendingIntent对象,并同时指定FLAG_UPDATE_CURRENT和FLAG_IMMUTABLE标志
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
 
// 发送广播
pendingIntent.send();

重点是:PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE

3.3 从当前APP跳转到另外一个目标APP的某个页面

由当前APP启动另外一个目标APP(非覆盖原来APP的方式)某个指定页面时,可以按照如下方案实施:

  1. 目标APP对应Activity中加入如下action:
 <activity
    android:name="com.xxx.package.TargetXXActivity"
    android:exported="true">
     <intent-filter>
       <action android:name="jp" />  <!--标志-->
       <category android:name="android.intent.category.DEFAULT" />  <!--这是一个普通页面-->
      </intent-filter>
 </activity>
  1. 当前应用APP执行跳转到目标APP页面。
 Intent intent = new Intent("jp");  // 标志(要和目标APP的清单文件中的标志一样)
 startActivity(intent);

注⚠️:Intent 跳转时须捕获一下异常,防止目标对象不存在引起崩溃。

四、拓展阅读

【版权声明】本文为华为云社区用户原创内容,未经允许不得转载,如需转载请自行联系原作者进行授权。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。