Intent的用途与组成
Intent用于处理Android各组件之间的通讯。Intent完成的工作主要有三部分:
1、Intent需标明本次通讯请求是从哪里来,到哪里去,要怎么走;
2、发起方携带上本次通讯需要的数据内容,接收方则对收到的Intent数据进行解包;
3、如发起方要求判断接收方的处理结果,Intent还需负责传回应答的数据内容;
Intent由以下部分组成:
Component : 组件,用于指定Intent的来源与目的
Action : 用于指定Intent的动作
Data(即Uri) : 用于指定动作要操纵的数据路径
Category : 用于指定动作的类别
Type : 数据类型,用于指定Data类型的定义
Extras : 扩展信息,用于指定装载的参数信息
Flags : 标志位,用于指定Intent的运行模式(也叫启动标志)。详细说明见上一节的《 Android开发笔记(三十九)Activity的生命周期》。
Intent的来源与目标
显式Intent
显式Intent便是直接指定来源类与目标的类名,属于精确匹配。下面在声明一个Intent对象时,第一个参数就指定了当前来源是MainActivity,第二个参数指定接下来要跳转到FirstActivity。
Intent intent = new Intent(MainActivity.this, FirstActivity.class);
startActivity(intent);
查看Intent的源码,看到这个显式的构造函数其实就是指定了一个Component。
public Intent(Context packageContext, Class<?> cls) {
mComponent = new ComponentName(packageContext, cls);
}
所以上面的Intent跳转也可以写成下面直接设置ComponentName的一种形式:
Intent intent = new Intent();
ComponentName component = new ComponentName(MainActivity.this, FirstActivity.class);
intent.setComponent(component);
startActivity(intent);
隐式Intent
隐式Intent没有明确指定要跳转的类名,只给出一个动作让系统自己去匹配拥有相同字串定义的目标,属于模糊匹配。因为常常我们不希望直接把源码的类名暴露出来,而只想给出一个事先定义好的名称,这样大家约定俗成按图索骥就好,所以隐式Intent便起到了这样的过滤作用。这个定义好的动作名称是个字符串,可以是我们自己定义的APP动作,也可以是系统自带的系统动作,下面是几个常用的系统动作说明:
ACTION_MAIN="android.intent.action.MAIN" : APP入口,每个应用程序启动时的入口
ACTION_VIEW="android.intent.action.VIEW" : 显示数据给用户
ACTION_EDIT="android.intent.action.EDIT" : 显示可编辑的数据
ACTION_CALL="android.intent.action.CALL" : 拨号
ACITON_DIAL="android.intent.action.DIAL" : 打电话
ACTION_SEND="android.intent.action.SEND" : 发短信
ACTION_ANSWER="android.intent.action.ANSWER" : 接电话
ACTION_SEARCH="android.intent.action.SEARCH" : 搜索,ActionBar上面SearchView的搜索动作
这个动作名称通过setAction方法指定,也可以通过构造函数Intent(String action)直接生成intent对象。当然,由于动作是模糊匹配,因此有时需要更详细的路径,比如说知道某人住在天通苑小区,但并不能直接找到他家,所以还得说明他住在天通苑的哪一期、哪号楼、哪一层、哪个单元。Uri和Category便是这样的路径与门类信息,Uri数据可通过构造函数Intent(String action, Uri uri)在生成对象时一起指定,也可通过setData方法指定(setData这个名字有歧义,实际就是setUri);而Category可通过addCategory方法来指定,之所以用add而不用set方法,是因为一个Intent可同时设置多个Category,一起加以过滤。
下面是个调用系统拨号程序的代码例子:
Intent intent = new Intent(Intent.ACTION_CALL, Uri.parse("tel:"+"15960238696"));
startActivity(intent);
不在构造函数里面直接指定的话,也可以这么写:
Intent intent = new Intent();
Uri uri = Uri.parse("tel:"+"15960238696");
intent.setAction(Intent.ACTION_CALL);
intent.setData(uri);
startActivity(intent);
Intent过滤器
过滤器是在隐式Intent中用到,顾名思义把不符合匹配条件的过滤掉,剩下符合条件的才按照优先顺序来调用。我们创建一个Android工程,AndroidManifest.xml里面的intent-filter就是xml中的过滤器,下面这个最常见的主页Acitivity,便设置了action和category的过滤条件,其中android.intent.action.MAIN表示APP入口动作,android.intent.category.LAUNCHER表示启动类别。
<activity
android:name=".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>
下面是几个常用的系统类别说明:
CATEGORY_BROWSABLE="android.intent.category.BROWSABLE" : 可被浏览器调用
CATEGORY_DEFAULT="android.intent.category.DEFAULT" : 可作为默认程序调用
CATEGORY_HOME="android.intent.category.HOME" : 可在系统启动时调用
CATEGORY_INFO="android.intent.category.INFO" : 在提供包信息时调用。该类别用于未注册CATEGORY_LAUNCHER的APP,如果不注册CATEGORY_LAUNCHER,就没有APP页面,也不会在桌面上显示,但总得有地方找到这个APP,所以就用到了CATEGORY_INFO。该类别基本用不上。
CATEGORY_LAUNCHER="android.intent.category.LAUNCHER" : 可在APP启动时调用
下面是在过滤器中运用类别的例子。现在有一个activity,它能够显示PDF文档,那么我们在AndroidManifest.xml中给它注册intent过滤器:
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="http" android:mimeType="application/pdf"/>
</intent-filter>
接着在浏览器中输入一个pdf文件的网址如"http://blog.csdn.net/aqi00/intent.pdf",这个Activity通过过滤器匹配就被自动调起来了。注意该过滤器还注册了一个Type,表示该Activity能够处理的数据类型即"application/pdf"。
Intent的参数传递
请求数据
前面说了,Intent的setData方法其实只是指定到达目标的路径,并非本次通讯所携带的参数信息,真正的参数信息是放在Extras中。Intent重载了很多种putExtra方法来传递各种类型的参数,包括String、int、double等基本数据类型,乃至Parcelable、Serializable这些类型的数据结构。不过大伙使劲地putExtra显然不好管理,像送快递一样大小包裹随便扔,不但找起来不方便、丢了也难以知道。所以Android又引入了Bundle这个概念,我们可以把Bundle理解为超市的寄包柜,或者时兴的快递收件柜,大小包裹由Bundle统一存取,方便又安全。
Bundle内部实质用于存放数据的是ArrayMap映射,可添加可删除还可判断是否存在,全部打包好了只要一次putExtras就好,同样的全部取出来也只要一次getExtras就行。下面是前一页面发送请求数据的代码例子:
Intent intent = new Intent(MainActivity.this, FirstActivity.class);
Bundle bundle = new Bundle();
bundle.putString("name", "张三");
bundle.putInt("age", 30);
bundle.putDouble("height", 160.0f);
intent.putExtras(bundle);
startActivity(intent);
下面是后一页面接收请求数据的代码例子:
Intent intent = getIntent();
Bundle bundle = intent.getExtras();
String name = bundle.getString("name", "");
int age = bundle.getInt("age", 0);
double height = bundle.getDouble("height", 0.0f);
应答数据
如同一般的通信那样,Intent有时只管把请求数据丢给下个页面,有时又得处理下个页面的应答数据(通常发生在下个页面返回到上个页面的时候)。只管把请求数据丢给下个页面,前一页面调用startActivity方法就好了;但又得处理下个页面的应答数据,这个时候便要分好几个步骤了。
1、前一页面打包好请求数据,调用方法startActivityForResult(Intent intent, int requestCode),方法名表示需要处理结果数据,第二个参数表示请求代码,用于标识每次请求的唯一性;
2、后一页面接收请求数据,进行相应处理;
3、后一页面在返回前一页面时,打包应答数据,调用setResult方法返回信息,setResult的第一个参数表示应答代码,代码示例如下:
Intent intent = new Intent();
Bundle bundle = new Bundle();
bundle.putString("job", "码农");
intent.putExtras(bundle);
setResult(Activity.RESULT_OK, intent);
4、前一页面重写方法onActivityResult,该方法的输入参数包含请求代码和应答代码,请求代码用于判断对应的是哪次请求,应答代码用于判断后一页面是否处理成功。然后就是对应答数据进行解包处理了,代码示例如下:
@Override
public void onActivityResult(int requestCode, int resultCode, Intent intent) {
Log.d(TAG, "onActivityResult. requestCode="+requestCode+", resultCode="+resultCode);
Bundle resp = intent.getExtras();
String job = resp.getString("job");
Toast.makeText(this, "您目前的职业是"+job, Toast.LENGTH_LONG).show();
}
点此查看Android开发笔记的完整目录