从源码剖析Android中的Intent组件

我们知道,Intent主要用来激活安卓几大组件,那么它具体是怎样来激活的?激活时是否可以携带java对象?为何要将对象序列化后才能传递?

一、Intent官网解释
Intent可以被startActivity用来加载Activity,也可以被broadcastIntent发送给指定的BroadReceiver组件,
或者被startService、bingService来与后台service通信。
Intent最主要作用就是加载Activity,好比Activity之间的胶水。
Intent数据结构:

  • action:所要执行的动作;(例如:ACTION_CALL创建打电话Activity;ACTION_BATTERY_LOW 发出广播警告电池电量低,)
  • data: 要使用的数据(Uri);
  • category:关于目标组件的信息;
  • component:目标组件的类名;
  • extras :这是Bundle数据。

Intent解析:

  • 显式Intent,指定了目标组件的类名,即component,则已知目标组件,不需解析;
  • 隐式Intent,未指定目标组件component,或者不知道、不关心谁来接收Intent,需要Android自己去解析找到目标组件。

隐式Intent解析方法:

1.在AndroidManifest.xml里所有<intent-filter>及其中定义的Intent;
2.通过PackageManager(获取当前设备所安装的应用程序package)查找能处理这个Intent的component。匹配Action、type、category三个变量来寻找。
二、简单解释:
Intent可以激活Andorid的三大组件:Activity、Service和BroadcastReceiver。使用Intent时一般要显式指定目标组件,若未指定则要根据Intent附带的action、type、category三个值来解析,查找能处理的组件。

三、问题:Intent如何实现组件的切换,具体流程?
1、基本方法:(以启动Activity为例)

Intent i = new Intent(MainActivity.this, TargetActivity.class);
startActivity(i);

2、实例化Intent:

/**
* 创建一个Intent,直接指定Intent要激活的**组件类名**,而不用依赖**系统去解析**合适的类来处理intent
* @param packageContext 要执行这个intent的context对象
* @param cls intent要激活的组件类名
*/
public Intent(Context packageContext, Class cls) {
//创建一个组件并赋值给Intent的Component成员
  mComponent = new ComponentName(packageContext, cls);
}

3、启动Activity

startActivity(i) ->
startActivity(Intent intent, @Nullable Bundle options)->
startActivityForResult(intent, -1, options)

