继续我们的Android之路吧。今天我要介绍的是Android的Intent。

对于基于组件的应用开发而言,不仅需要构造和寻找符合需求的组件,更重要的是要将组件有机的连接起来,互联互通交换信息,才能够最终提供应用所期望的服务。而为了能够更好地实现组件复用,充分地利用每一个组件的能力,就需要这些组件连接的模式足够灵活和统一,并且可以进行动态地扩展。所以Android提供了利用Intent对象建立连接并实现组件通信的模式。Intent对象是Android组件连接的核心,集中体现了整个组件连接模型的设计思想。

一、Intent对象

Android的Intent机制最核心的设计思想,就是引入了组件管理服务作为连接组件的管理者。该服务可以通过组件的配置信息了解系统中每个组件的类别和功能,从而帮助调用组件寻找符合其需求的实现组件,将调用者与实现者彻底解耦。

一般的Intent实现的流程图:

  1. Intent对象的构成
    Intent对象的这些作用,都是通过它的实现和设计体现出来的。从数据结构来看,Intent类的实现非常简单,它并没有包含复杂的逻辑功能,只是包含着若干个数据项。
    (1)Action项 在Intent中用来表示动作,可以通过Intent的setAction和getAction来进行操作。在Android中定义了很多标准的动作,比如:Intent.ACTION_VIEW等。这些动作约定了    Android组件间的通信规范,保证了组件系统的可扩展性。
    (2)Data项,当发起请求时,调用组件如果有明确的数据对象,通常就会用Data项来存储表示。Data数据也是用字符串进行存储,它的格式符合URI标准。所以可以通过URI来描述数据来增强数据的作用范围。
    (3)Type项, Type是MINE格式的字符串数据,用于描述组件能够处理的请求类型,或者补充说明Data数据的类型,它可以通过通配符*来表示整个类别的信息。注意:Data项和Type项在很多时候是互斥使用的,当调用Intent.setType时,之前设定的Data信息就会被清空,反之亦然。但可以通过intent.setDataAndType()来同时设置。
    (4)Category项 Category表示约束。每个Intent对象可包含多个categroy项。而一个组件需要支持全部的Category才可能处理请求。如一个组件需要启动其他应用,且希望进入的是这个应用的入口组件,这时就需要添加Intent.CATEGORY_LAUNCHER作为约束。
    (5)Component项  它指的是目标组件的类型信息,可以通过Intent.setComponent方法利用类名进行设置,也可以通过Intent.setClass方法利用类型对象信息进行设置。
    (6)Extras项 Intent中数据传输的载体,负责将数据从调用组件传递到实现组件。
    (7)Flags项 Flags是一个整数型,由一系列的标志位汇集而成。
    在组件开发中,应该遵循SDK或第三方拟定的相关规范。当一个组件需要支持特定的Action时,就需要同支持相关的Extras项和Data项相关内容。协议的完整性和正确性是整个Android Intent机制的根基。
  2. Intent对象的解析
    从实现组件的选择来看,Intent对象可以分为两类:精确描述的Intent和模糊描述的Intent。
    (1)精确描述的Intent,指的是所有带有Component信息的Intent对象,调用组件可以通过Intent.setComponent或Intent.setClass等方法进行设置。当组件管理服务收到Intent对象时,会先校验其Component数据项,如果含有Component信息,组件管理服务只需要依照Component构造对应的实现组件。平时我们用的Intent的跳转都是用的精确Intent.
    (2)除此之外,所有不包含Component信息的Intent对象,都归类于模糊描述的Intent.

二、Intent Filter对象
除了Intent对象,在Android的Intent机制中还有一个重要的角色:Intent Filter对象。Intent Filter对象,指的是android.content.IntentFilter类的对象,它是Intent对象的“姊妹对象”。与Intent对象类似,Intent Filter对象也包含Action、Type、Data、Catagory等数据项,每个数据项的结构和含义,与Intent的数据项也一一对应。

  1. Intent Filter的用途
    Intent Filter是各个组件用于描述其功能的,通过组件的Intent Filter信息,Android的组件管理应用就可以了解和掌握各个组件所具备的能力和能够处理的请求。当组件管理服务接收到调用组件发送来的基于模糊描述的Intent对象时,会与所有组件的Intent Filter信息进行匹配计算,寻找符合需求的实现组件。
    每个组件都可以有任意数量的Intent Filter。组件包含的Intent Filter对象越多,说明他能接受Intent请求范围越广,同时,其实现也会越复杂。
  2. Intent Filter的范例
             <activity  android:name="com.example.activitydemo.SecondActivity">
    <intent-filter android:icon=""
    android:label=""
    android:priority="">
    <action android:name=""></action>
    <category android:name=""></category>
    <data android:host=""
    android:mimeType=""
    android:path=""
    android:pathPattern=""
    android:port=""
    android:scheme=""/>
    <!-- 可以继续添加相关的actio、category和data项 -->
    </intent-filter><!-- 可以继续添加相关的intent-filter项 --> </activity>

