Android intent消息通知机制

Android intent消息通知机制 java

    一直想深刻分析研究android中的intent消息通知机制和binder机制,如今终于有机会了,在这里我先经过网上查询资料,看相关文献,研究分析了android中的intent机制,最后将学到的东西整理以下,供你们学习参考。 android

   大部分移动设备平台上的应用程序都运行在他们本身的沙盒中。他们彼此之间互相隔离,而且严格限制应用程序和原始组件之间的直接交互。咱们知道组件间交流是多么的重要,做为一个孤岛没有交流,必定毫无心义! 数据库

   Android应用程序也是一个沙盒,可是他们可以使用Intent、BroadcastReceivers、Adapters、ContentProviders、Internet去突破他们的边界互相交流。有交流还会和谐,因而可知这些交流手段有多重要。 数组

   咱们知道android应用程序的三大组件——Activities、Services、BroadcastReceiver(IntentReceiver),不一样的活动(服务,广播接收器)之间的交流便是经过消息触发,这个消息就称做意图(Intent)。那么这个消息是如何触发的,如何传递的,如何响应的,在这里咱们就借机顺着这条线,完全详细地介绍一下Intent。 数据结构

   为了以更容易理解,更清晰的形式介绍intent机制,现分两篇文章介绍:理论篇和实例篇。 app

实例篇: ide

本篇的主要内容以下: 学习

•        一、概述 测试

•        二、Intent对象 this

•        2.一、组件名字

•        2.二、动做

•        2.三、数据

•        2.四、种类

•        2.五、附加信息

•        2.六、标志

•        三、Intent解析

•        3.一、Intent过滤器

•        3.1.一、动做检测

•        3.1.二、种类检测

•        3.1.三、数据检测

•        3.二、通用状况

•        3.三、使用intent匹配

一、概述

   一个应用程序的三个核心组件——activities、services、broadcastreceivers,都是经过叫作intents的消息触发激活。Intent消息是android中一种同一或不一样应用程序中的组件之间延迟运行时绑定的消息通知机制。intent自己(一个Intent对象)是一个被动的数据结构,保存一个将要执行的操做的抽象描述,或在广播的状况下,一般是某事已经发生且正在宣告。

   具体地说:Intent负责对应用中一次操做的动做、动做涉及数据、附加数据进行描述,Android则根据此Intent的描述,负责找到对应的组件,将Intent传递给调用的组件,并完成组件的调用。所以,Intent在这里起着一个媒体中介的做用,专门提供组件互相调用的相关信息,实现调用者与被调用者之间的解耦。

   对于这三种组件,都有独立的传送intent的机制:

  (1)Activity:一个intent对象传递给Context.startActivity()或Activity.startActivityForRestult()去启动一个活动或使一个已存在的活动去作新的事情。

  (2)Service:一个intent对象传递给Context.startService()去初始化一个service或传递一个新的指令给正在运行的service。相似的,一个intent能够传递给Context.bindService()去创建调用组件和目标服务之间的链接。

   (3)BroadcastReceiver:一个intent对象传递给任何广播方法(如Context.sendBroadcast(),Context.sendOrderedBroadcast(),Context.sendStickyBroadcast()),都将传递到全部感兴趣的广播接收者。

   在每种状况下,Android系统查找合适的activity、service、broadcastreceivers来响应意图,若是有必要的话,先初始化他们。这些消息系统之间没有重叠,即广播意图仅会传递给广播接收者,而不会传递活动或服务,反之亦然。

   下面首先描述intent对象,而后介绍Android将intent映射到相应组件的规则——如何解决哪一个组件应该接收intent消息。对于没有指定目标组件名字的intent,这个处理过程包括按照intentfilters匹配每一个潜在的目标对象。

二、Intent对象

   一个Intent对象是一个捆信息,包含对intent有兴趣的组件的信息(如要执行的动做和要做用的数据)、Android系统有兴趣的信息(如处理intent组件的分类信息和如何启动目标活动的指令)。下面列出它的主要信息:

2.一、组件名字

   处理intent的组件的名字。这个字段是一个ComponentName对象——是目标组件的彻底限定类名(如"com.example.project.app.FreneticActivity")和应用程序所在的包在清单文件中的名字(如"com.example.project")的组合。其中组件名字中的包部分没必要必定和清单文件中的包名同样。组件名字是可选的,若是设置了组件名字,intent对象传递到指定类的实例;若是没有设置,Android使用intent中的其它隐式信息来定位合适的目标组件(见下面的Intent解析)。

   组件的名字经过setComponent(),setClass()或setClassName()设置,经过getComponent()读取。