/**
*startActivityForResult(Intent intent, int requestCode, @Nullable Bundle options)
* 加载一个Activity并获取结果
* @param intent 要启动的intent.
* @param requestCode 如果大于0则会被返回,且只有返回值返回成功后才会显示视图
* @param options 其他信息.
*/
public void startActivityForResult(Intent intent, int requestCode, @Nullable Bundle options) {
if (mParent == null) { //如果没有父Activity;Instrumentation是用来与程序指南清单AndroidManifest文件交互的。
  Instrumentation.ActivityResult ar =
  mInstrumentation.execStartActivity(this, mMainThread.getApplicationThread(), mToken, this,intent, requestCode, options); //执行startActivity命令
.....
} else { //如果有父Activity
   if (options != null) {
     mParent.startActivityFromChild(this, intent, requestCode, options);
   } .....
}

4、执行startActivity命令核心代码:
启动Activity的任务交给了底层ActivityManagerNative来做。

intent.migrateExtraStreamToClipData(); //将intent里的bundle数据进行处理以便给底层处理
intent.prepareToLeaveProcess(); //准备离开应用程序进程,进入ActivityManagerService进程(意味着bundle的数据要在进程间传递)
int result = ActivityManagerNative.getDefault().startActivity(whoThread,
who.getBasePackageName(), intent,
intent.resolveTypeIfNeeded(who.getContentResolver()),
token, target != null ? target.mEmbeddedID : null,
requestCode, 0, null, options); //调用系统的activity manager服务来启动新的Activity。考虑如果是显式Intent,则直接找对对应的组件类(此处是Activity组件);如果是隐式Intent,为指定目标组件类名,则自动去Application->system搜索合适的组件来处理。
//todo:具体的系统级代码下次进行分析

四、核心问题:为何Intent不能直接在组件间传递对象而要通过序列化机制?
根据上面代码可以看到,Intent在启动其他组件时,会离开当前应用程序进程,进入ActivityManagerService进程(intent.prepareToLeaveProcess()),这也就意味着,Intent所携带的数据要能够在不同进程间传输。首先我们知道,Android是基于Linux系统,不同进程之间的java对象是无法传输,所以我们此处要对对象进行序列化,从而实现对象在 应用程序进程 和 ActivityManagerService进程 之间传输。
而Parcel或者Serializable都可以将对象序列化,其中,Serializable使用方便,但性能不如Parcel容器,后者也是Android系统专门推出的用于进程间通信等的接口。

附加知识:
在不同进程之间,常规数据类型可以直接传递,如整数,以传递字符串为例,要从A进程传递到B进程,只需在B进程的内存区开辟一样大小的空间,然后复制过去即可。
但是,对象却不能直接跨进程传递。即使成员变量值能传递过去,成员方法是无法传递过去的,此时如果B进程要调用成员方法则出错。
具体传递对象的方法:
1. 在进程A中把类中的非默认值的属性和类的唯一标志打成包(这就叫序列化);
2. 把这个包传递到进程B;
3. 进程B接收到包后,根据类的唯一标志把类创建出来(java反射机制);
4. 然后把传来的属性更新到类对象中。
这样进程A和进程B中就包含了两个完全一样的类对象。

五、Intent如何实现对象传递?
Object implements Serializable {...};bundle.putSerializable(Key, Object);
Object implements Parcelable {...} ; bundle.putParcelable(Key, Object);
Serializable接口:这是Java的序列化技术,将Java对象序列化为二进制文件。让对象实现Serializable接口,使用ObjectInputStream 和 ObjectOutputStream 进行对象读写。
Parcelable接口:这是Android提供的用作封装数据的容器,封装后的数据可以通过Intent或IPC来传递。只有基本类型和实现了Parcelable接口的类才能被放入Parcel中。
六、Serializable接口 - Java
属于java序列化机制:只需让java类实现该接口,不用实现任何方法,即可标记该类可序列化。

class Person implements Serializable {...}
Person per = new Person();
bundle.putSerializable("person", per); //传递Person对象的引用

Person mPerson = (Person)getIntent().**getSerializableExtra**("person");

注意:如果此处序列化类Person内部包含其他类(如:PersonInfo)的引用,如:

class Person implements Serializable {
   PersonInf**o info;
}

那么所引用的类必须也可序列化,即实现Serializable接口。因为Person对象在序列化过程中,也会对成员变量序列化。

七、Parcelable接口 - Android
此处围绕 - Android中如何使用Parcel实现对象的传递 - 简单介绍一下原因。
首先要了解Android里面的Parcel容器。

Parcel是一个容器,用来存储可通过IBindler传送的消息(数据或对象引用)。
主要用于轻量级、高性能IPC进程间通信的消息容器。在Android里,一个“process”是一个标准Linux进程,一般而言一个进程无法接触到另一个进程的内存区。而通过Parcel,Android系统会将对象分解成可序列化与反序列化,从而实现进程间通信。
不过,Parcel同样可用于进程内通信,主要实现在应用程序的不同组件之间传递数据。例如,我们可以使用Intent封装Parcel对象在Activity之间传递。
简单来说,Parcel容器实现了进程内与进程间通信,而且还能实现远程调用。

组件间传递对象的具体方法:

让要传递的对象所属类实现 Parcelable 接口;
实现 describeContents 方法;
实现抽象方法 writeToParcel,用于获取对象的当前状态并写入一个Parcel容器中;
给该目标类添加一个静态域 CREATOR ,它是一个实现了Parcelable.Creator接口的对象;
添加一个参数为一个Parcel对象的构造函数,CREATOR会调用这个构造函数来重新改造我们的对象。
问题:
为什么已经有了Java的Serializable接口还要创建一个Parcelable接口?
性能
虽然Parcelable使用起来更复杂一点,但是它的性能更好。

Parcelable的限制:

当使用Parcelable来传递图片Bitmap时不太理想,虽然Bitmap也实现了Parcelable接口。比较优的方法是传递
Parcelable不能用来当做常规的序列化存储,因为Android系统版本不同,Parcelable的具体实现方法也不完全一样,可能导致无法读取Parcel数据。

(0)

相关推荐

  • 详解Android应用开发中Intent的作用及使用方法

    Intent是一种运行时绑定(run-time binding)机制,它能在程序运行过程中连接两个不同的组件.通过Intent,你的程序可以向Android表达某种请求或者意愿,Android会根据意愿的内容选择适当的组件来完成请求.比如,有一个Activity希望打开网页浏览器查看某一网页的内容,那么这个Activity只需要发出WEB_SEARCH_ACTION给Android,Android就会根据Intent的请求内容,查询各组件注册时声明的IntentFilter,找到网页浏览器的Ac

  • Android Intent启动别的应用实现方法

    我们知道Intent的应用,可以启动别一个Activity,那么是否可以启动别外的一个应用程序呢,答案是可以的. 1.首先我们新建一个Android应用,名为AnotherPro,此应用什么内容都没有,用于被另外一个程序打开. 2.新建一个工程用于打开上面的应用,程序界面如下 3.修改程序代码,在onCreate中添加如下代码 anotherPro = (Button) findViewById(R.id.startAnotherPro);calendar = (Button) findView

  • android中Intent传值与Bundle传值的区别详解

    举个例子我现在要从A界面跳转到B界面或者C界面   这样的话 我就需要写2个Intent如果你还要涉及的传值的话 你的Intent就要写两遍添加值的方法 那么 如果我用1个Bundle  直接把值先存里边 然后再存到Intent中 不就更简洁吗? 另外一个例子如果我现在有Activity A ,B ,C:现在我要把值通过A经过B传给C你怎么传 如果用Intent的话 A-B先写一遍 再在B中都取出来 然后在把值塞到Intent中 再跳到C 累吗?如果我在A中用了 Bundle 的话  我把Bun

  • Android中使用IntentService创建后台服务实例

    IntentService提供了在单个后台线程运行操作的简单结构.这允许它操作耗时操作,而不影响UI响应.同样,IntentService也不影响UI生命周期事件,所以,它在某些可能关闭AsyncTask的情况下,仍会继续运行(实测在Activity的onDestory里写AsyncTask无法运行). IntentService有如下限制: 1.它不能直接影响UI.要把结果反映给UI,需要发给Activity 2.工作请求会顺序运行.如果一个操作未结束,后面发送的操作必须等它结束(单线程) 3

  • Android组件间通信--深入理解Intent与IntentFilter

    Understanding Intent and IntentFilter--理解Intent和IntentFilterIntent(意图)在Android中是一个十分重要的组件,它是连接不同应用的桥梁和纽带,也是让组件级复用(Activity和 Service)成为可能的一个重要原因.Intent的使用分为二个方面一个是发出Intent,另一个则是接收Intent用官方的说法就是Intent Resolving.本主将对Intent和IntentFilter进行一些介绍.Intent和Inte

  • Android系列之Intent传递对象的几种实例方法

    在Android中intent传递对象主要有2种方式分别是,Bundle.putSerializable(Key,Object)和Bundle.putParcelable(Key, Object);当然这些Object是有一定的条件的,前者是实现了Serializable接口,而后者是实现了Parcelable接口,以下是我为大家做的一个实例 首先我们建立一个工程项目命名为:ObjectTestDemo 然后我们再修改main.xml布局文件,主要增加2个按钮view plaincopy to

  • android教程之intent的action属性使用示例(intent发短信)

    Action :规定了Intent要完成的动作,是一个字符串常量.使用setAction()来设置Action属性,使用getAction()来获得Action属性.既可以使用系统内置的Action,也可以自己定义.系统自定义的action,如ACTION_VIEW, ACTION_EDIT, ACTION_MAIN等等. 1.自定义Action 在"目的Activity"的AndroidManifest.xml中指定action常量. 复制代码 代码如下: <activity

  • Android Intent的几种用法详细解析

    Intent应该算是Android中特有的东西.你可以在Intent中指定程序要执行的动作(比如:view,edit,dial),以及程序执行到该动作时所需要的资料.都指定好后,只要调用startActivity(),Android系统会自动寻找最符合你指定要求的应用程序,并执行该程序. 下面列出几种Intent的用法显示网页: 复制代码 代码如下: Uri uri = Uri.parse("http://www.google.com");Intent it  = new Intent

  • Android学习笔记--Activity中使用Intent传值示例代码

    Intent,又称为意图,是一种运行时绑定机制,它能在程序运行的过程中链接两个不同的组件(Activity.Service.BroadcastReceiver).通过Intent,程序可以向Android表达某种请求或意愿,Android会根据意愿的内容选择适当的组件来请求. 在这些组件之间的通讯中,主要是由Intent协助完成的.Intent负责对应用中一次操作的动作.动作涉及数据.附加数据进行描述,Android则根据此Intent的描述,负责找到对应的组件,将Intent传递给调用的组件,

  • Android 使用Intent传递数据的实现思路与代码

    Intent是Android中一个非常重要的概念,跟这个词的本意(意图,目的)一样,这个类在Android中的作用就是要调用某个组建去做某一件事,比如通过startActivity启动一个Activity,通过startService启动一个Service,通过sendBroadcast发送一个广播等,它相当于各个组建间的一个桥梁.很重要的一点是这个过程是可以跨进程的,比如在应用中可以启动调用某个播放器组件(系统自身的或者第三方的)来播放一段视频,启动照相机程序拍照等等. 当我们启动视频播放器播

随机推荐