三、Intent 匹配算法

当组件管理服务接受到请求组件的Intent对象后,会先查看Intent对象是否包含了目标实现组件的Component信息。如果不包括,则会从应用管理服务中获取所有组件的Intent Filter信息,并与Intent对象相比较,选择符合需求的实现组件。其中涉及到的就是Intent的匹配算法了。

算法输入的是进行比较的Intent对象和Intent Filter对象;如果返回值为正,则表示匹配成功,并且,正值越大意味着匹配程度越高,流程图如下。

其中,Data与Type信息是Intent Filter中最复杂的数据项,其比较算法是决定Intent与Intent Filter对象匹配程度的关键。

如果Intent的对象包含Type信息,就必须要求Intent Filter的Type信息与之对应,否则,匹配也将以失败告终;然后,如果Intent对象中含有Data项,则会将该Data项的URI信息拆分为Scheme和Authority等部分,逐一与Intent Filter对象中对应的部分进行比较,只要两者有任何不符的地方,匹配都会失败。

以Android原生的邮件应用为例,它配置了如下的Intent Filter信息:

          <activity  android:name=".SecondActivity.MessageCompose">
<intent-filter >
<action android:name="android.intent.action.VIEW" />
<action android:name="android.intent.action.SENDTO" />
<data android:scheme="mailto"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<!-- 可以继续添加相关的actio、category和data项 -->
</intent-filter>
<!-- 可以继续添加相关的intent-filter项 --> </activity>

当调用者发出如下请求时,就会触发一次意图匹配:

                Intent intent = new Intent(Intent.ACTION_SENDTO);
intent.putExtra(Intent.EXTRA_TEXT, "");
//其中,mailto这部分是URI的scheme部分
intent.setData(Uri.parse("mailto:xxx@oo.com"));
startActivity(intent);

开始匹配检查。ACTION包含OK—>data包含OK—>Category检查(通过Context.startActivity函数发出请求,组件管理服务会自动为其添加上Intent.CATEGORY_DEFAULT)OK—>匹配成功。

四、匹配组件的选择

如果有多个Intent Filter对象与调用组件发出的Intent对象都相匹配,就需要在所有符合条件的Intent Filter对象中进行筛选,选出最符合调用组件和用户需求的实现组件,这就是匹配组件的选择。而我们可以设定Intent Filter对象的优先级来进行设定,即通过<intent-filter>配置项中的android:priority属性进行变更,或者通过IntentFilter.setPriority函数进行动态地修改。范围是-1000至1000,默认为0。

在Android的Intent机制中,通过引入第三方组件管理服务,降低了调用组件与实现组件之间的耦合,提高了整个系统的灵活性及组件的复用性,使得应用开发变得更为简单快捷。但同时,正是由于第三方服务地介入增加了组件间连接的成本,可能会使组件间的调用不够流畅。因此,在组件管理服务中,系统对组件的匹配和选择过程进行了大量的优化,以提高组件调用的效率,其中包括:

(1)索引
       组件管理服务通过哈希表,为所有Intent Filter对象的Action、Type等数据项建立索引。每个索引项对应着一组Action相同,Type相同或者其他数据项相同的Intent Filter对象。Intent先与索引项比较,快速地选择出可能与Intent相匹配的Intent Filter对象。这样,就加快了匹配速度。

(2)缓存
       所谓缓存,是将Intent与Intent Filter的匹配结果记录下来,当再碰到相同Intent的调用时,可直接返回上次记录的结果,从而跳过匹配的过程,加速组件的调用。

