4.3 意图机制的应用
在Android中,对界面组件的调用,对服务组件的调用与绑定,对触发器组件的事件广播,都是通过意图机制来完成的。对于不同组件的调用,在匹配算法、组件排序等方面都是相同的,但在组件选取、组件构造等实现细节上有所区别。
4.3.1 意图机制在界面组件中的应用
对界面组件的调用是Android中最常用的场景。调用组件可以通过调用Context.startActivity和Activity.startActivityForResult等函数发起对目标界面组件的调用,选择并构造出一个符合其需求的界面组件实例,切换至前台与用户进行交互。
调用组件可以在构造的Intent对象中添加一些Flags标志位信息,用于告知组件管理服务如何去构造实现组件。在Intent类中,定义了一系列FLAG_ACTIVITY开头的Flags标志位,比如Intent.FLAG_ACTIVITY_NEW_TASK、Intent.FLAG_ACTIVITY_NO_FLAG等。这些标志位都是专属于界面调用组件的,组件管理服务会尝试依照这些Flags信息调整任务的界面组件栈和构造新进程,搭建符合预期的目标实现组件运行的环境。
如果组件管理服务发现系统中有多个界面组件都能满足调用组件的需求,就需要从中选择一个最适合的组件作为最终的实现组件。对于界面组件的选择,需要兼顾效率和公平性。所谓效率,是指选择过程应当尽可能地流畅,减少停顿和用户参与;而公平性,指的是每个符合调用者需求的组件,都应当具有同等的权利来响应调用组件的请求。
为了实现公平性和效率的平衡,Android在界面组件调用中部署了相关策略。首先是通过算法对已排序的Intent Filter进行筛选,组件管理服务会将排序后队列中的第一个Intent Filter对象(记为A)与第二个Intent Filter对象(记为B)进行比较,如果满足以下条件中的一个,组件管理服务就会直接选择A对象对应的组件作为实现组件:
1)A的优先级高于B。
2)A包含Intent.CATEGORY_DEFAULT,而B没有。
通过使用优先级和匹配度的筛选,可以最高效地从符合需求的组件中选择出最合适的实现组件。但在很多情况下,实现同样功能的组件,其Intent Filter信息往往很相近,无法通过这样的算法手段进行抉择。于是,为了保证公平性,系统将选择界面组件这个艰巨的任务交给了用户。
如图4-6a所示,Android会将所有符合需求的组件列举出来,让用户决定选择哪个组件来执行该操作。同时,用户可以选择将该组件作为执行类似Intent调用的默认组件。当任何一个调用组件再次通过同样的Intent进行界面组件调用时,Android会跳过用户选择步骤,直接采用该默认组件来执行。直到系统中又安装了其他符合该Intent对象请求的组件,默认组件设置会被清空,系统会再次让用户进行选择,以保证所有界面组件都有公平处理请求的机会。
如果调用组件期望绕过默认值的约束,让用户依据当前场景再次做出选择,可以通过Intent.createChooser方法自定义组件选择列表(效果如图4-6b所示)。
//选择合适的应用来发送推荐给好友的信息
Intent intent=new Intent();
intent.setAction(Intent.ACTION_SEND);
intent.setType("text/plain");
intent.putExtra(Intent.EXTRA_TEXT,"Hello");
try{
//构造自定义的组件选择器,没有默认组件,每次都会显示
activity.startActivity(Intent.createChooser(intent,
R.string.invite_friend);
}catch(android.content.ActivityNotFoundException ex){
//如果没有合适的应用能够响应该请求,则需要处理这个异常
}
除此之外,如果开发者期望对备选组件有更强的掌控力,自定义组件选择界面样式,可以通过PackageManager.queryIntentActivities函数,获得与Intent对象相匹配的所有备选组件列表,从而自行构造组件选择逻辑(如图4-6c所示):
//选择合适的应用来发送推荐给好友的信息
Intent intent=new Intent();
intent.setAction(Intent.ACTION_SEND);
intent.setType("text/plain");
intent.putExtra(Intent.EXTRA_TEXT,"Hello");
//查询符合该Intent对象的所有界面组件信息
List<ResolveInfo>activities=
getPackageManager().queryIntentActivities(intent,
PackageManager.MATCH_DEFAULT_ONLY);
//用这些组件信息构建出选择组件的交互界面
ShowSelectActivityDialog(activities);
图 4-6 界面组件选择的不同样式