2.二、动做

   一个字符串命名的动做将被执行,或在广播intent中,已发生动做且正被报告。Intent类定义了一些动做常量,以下:

Constant

Target component

Action

ACTION_CALL

activity

Initiate a phone call.

ACTION_EDIT

activity

Display data for the user to edit.

ACTION_MAIN

activity

Start up as the initial activity of a task, withno data input and no returned output.

ACTION_SYNC

activity

Synchronize data on a server with data on themobile device.

ACTION_BATTERY_LOW

broadcast receiver

A warning that the battery is low.

ACTION_HEADSET_PLUG

broadcast receiver

A headset has been plugged into the device, orunplugged from it.

ACTION_SCREEN_ON

broadcast receiver

The screen has been turned on.

ACTION_TIMEZONE_CHANGED

broadcast receiver

The setting for the time zone has changed.

查看更多的动做请参考Intent类。其它的动做定义在AndroidAPI中,咱们还能够定义本身的动做字符串,而后在咱们的应用程序中激活组件。自定义动做字符串应该包含应用程序包名前缀,如"com.example.project.SHOW_COLOR"。

   动做很大程度上决定了剩下的intent如何构建,特别是数据(data)和附加(extras)字段,就像一个方法名决定了参数和返回值。正是这个缘由,应该尽量明确指定动做,并紧密关联到其它intent字段。换句话说,你应该定义你的组件可以处理的Intent对象的整个协议,而不只仅是单独地定义一个动做。

   一个intent对象的动做经过setAction()方法设置,经过getAction()方法读取。

2.三、数据

   数据(data)是将做用于其上的数据的URI和数据的MIME类型。不一样的动做有不一样的数据规格。例如,若是动做字段是ACTION_EDIT,数据字段包含将显示用于编辑的文档的URI;若是动做是ACTION_CALL,数据字段将是一个tel:URI和将拨打的号码;若是动做是ACTION_VIEW,数据字段是一个http:URI,接收活动将被调用去下载和显示URI指向的数据。

   当匹配一个intent到一个可以处理该数据的组件,一般知道数据的类型(它的MIME类型)和它的URI很重要。例如,一个组件可以显示图像数据,不该该被调用去播放一个音频文件。在许多状况下,数据类型可以从URI中推测,特别是content:URIs,它表示位于设备上的数据且被内容提供者(contentprovider)控制。可是类型也可以显示地设置,setData()方法指定数据的URI,setType()指定MIME类型,setDataAndType()指定数据的URI和MIME类型。经过getData()读取URI,getType()读取类型。

2.四、种类

   此外,还包含关于应该处理intent的组件类型信息。能够在一个Intent对象中指定任意数量的种类描述。Intent类定义的一些种类常量,以下这些:

Constant

Meaning

CATEGORY_BROWSABLE

The target activity can be safely invoked by thebrowser to display data referenced by a link — for example, animage or an e-mail message.

CATEGORY_GADGET

The activity can be embedded inside of anotheractivity that hosts gadgets.

CATEGORY_HOME

The activity displays the home screen, the firstscreen the user sees when the device is turned on or when the HOMEkey is pressed.

CATEGORY_LAUNCHER

The activity can be the initial activity of atask and is listed in the top-level application launcher.

CATEGORY_PREFERENCE

The target activity is a preference panel.

更多的种类常量请参考Intent类。

   下面是一些关于种类的调用方法:addCategory()方法添加一个种类到Intent对象中,removeCategory()方法删除一个以前添加的种类,getCategories()方法获取Intent对象中的全部种类。

2.五、附加信息

   额外的键值对信息应该传递到组件处理intent。就像动做关联的特定种类的数据URIs,也关联到某些特定的附加信息。例如,一个ACTION_TIMEZONE_CHANGEintent有一个"time-zone"的附加信息,标识新的时区,ACTION_HEADSET_PLUG有一个"state"附加信息,标识头部如今是否塞满或未塞满;有一个"name"附加信息,标识头部的类型。若是你自定义了一个SHOW_COLOR动做,颜色值将能够设置在附加的键值对中。

   Intent对象有一系列的put…()方法用于插入各类附加数据和一系列的get…()用于读取数据。这些方法与Bundle对象的方法相似,实际上,附加信息能够做为一个Bundle使用putExtras()和getExtras()安装和读取。关于Bundle的用法查阅相关资料。