五、Intent使用的一个小例子

  1. 一个ActivityDemo工程,用以启动其他应用的Activity。其中的代码为

    public class ChangeActivity extends Activity {
    
        @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    Button button = (Button) findViewById(R.id.button);
    Button button2 = (Button) findViewById(R.id.button2);
    button.setOnClickListener(new OnClickListener() { @Override
    public void onClick(View v) { Intent intent = new Intent();
    //因为是精确启动,这些不加都是没问题的
    //intent.addCategory(Intent.CATEGORY_LAUNCHER);
    // intent.setAction(Intent.ACTION_MAIN);
    ComponentName cn = new ComponentName(
    "com.example.demo",//另一个应用的包名
    "com.example.demo.MainActivity");//要启动的另一个应用的类名
    //intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    intent.setComponent(cn);
    Log.d("TEST", intent.toString());
    startActivity(intent); //精确启动的另一种方法
    /*Intent LaunchIntent = getPackageManager()
    .getLaunchIntentForPackage("com.example.demo");
    Log.d("TEST", LaunchIntent.toString());
    startActivity(LaunchIntent);*/
    }
    }); button2.setOnClickListener(new OnClickListener() { @Override
    public void onClick(View v) { //只能获得系统级的应用组件
    //action为MAIN,应用入口界面
    Intent intent = new Intent(Intent.ACTION_MAIN);
    intent.addCategory(Intent.CATEGORY_LAUNCHER);
    startActivity(intent); //获取所有符合的组件,包括第三方安装的应用
    //List<ResolveInfo> activities = getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
    //自定义对话框
    //ShowSelectActivityDialog(activities);
    }
    }); } }
  2. 另一个应用,即包名为com.example.demo的应用,其中配置文件部分为:
    <activity
    android:name="com.example.demo.MainActivity"
    android:label="@string/app_name" >
    <intent-filter>
    <action android:name="android.intent.action.MAIN" />
    <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
    </activity>
  3. 安装以上两个应用,运行效果图:

    点击精确Intent,成功跳转到另一个应用中。
    点击模糊Intent,出现组件选择列表,如下图:

PS:其中关于PackageManager的详细用法,可以参考这篇文章Android随笔—PackageManager详解

六、结束语

看到这里,大家是不是对Intent的传递原理有所了解了呢。形象一点地说,Intent好比是插头,而Intent Filter就好比是插座,插座的孔只要满足插头的孔脚数量就能工作了,多了没事,但少了就不行。

Intent是Android应用模型的核心,解决了组件间的连接问题。通过组件管理服务提供的Intent匹配策略,降低了组件间的耦合度,提高了平台的灵活性,增强了组件的复用性,从根本上减轻了应用开发的负担。

参考文章:(1) android Intent机制详解 http://www.oschina.net/question/565065_67909

               (2)android intent和intent action大全  http://www.apkbus.com/android-72361-1-1.html

========================================

作者:cpacm

地址:http://www.cpacm.net/2015/03/26/Android开发日记(八)——Android的Intent机制/

