Intent和IntentFilter
public class Intent implements Parcelable, Cloneable 复制代码
1.Intent简介
Intent是一个经过序列化的消息传递对象,实现于Parcelable。应用于组件之间的交互与通信。 Intent负责对应用中的一次操作的action,catogary,data描述,系统会根据Intent的描述找到对应的组件完成组件的调用。基本用例包括以下三类:
启动 Activity
通过将 Intent 传递给 startActivity(),可以启动新的 Activity 实例。Intent 用于描述要启动的 Activity,并携带任何必要的数据。
如果希望在 Activity 完成后收到结果,请调用 startActivityForResult()。在Activity 的 onActivityResult() 回调中, Activity 将结果作为单独的 Intent 对象接收
启动服务
Service 是一个不使用用户界面而在后台执行操作的组件。使用 Android 5.0(API 级别 21)及更高版本,可以启动包含 JobScheduler 的服务。
对于 Android 5.0(API 级别 21)之前的版本,可以使用 Service 类的方法来启动服务。通过将 Intent 传递给 startService()或者bindService()
传递广播
广播是任何应用均可接收的消息。系统将针对系统事件(例如:系统启动或设备开始充电时)传递各种广播。通过将 Intent 传递给 sendBroadcast() 或 sendOrderedBroadcast(),可以将广播传递给其他应用。
Intent种类
Intent分为两种类型:
显示Intent
在我们startActivity()或者startService时构造的Intent对象时指定固定的类也就是我们能够提供类来构造ComponentName对象
下面是基于Android API 31源码中的两种构造方法 public Intent(Context packageContext, Class<?> cls) { mComponent = new ComponentName(packageContext, cls); } public @NonNull Intent setClass(@NonNull Context packageContext, @NonNull Class<?> cls) { mComponent = new ComponentName(packageContext, cls); return this; } *//使用 1.val intent = Intent(context,class) 或者 2.val intent = Intent() intent.setClass(context,class) startActivity(intent)*复制代码
隐式Intent
也就是不会指定特定的组件,需要配置IntentFilter来在AndroidManifest.xml中去匹配, 我们常用的是指定Action和包名来进行匹配。如果多个 IntentFilter兼容,则系统会显示一个对话框,支持用户选取要使用的应用。以下是谷哥官方图
隐式 Intent 如何通过系统传递以启动其他 Activity: [1] Activity A 创建包含操作描述的 Intent
,并将其传递给 startActivity()
。 [2] Android 系统搜索所有应用中与 Intent 匹配的 Intent 过滤器。找到匹配项之后, [3] 该系统通过调用匹配 Activity (Activity B) 的 onCreate()
方法并将其传递给 Intent
,以此启动匹配 Activity。
//使用方式 val intent = Intent() intent.setPackage("com.stxx.newapp") intent.action = "com.stxx.newapp.process" //或者 //在构造函数中指定Action val intent = Intent("com.stxx.newapp.process") intent.setPackage("com.stxx.newapp") bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE)复制代码
使用隐式Intent的注意事项:
使用隐式意图时要注意检查是否能够匹配上意图,否则会导致程序崩溃。我们可以使用Intent中的ResolveActivity()方法进行检查,如果结果不为null再进行startActivity()或者startService()操作
// 创建一个隐式Intent val sendIntent = Intent().apply { action = Intent.ACTION_SEND putExtra(Intent.EXTRA_TEXT, textMessage) type = "text/plain" } // 验证是否能够匹配,下面两种方法注意编译时API版本>30需要在manifest.xml中指定queries标签指定intent package,第三种方法则不需要 方法1:if (sendIntent.resolveActivity(packageManager) != null) { startActivity(sendIntent) } 方法2:if(packageManager.queryIntentActivities(sendIntent, MATCH_DEFAULT_ONLY)){ startActivity(sendIntent) } 方法3:val info = packageManager.resolveActivity(sendIntent, MATCH_DEFAULT_ONLY) if (info != null) { startActivity(sendIntent) } else { toast("未匹配到组件") }复制代码
如果有多个应用响应隐式 Intent,则用户可以选择要使用的应用,并将其设置为该操作的默认选项。如果用户可能希望每次使用相同的应用执行某项操作(例如,打开网页时,用户往往倾向于仅使用一种网络浏览器),则选择默认选项的功能十分有用。
但是,如果多个应用可以响应 Intent,且用户可能希望每次使用不同的应用,则应采用显式方式显示选择器对话框。选择器对话框会要求用户选择用于操作的应用(用户无法为该操作选择默认应用)。例如,当应用使用 ACTION_SEND
操作执行“共享”时,用户根据目前的状况可能需要使用另一不同的应用,因此应当始终使用选择器对话框,如图 2 中所示。
要显示选择器,请使用 createChooser()
创建 Intent
,并将其传递给 startActivity()
,如下例所示。此示例将显示一个对话框,其中有响应传递给 createChooser()
方法的 Intent 的应用列表,并且将提供的文本用作对话框标题。
val sendIntent = Intent(Intent.ACTION_SEND) ... val title: String = resources.getString(R.string.chooser_title) val chooser: Intent = Intent.createChooser(sendIntent, title) if (sendIntent.resolveActivity(packageManager) != null) { startActivity(chooser) }复制代码
使用startActivity()启动隐式意图时,intent会默认添加一个android.intent.category.DEFAULT的category所以要注意在intent-filter中配置以下代码
<category android:name="android.intent.category.DEFAULT"/>复制代码
IntentFilter匹配规则
Intent对象大致包括7大属性:Action(动作) 、Data(数据) 、Category(类别) 、Type(数据类型) 、Component(组件) 、Extra(扩展信息) 、Flag(标志位) 。其中最常用的是Action属性和Data属性。
Action:
定义匹配动作,属性值为一个字符串,Intent中必须携带有action,系统预定义了一些action,但是我们也可以在应用中定义自己的action。action元素可以声明多个。示例如下:
<intent-filter> <action android:name="android.intent.action.EDIT" /> <action android:name="android.intent.action.VIEW" /> ... </intent-filter>复制代码
Intent中必须指定action否则无法匹配任何组件。Intent中指定的action必须能在intent-filter中的某一项匹配
category
Intent 过滤器既可以不声明任何 <category>
元素,也可以声明多个此类元素,
如果设置了intent-filter则必须设置《category android:name="android.intent.category.DEFAULT"》原因前面已经说过。
还有在intent中设置的category必须和intent-filter中的某一项匹配。
Intent中如果不含任何category,则只匹配action即可。
Data
每个 <data>
元素均可指定 URI 结构和数据类型(MIME 媒体类型)。URI 的每个部分都是一个单独的属性:scheme
、host
、port
和 path
:
<scheme>://<host>:<port>/<path>
过滤规则:
如果intent-filter中定义了data,那么Intent中必须也要携带可匹配的data反之如果未定义,intent中也不能携带
如果intent-filter未指定mimeType ,那么Intent中也不许指定否则无法匹配
当intent-filter列出相同的 MimeType且未指定 URI 格式时,包含MimeType但不含 URI 的 Intent 才会通过。
注意在Intent中同时设置data和type时 要使用setDataAndType()不可以分开调用setData()和setType().因为在单独调用每一个方法时都会把另一个参数 设置为null
public @NonNull Intent setData(@Nullable Uri data) { mData = data; mType = null; return this; }复制代码
下面是一个在浏览器中打开activity的示例:声明了两个过滤器。打开activity时只要命中一个即可
<activity android:name=".view.start.LaunchActivity" android:launchMode="singleTop" android:screenOrientation="portrait" android:theme="@style/AppStartLoadTranslucent"> <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> <intent-filter> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.DEFAULT" /> //代表可以从浏览器中打开 <category android:name="android.intent.category.BROWSABLE" /> //声明了scheme为xjtmeeting;在js中只要调用[xjtmmeting://]的url就可以打开该activity <data android:scheme="xjtmeeting" /> </intent-filter>
作者:fighter_
链接:https://juejin.cn/post/7031459769932054565