2.六、标志

   Intent对象中有各类各样的标志,许多指示Android系统如何去启动一个活动(例如,活动应该属于那个任务)和启动以后如何对待它(例如,它是否属于最近的活动列表)。全部这些标志都定义在Intent类中。

三、Intent解析

   Intent能够分为两组:

  (1)显式intent:经过名字指定目标组件。由于开发者一般不知道其它应用程序的组件名字,显式intent一般用于应用程序内部消息,如一个活动启动从属的服务或启动一个姐妹活动。

  (2)隐式intent:并不指定目标的名字(组件名字字段是空的)。隐式intent常常用于激活其它应用程序中的组件。

   显示intent传递:Android显式传递一个intent到一个指定目标类的实例。Intent对象中只用组件名字内容去决定哪一个组件应该得到这个intent,而不用其余内容。

   隐式intent传递:须要另一种不一样的策略。因为缺省指定目标,Android系统必须查找一个最适合的组件(一些组件)去处理intent——一个活动或服务去执行请求动做,或一组广播接收者去响应广播声明。这是经过比较Intent对象的内容和intent过滤器(intentfilters)来完成的。每一个intent过滤器都关联到一个潜在的接收intent的组件。过滤器声明组件的能力和界定它能处理的intents,它们打开接收组件声明的intent类型的隐式intents。若是一个组件没有任何intent过滤器,它仅能接收显示的intents,而声明了intent过滤器的组件能够接收显示和隐式的intents。

   注意:只有当一个Intent对象的下面三个方面都符合一个intent过滤器:action、data(包括URI和数据类型)、category,才被考虑。附加信息和标志在解析哪一个组件接收intent中不起做用。

3.一、Intent过滤器

   活动、服务、广播接收者为了告知系统可以处理哪些隐式intent,它们能够有一个或多个intent过滤器。每一个过滤器描述组件的一种能力,即乐意接收的一组intent。实际上,它筛掉了不想要的intents,也仅仅是不想要的隐式intents。一个显式intent老是可以传递到它的目标组件,无论它包含什么,不考虑过滤器。可是一个隐式intent,仅当它可以经过组件的过滤器之一才可以传递给它。

   一个组件可以作的每一工做,都有独立的过滤器。例如,记事本中的NoteEditer活动有两个过滤器,一个是启动一个指定的记录,用户能够查看和编辑;另外一个是启动一个新的、空的记录,用户可以填充并保存。一个intent过滤器是一个IntentFilter类的实例。由于Android系统在启动一个组件以前必须知道它的能力,要注意的是:intent过滤器一般不在java代码中设置,而是在应用程序的清单文件(AndroidManifest.xml)中以<intent-filter>元素设置。但有一个例外,广播接收者的过滤器经过调用Context.registerReceiver()动态地注册,它直接建立一个IntentFilter对象。

   每个过滤器都有对应于Intent对象的动做、数据、种类的字段。过滤器要检测隐式intent的全部这三个字段,其中任何一个失败,Android系统都不会传递intent给组件。然而,由于一个组件能够有多个intent过滤器,一个intent通不过组件的过滤器检测,其它的过滤器可能经过检测。

3.1.一、动做检测

   清单文件中的<intent-filter>元素以<action>子元素列出动做,例如:

<intent-filter . . .>

   <actionandroid:name="com.example.project.SHOW_CURRENT"/>

   <actionandroid:name="com.example.project.SHOW_RECENT"/>

   <actionandroid:name="com.example.project.SHOW_PENDING"/>

    . . .

</intent-filter>

像例子所展现,虽然一个Intent对象仅是单个动做,可是一个过滤器能够列出不止一个。这个列表不可以为空,一个过滤器必须至少包含一个<action>子元素,不然它将阻塞全部的intents。要经过检测,Intent对象中指定的动做必须匹配过滤器的动做列表中的一个。若是对象或过滤器没有指定一个动做,结果将以下:

(1)若是过滤器没有指定动做,没有一个Intent将匹配,全部的intent将检测失败,即没有intent可以经过过滤器。

(2)若是Intent对象没有指定动做,将自动经过检查(只要过滤器至少有一个过滤器,不然就是上面的状况了)

3.1.二、种类检测

   相似的,清单文件中的<intent-filter>元素以<category>子元素列出种类,例如:

<intent-filter . . .>

   <categoryandroid:name="android.intent.category.DEFAULT"/>

   <categoryandroid:name="android.intent.category.BROWSABLE"/>

    . . .