【Android开发日记】之入门篇(十一)——Android的Intent机制的更多相关文章

  1. 【Android开发日记】第一个任务Android Service!Service靴+重力感应器+弹出窗口+保持执行

    前言: 近期在写一个小程序,需求是手机摇一摇就弹窗出来.第一次使用了Service,学习了两天,实现了Service弹窗,开机启动,Service启动和销毁,Service保持一直执行. 满足了自己的 ...

  2. 【Android开发日记】之入门篇(十二)——Android组件间的数据传输

    组件我们有了,那么我们缺少一个组件之间传递信息的渠道.利用Intent做载体,这是一个王道的做法.还有呢,可以利用文件系统来做数据共享.也可以使用Application设置全局数据,利用组件来进行控制 ...

  3. 【Android开发日记】之入门篇(十四)——Button控件+自定义Button控件

        好久不见,又是一个新的学期开始了,为什么我感觉好惆怅啊!这一周也发生了不少事情,节假日放了三天的假(好久没有这么悠闲过了),实习公司那边被组长半强制性的要求去解决一个后台登陆的问题,结果就是把 ...

  4. 【Android开发日记】之入门篇(七)——Android数据存储(上)

    在讲解Android的数据源组件——ContentProvider之前我觉得很有必要先弄清楚Android的数据结构. 数据和程序是应用构成的两个核心要素,数据存储永远是应用开发中最重要的主题之一,也 ...

  5. 【Android开发日记】之入门篇(九)——Android四大组件之ContentProvider

    数据源组件ContentProvider与其他组件不同,数据源组件并不包括特定的功能逻辑.它只是负责为应用提供数据访问的接口.Android内置的许多数据都是使用ContentProvider形式,供 ...

  6. 【Android开发日记】之入门篇(一)——开发环境的搭建

    写给自己的话:至此,大学的时光已经剩下一年的时光,下一年等毕业设计结束后就算是正式地踏入社会.自己学android也不过几个月的时间,为了更好管理文档,写点东西记录下自己曾经做过的点点滴滴是一个不错的 ...

  7. 【Android开发日记】之入门篇(八)——Android数据存储(下)

    废话不多说了,紧接着来讲数据库的操作吧.Come On! 提到数据存储问题,数据库是不得不提的.数据库是用来存储关系型数据的不二利器.Android为开发者提供了强大的数据库支持,可以用来轻松地构造基 ...

  8. 【Android开发日记】之入门篇(五)——Android四大组件之Service

    这几天忙着驾校考试,连电脑都碰不到了,今天总算告一段落了~~Service作为Android的服务组件,默默地在后台为整个程序服务,辅助应用与系统中的其他组件或系统服务进行沟通.它跟Activity的 ...

  9. 【Android开发日记】之入门篇(六)——Android四大组件之Broadcast Receiver

    广播接受者是作为系统的监听者存在着的,它可以监听系统或系统中其他应用发生的事件来做出响应.如设备开机时,应用要检查数据的变化状况,此时就可以通过广播来把消息通知给用户.又如网络状态改变时,电量变化时都 ...

随机推荐

  1. redis安装与简单使用

    第一步 新建一个文件 第二步 利用winscrp软件从本机上传redis的压缩包到linux新建的rdtar目录 第三步   cd rdtar 第四步   解压  tar zxvf redis-2+t ...

  2. 【JavaScript】jQuery绑定事件

    jquery中直接绑定事件:只能用在程序中一开始就存在的html代码 目标元素.click(function(){ }) jquery中间接绑定事件: 如果目标元素是js生成的,则需要间接绑定事件,用 ...

  3. Cache、Buffer的区别

    什么是Cache?什么是Buffer?二者的区别是什么? Buffer和Cache的区别 buffer与cache操作的对象就不一样. 1.buffer(缓冲)是为了提高内存和硬盘(或其他I/O设备) ...

  4. [BZOJ3172 ][Tjoi2013]单词(AC自动机)

    Description 不稳定的传送门 某人读论文,一篇论文是由许多单词组成.但他发现一个单词会在论文中出现很多次,现在想知道每个单词分别在论文中出现多少次.单词个数<=200,单词总长度< ...

  5. Android 内嵌 HTML5 并进行交互

    Android与HTML5的交互主要是两个部分, 与HTML5的交互以及与JavaScript的交互, 与HTML5的交互可以通过注册onclick事件转化为与JavaScript的交互 Androi ...

  6. 51Nod 1680 区间求和 树状数组

    题意: 给出一个长度为\(n\)的数列\(A_i\),定义\(f(k)\)为所有长度大于等于\(k\)的子区间中前\(k\)大数之和的和. 求\(\sum_{k=1}^{n}f(k) \; mod \ ...

  7. 阿里巴巴Java开发规约Eclipse插件安装及使用

    技术交流群:233513714 插件安装 环境:JDK1.8,Eclipse4+.有同学遇到过这样的情况,安装插件重启后,发现没有对应的菜单项,从日志上也看不到相关的异常信息,最后把JDK从1.6升级 ...

  8. 《数据结构》C++代码 Splay

    Splay,伸展树.之所以先写这个课内并不怎么常用的数据结构,是因为本人非常喜欢Splay,我觉得这是非常有美感且灵活的一种平衡树.在此先声明,我的伸展树写法来源于CLJ大牛,基础好的同学可以去他的博 ...

  9. 编译gearman提示缺少boost

    编译german时提示缺少boost: checking for boostlib >= 1.39... configure: We could not detect the boost lib ...

  10. 平衡二叉树(AVL)

    AVL就是优化二叉查找树 平衡因子不大于1 左 < 根 < 右 具体看代码 #include<bits/stdc++.h> using namespace std; typed ...