Android开发笔记(四十)组件通讯工具Intent

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开发笔记的完整目录

你可能感兴趣的:(android,Bundle,intent,ComponentName,组件通讯)