</intent-filter>

   注意:本文前面两个表格列举的动做和种类常量并不在清单文件中使用,而是使用全字符串值。例如,例子中所示的"android.intent.category.BROWSABLE"字符串对应于本文前面提到的BROWSABLE常量。相似的,"android.intent.action.EDIT"字符串对应于ACTION_EDIT常量。

   对于一个intent要经过种类检测,intent对象中的每一个种类必须匹配过滤器中的一个。即过滤器可以列出额外的种类,可是intent对象中的种类都必须可以在过滤器中找到,只要有一个种类在过滤器列表中没有,就算种类检测失败。所以,原则上若是一个intent对象中没有种类(即种类字段为空)应该老是经过种类测试,而无论过滤器中有什么种类。可是有个例外,Android对待全部传递给Context.startActivity()的隐式intent好像它们至少包含"android.intent.category.DEFAULT"(对应CATEGORY_DEFAULT常量)。所以,活动想要接收隐式intent必需要在intent过滤器中包含"android.intent.category.DEFAULT"。

   注意:"android.intent.action.MAIN" 和"android.intent.category.LAUNCHER"设置,它们分别标记活动开始新的任务和带到启动列表界面。它们能够包含"android.intent.category.DEFAULT"到种类列表,也能够不包含。

3.1.三、数据检测

   相似的,清单文件中的<intent-filter>元素以<data>子元素列出数据,例如:

<intent-filter . . .>

   <data android:mimeType="video/mpeg"android:scheme="http" . . . />

   <data android:mimeType="audio/mpeg"android:scheme="http" . . . />

    . . .

</intent-filter>

每一个<data>元素指定一个URI和数据类型(MIME类型)。它有四个属性scheme、host、port、path对应于URI的每一个部分:scheme://host:port/path。例以下面的URI:“content://com.example.project:200/folder/subfolder/etc”,scheme是content,host是"com.example.project",port是200,path是"folder/subfolder/etc"。host和port一块儿构成URI的凭据(authority),若是host没有指定,port也被忽略。这四个属性都是可选的,但它们之间并不都是彻底独立的。要让authority有意义,scheme必须也要指定。要让path有意义,scheme和authority也都必需要指定。

   当比较intent对象和过滤器的URI时,仅仅比较过滤器中出现的URI属性。例如,若是一个过滤器仅指定了scheme,全部有此scheme的URIs都匹配过滤器;若是一个过滤器指定了scheme和authority,但没有指定path,全部匹配scheme和authority的URIs都经过检测,而无论它们的path;若是四个属性都指定了,要都匹配才能算是匹配。然而,过滤器中的path能够包含通配符来要求匹配path中的一部分。

   <data>元素的type属性指定数据的MIME类型。Intent对象和过滤器均可以用"*"通配符匹配子类型字段,例如"text

    @Override

    publicvoid onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.main);

 

        IntentorgIntent=getIntent();

        UriqueryUri=orgIntent.getData();        

        finalCursor c = managedQuery(queryUri,

                null,

                null,

                null,

                null);

        

        String[]fromColumns=newString[]{ContactsContract.Contacts.DISPLAY_NAME};

        int[]toLayoutIDs = new int[] { R.id.itemTextView};

        SimpleCursorAdapteradapter = new SimpleCursorAdapter(this,

                R.layout.listitemlayout,c, fromColumns, toLayoutIDs);

        ListViewlv = (ListView) findViewById(R.id.contactListView);

        lv.setAdapter(adapter);

        lv.setOnItemClickListener(newOnItemClickListener() {

            @Override

            publicvoidonItemClick(AdapterView<?> parent,View view, int pos,

                    longid) {

                c.moveToPosition(pos);    

                introwId =c.getInt(c.getColumnIndexOrThrow(ContactsContract.Contacts._ID));

                UrioutURI =Uri.parse(ContactsContract.Contacts.CONTENT_URI.toString()+ rowId);

                IntentoutData = new Intent();

                outData.setData(outURI);

                setResult(Activity.RESULT_OK,outData);

                finish();

            }

        });

    }

}

 

STEP四、解析通信录返回的数据

   从通信录活动返回以后,咱们从返回的Intent中提取数据并填充到填写电话号码的EditView中。代码主要以下:

@Override

public voidonActivityResult(int reqCode, int resCode, Intentdata) {

    super.onActivityResult(reqCode,resCode, data);

 

    switch(reqCode) {

    case(PICK_CONTACT): {

        if(resCode == Activity.RESULT_OK) {

            Stringname;                

            UricontactData = data.getData();

            Cursorc = managedQuery(contactData, nullnull,nullnull);

            c.moveToFirst();

            name=c.getString(c.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));

            TextViewtv;

            tv= (TextView)findViewById(R.id.edtPhoneNo);

            tv.setText(name);

        }

        break;

    }

    }

}

 

STEP五、在清单文件AndroidManifest.xml中注册通信录活动和读取Contact数据库的权限

   主要工做基本作完了,如今咱们只须要注册通信录活动和读取Contact数据的权限了。完整的清单文件代码以下:

<?xml version="1.0"encoding="utf-8"?>

<manifestxmlns:android="http://schemas.android.com/apk/res/android"

    package="skynet.com.cnblogs.www"android:versionCode="1"

    android:versionName="1.0">

    <application>

        <activityandroid:name=".TextMessage"android:label="@string/app_name">

            <intent-filter>

                <actionandroid:name="android.intent.action.MAIN"/>

                <categoryandroid:name="android.intent.category.LAUNCHER"/>

            </intent-filter>

        </activity>

        <activityandroid:name=".ContactPick"android:label="@string/app_name">

            <actionandroid:name="android.intent.action.PICK"/>

            <categoryandroid:name="android.intent.category.DEFAULT"/>

        </activity>

    </application>

    <uses-permissionandroid:name="android.permission.SEND_SMS"/>

    <uses-permissionandroid:name="android.permission.READ_CONTACTS"/>

</manifest>

   注意通信录活动的IntentFilters,它的action是android.intent.action.PICK;category是android.intent.category.DEFAULT。如今咱们分析一下这个IntentFilter:

•      <action android:name="android.intent.action.PICK"/>:使用户可以能够在通信录列表中选择一个,而后将选择的联系人的 URL返回给调用者。

•      <categoryandroid:name="android.intent.category.DEFAULT"/>:这是默认的category,若是不知道category系统会自动加上。这个属性是让使其可以被像Context.startActivity()等找到。要说明的的是,若是列举了多个category,这个活动仅会去处理那些Intent中都包含了全部列举的category的组件。

   咱们还能够在清单文件中看到TextMessage活动的Intent Filter:

<intent-filter>

    <actionandroid:name="android.intent.action.MAIN"/>

    <categoryandroid:name="android.intent.category.LAUNCHER"/>

</intent-filter>

    它指定TextMessage活动是这个程序的入口,而且TextMessage会列举在Launcher即启动列表中。


总结

   咱们用发短信中选择联系人的例子说明Intent和Intent Filter,这里体现了两个活动之间如何经过Intent和IntentFilter来交互,这也是咱们在编写Android应用程序的时候常常遇到了。本文除了上述的主要内容以外,还涉及别的知识点,下面列举几个我的认为比较有用的知识点:

•    Curor类它跟咱们平时用的数据库中的游标相似,它提供了对从数据库返回的结果的随机读写操做。如咱们例子中用到的,经过managedQuery方法查询数据库并返回结果,而后利用Cursor对它进行操做。下面介绍Cursor类的几个方法(咱们例子中用到的,更多的方法请自行查阅相关资料):

•      public abstract int getColumnIndexOrThrow (StringcolumnName):返回给定列名的索引(注意:从0开始的),或者当列名不存在时抛出llegalArgumentException异常;

•      public abstract boolean moveToFirst():移动到第一行。若是Cursor为空,则返回FALSE

•      public abstract boolean moveToPosition (intposition):将游标移动到一个指定的位置,它的范围在-1 <= position<= count。若是position位置不可达,返回FALSE

•      managedQuery方法:根据指定的URI路径信息返回包含特定数据的Cursor对象,应用这个方法可使Activity接管返回数据对象的生命周期。参数:

URI: Content Provider 须要返回的资源索引

Projection: 用于标识有哪些columns须要包含在返回数据中

Selection: 做为查询符合条件的过滤参数,相似于SQL语句中Where以后的条件判断

SelectionArgs: 同上

SortOrder: 用于对返回信息进行排序

•      SimpleCursorAdapter容许你绑定一个游标的列到ListView上,并使用自定义的layout显示每一个项目。SimpleCursorAdapter的建立,须要传入当前的上下文、一个layout资源,一个游标和两个数组:一个包含使用的列的名字,另外一个(相同大小)数组包含View中的资源ID,用于显示相应列的数据值。

相关文章
相关标签/搜索