Intent 传递数据源码解析

起因

相信做过做过安卓 APP 开发的同学对 Intent 这个东西一定不会陌生,无论是打开 Activity,Service,还是唤醒系统的服务器都离不开它。当我们使用 Intent 打开组件(安卓中的四大组件),我们可以先将数据设置给 Intent 然后在经过系统传递给目标组件,那么 Intent 是如何将数据传递的呢?为什么传递对象为什么要实现 Parcelable 接口呢?

具体原因就需要通过源码来寻找答案了。

Intent

Intent 中文意识为意图,基本如果说学安卓认识的第一个类叫 Activity 那么认识的第二类就一定是 Intent 了,比如打开一个新的 Activity 就离不开 Intent。先看看使用 Intent 打开一个 Activity 的步骤,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14

class MainActivity : AppCompatActivity() {

//单击事件
fun toMain2Activity(view: View) {
val intent = Intent(this,Main2Activity::class.java)
//等价于
val intent = Intent().apply{
compent = CompentName(this,Main2Activity::class.java)
}
startActivity(intent)
}

}

可以看到,如果我们想打开一个新的 Activity 只需要两个参数,分别是

  1. Context 对象
  2. 目标 Activity 的 class 对象

而如果我们想传递一些数据给新 Activity,那么我们就要使用 Intent 的 一些方法,而且对传递数据的类型也是有限制的。具体如下:

1
2
3
4

val intent = Intent().apply{
putExtra("key", "value")
}

Intent 的 putExtra() 方法的 key 只能是 String 类型,value 支持 Java 中的基本数据类型,和基本数据的数组类型,但是有几个类型是比较特殊的,比如:

1
2
3
4
5
6
7

val intent = Intent().apply{
putIntegerArrayListExtra()
putStringArrayListExtra()
putCharSequenceArrayListExtra()
putParcelableArrayListExtra()
}

可以看到,Intent 只提供了上面 4 种类型的 ArrayList 传递的方法,这里有一点要注意一下,这里 Intent 使用的是 ArrayList 而不是 List,这就表明了我们只能传递 ArrayList 类型的对象,不能传 LinkedList 等等之类的,这一点和 Java 的集合框架有点冲突。

而且在 Kotlin 中如果我们使用 listOf() 或者 mutableListOf() 方法创建的集合是无法在上面 4 个方法中使用的,必须要使用 arrayListOf() 创建的集合才能传递。不知道是不是 Kotlin 故意正针对 Android XD。

Parcelable

Intent 的 putExtra(String, Parcelable) 这个方法应该是用的最多的了,其主要作用就是将一个对象添加到 Intent 里面,方便在 Activity 之间传递,这个对象必须要实现 Parcelable 接口。

Parcelable 接口是 Android 提供的一种机制,用于将一个类的对象的数据写入到 Parcel 中或者从 Parcel 读取出来。其实现方式稍微有一点复杂,切 Kotlin 的实现方式和 Java 有一点不一样,所有下面就用 Kotlin 为例来看看怎么实现 Parcelable 接口,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

data class User(
val id: Long,
var name: String
) : Parcelable {
constructor(source: Parcel) : this(
source.readLong(),
source.readString()
)

//此方法基本上只用返回 0
override fun describeContents() = 0

override fun writeToParcel(dest: Parcel, flags: Int) = with(dest) {
writeLong(id)
writeString(name)
}

companion object {
@JvmField
val CREATOR: Parcelable.Creator<User> = object : Parcelable.Creator<User> {
override fun createFromParcel(source: Parcel): User = User(source)
override fun newArray(size: Int): Array<User?> = arrayOfNulls(size)
}
}
}

如上,Parcelable 是 Android 中序列化对象的方式,和 Java 中 Serializable 作用是一样的,不过和 Serializable 不一样的是 Android 中的 Parcelable 实现起来比较麻烦,需要实现几个方法,如上面的 writeToParcel() 方法,通名字可以推断出这个方法的作用就是将我们的对象写入到 Parcel 对象中。除了要实现 writeToParcel() 方法以外,还需要提供一个参数类型为 Parcel 的构造函数,很显然我们c除了要将对象写入带 Parcel 对象中,还要讲 Parcel 中的值读出来,最后我们还要提供一个静态 CREATOR 其类型为 Parcelable.Creator,需要注意的是在 Kotlin 中要给 CREATOR 设置一个 @JvmFiel 的注解,否则出现下面这个错误。

1
2

android.os.BadParcelableException: Parcelable protocol requires a Parcelable.Creator object called CREATOR on class com.lz.demo1.User

原因在于,Kotlin 默认生成的属性是提供了 Getter/Setter 方法的,而我们在实现 Parcelable 接口的时候,IDE 只认识名为 CREATOR 的静态公共对象,这个时候 @JvmField 注解就派上用处了,其作用主要是告诉 Kotlin 的编译器,不要为被标识的对象生成 Getter/Setter 方法,并且将其设置成 public 的。

还有一点需要说明的是写入数据的顺序必须要和读取数据的顺序保持一致,这个有点像一个队列,比如写入的顺序为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

//写
parcel.writeString("1");
parcel.writeInt(1);
parcel.writeBoolean(true);

//读
parcel.readString();
parcel.readInt();
parcel.readBoolean();

//错误的读
parcel.readInt(); //直接报错
parcel.readString();
parcel.readBoolean();

下面我们来看看怎么在 Activity 之间传递对象,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

class MainActivity : AppCompatActivity() {

private val user = User(1, "张三")

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}

fun toSecondActivity(v: View) {
val intent = Intent(this,Main2Activity::class.java)
intent.putExtra("user", user)
startActivity(intent)
}

}


class SecondActivity : AppCompatActivity() {

lateinit var user: User

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main2)
user = intent.getParcelableExtra("user")
print(user.name)
}

}

上面就是在 Activity 之间传递对象的代码,很简单但是其被的工作原理是怎么样的呢?

Binder

想要知道背后的原理,需要从 Android 的 Binder 机制入手,因为在 Android 系统中无论是 startActivity() 还是 startService() 都是需要借助 Binder 机制来实现的,Intent 说到底还是 Java 中的一个类而已,最终都需要通过 Binder 来进行传递,首先我们来看一个简单的例子,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

//Book.java 实现了 Parcelable 接口

//Book.aidl
package com.lz.demo1;

parcelable Book;

//IBookManager.aidl
package com.lz.demo1;

import com.lz.demo1.Book;

interface IBookManager {

//添加一本书
void addBook(in Book book);

}

如上,Android 为了让我们方便的使用 Binder 提供了 AIDL 机制,简单来说 AIDL 就是一种对 Binder 实现的抽象,将 Binder 中的重复代码自动生成,开发者只需要提供少量的 AIDL 文件即可。

生成的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98

package com.lz.demo1;

public interface IBookManager extends android.os.IInterface {

public static abstract class Stub extends android.os.Binder implements com.lz.demo1.IBookManager {
private static final java.lang.String DESCRIPTOR = "com.lz.demo1.IBookManager"

public Stub(){ this.attachInterface(this,DESCRIPTOR) }

public static com.lz.demo1.IBookManager asInterface(android.os.IBinder obj) {
if((obj == null)){
return null;
}

android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
//不是跨进程调用
if(((iin != null) && (iin instanceof com.lz.demo1.IBookManager))){
return ((com.lz.demo1.IBookManager) iin);
}
//是跨进程调用
return new com.lz.demo1.IBookManager.Stub.Proxy(obj)
}

@Override
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
java.lang.String descriptor = DESCRIPTOR;
switch (code) {
case INTERFACE_TRANSACTION: {
reply.writeString(descriptor);
return true;
}
case TRANSACTION_addBook: {
data.enforceInterface(descriptor);
com.lz.demo1.Book _arg0;
if ((0 != data.readInt())) {
//重点在这行
_arg0 = com.lz.demo1.Book.CREATOR.createFromParcel(data);
} else {
_arg0 = null;
}
this.addBook(_arg0);
reply.writeNoException();
return true;
}
default: {
return super.onTransact(code, data, reply, flags);
}
}
}

//省略部分代码...

private static class Proxy implements com.lz.demo1.IBookManager {

static final int TRANSACTION_addBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);

private android.os.IBinder mRemote;

public Proxy(android.os.IBinder remote) { mRemote = remote }

@Override
public android.os.IBinder asBinder() {
return mRemote;
}

public java.lang.String getInterfaceDescriptor() {
return DESCRIPTOR;
}

@Override
public void addBook(com.lz..demo1.Book book) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
if ((book != null)) {
_data.writeInt(1);
//重点在这行
book.writeToParcel(_data, 0)
} else {
_data.writeInt(0);
}
mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply, 0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}

}

}

public void addBook(com.lz.demo1.Book book) throws android.os.RemoteException;

}

上面的代码都是 IDE 帮我们自动生成的,可以看到在内部类 Stub 中的 onTransact() 方法中,会先调用 Book.CREATOR.createFromParcel() 方法,因为这个方法是 static 的,所以不需要实例化对象也能调用。

我们前面说过如果像让一个对象在 Activitiy 之间传递,就必须要实现 Parcelable 接口,原因就在上面的代码中,在一般的情况下我们调用自己写的 Activity 都不会涉及到跨进程所以上面的 asInterface() 都会直接返回接口的实现类,但是如果我们调用系统的 Activity,比如启动个相机,打开浏览器等等都是涉及到跨进程,这个时候调用方法就会进入代理类 Proxy 里面。

Proxy 既然是个代理类,那么就有它代理的原因,原因是因为我们如果是跨进程调用,那么所有的数据都需要吸写入到 Parcel 中,这一过程是发生在真正调用之前,并且在真正调用之后我们还需处理异常额释放资源,很明显这两个操作都是重复发生的,这个时候就需要使用设计模式中的代理模式。

在 Porxy 的 addBook() 方法中,调用了 Book 对象的 writeToParcel() 方法,将 Book 对象写入到 Parcel 中,然后在调用 mRemote.transact(),在这个方法内部会在调用上面 Stub 类的 onTransact() 方法。

自此一个简单的 Binder 机制就分析完了,但是这个 Intent 有什么关系呢?不急,慢慢往下看。

ActivityManagerNative

在 Android 7.1.1 之前(API level <= 25),在源码中 ActivityManagerNative.java 的实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80

//android.app.ActivityManagerNative.java
public abstract class ActivityManagerNative extends Binder implements IActivityManager {

//省略部分代码...
@Override
public boolean onTransact(int code, Parcel data, Parcel reply, int flags){
switch(code) {
case START_ACTIVITY_TRANSACTION:
{
data.enforceInterface(IActivityManager.descriptor);
IBinder b = data.readStrongBinder();
IApplicationThread app = ApplicationThreadNative.asInterface(b);
String callingPackage = data.readString();
//重点在这
Intent intent = Intent.CREATOR.createFromParcel(data);
String resolvedType = data.readString();
IBinder resultTo = data.readStrongBinder();
String resultWho = data.readString();
int requestCode = data.readInt();
int startFlags = data.readInt();
ProfilerInfo profilerInfo = data.readInt() != 0
? ProfilerInfo.CREATOR.createFromParcel(data) : null;
Bundle options = data.readInt() != 0
? Bundle.CREATOR.createFromParcel(data) : null;
int result = startActivity(app, callingPackage, intent, resolvedType,
resultTo, resultWho, requestCode, startFlags, profilerInfo, options);
reply.writeNoException();
reply.writeInt(result);
return true;
}
}
//省略部分代码...
}

//省略部分代码...

class ActivityManagerProxy implements IActivityManager {

public ActivityManagerProxy(IBinder remote)
{
mRemote = remote;
}

public int startActivity(IApplicationThread caller, String callingPackage, Intent intent,
String resolvedType, IBinder resultTo, String resultWho, int requestCode,
int startFlags, ProfilerInfo profilerInfo, Bundle options) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
data.writeStrongBinder(caller != null ? caller.asBinder() : null);
data.writeString(callingPackage);
//重点在这
intent.writeToParcel(data, 0);
data.writeString(resolvedType);
data.writeStrongBinder(resultTo);
data.writeString(resultWho);
data.writeInt(requestCode);
data.writeInt(startFlags);
if (profilerInfo != null) {
data.writeInt(1);
profilerInfo.writeToParcel(data, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
} else {
data.writeInt(0);
}
if (options != null) {
data.writeInt(1);
options.writeToParcel(data, 0);
} else {
data.writeInt(0);
}
mRemote.transact(START_ACTIVITY_TRANSACTION, data, reply, 0);
reply.readException();
int result = reply.readInt();
reply.recycle();
data.recycle();
return result;
}
}
}

看看上面的源码,和我们之前的 IDE 通过 AIDL 帮我们生成的代码几乎一样,唯一不同的就是源码比我们复杂很多,虽然代码多了不少,但是和 Intent 有关的也就两行,分别是调用 Intent.CREATOR 的 createFromParcel() 方法和调用 Intent 的 writeToParcel() 方法。

然而在 Android 7.1.1 之后(API level > 25)的源码中 ActivityManagerNative.java 被表示为 Deprecated,其内部也就只有几个简单的方法,源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

//android.app.ActivityManagerNative.java
@Deprecated
public abstract class ActivityManagerNative {
/**
* Cast a Binder object into an activity manager interface, generating
* a proxy if needed.
*
* @deprecated use IActivityManager.Stub.asInterface instead.
*/
static public IActivityManager asInterface(IBinder obj) {
return IActivityManager.Stub.asInterface(obj);
}

/**
* Retrieve the system's default/global activity manager.
*
* @deprecated use ActivityManager.getService instead.
*/
static public IActivityManager getDefault() {
return ActivityManager.getService();
}

//省略部分代码

}

可以看到在 Android 7.1.1 之后, ActivityManagerNative.java 变成了一个简简单单的工具了,原因其实很简单,在 Android 7.1.1 之前的 ActivityManagerNative.java 中的代码基本上是没有意义的,完全可以使用 AIDL 让 IDE 来生成出来,可能在 Android 初期生成出来的代码达不到预期,或者说需要在生成的代码上进行修改和添加,所以 Android 就打算手搓这段代码,随着版本的演变,Android 的源码也在不但的优化,最终在 Android 7.1.1 之后就可以完全依赖 AIDL 来生成而不需要在手搓这段代码了(纯属我个人猜想)。

write and read

我们在回到 Intent 的源码中,分别是 Intent.CREATOR.createFromParcel() 方法和 Intent 的 readFromParcel() 方法,源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

//Intent.java
public static final Parcelable.Creator<Intent> CREATOR = new Parcelable.Creator<Intent>() {
public Intent createFromParcel(Parcel in) { return new Intent(in) }
public Intent[] newArray(int size) { return new Intent[size] }
}

/** @hide */
protected Intent(Parcel in) {
readFromParcel(in);
}

public void readFromParcel(Parcel in) {
//省略部分代码...
mExtras = in.readBundle();
}

从上面源码我们可以看出,Intent 和我们上面实现 Parcel 接口的了一样,提供了一个名为 CREATOR 的静态对象,该对象有两个方法,分别是 createFromParcel() 和 newArray(),在 createFromParcel() 中调用了 Intent(Parcel in) 这个构造函数,紧接着在构造函数中有调用了 Parcel 的 readBundle() 方法。

这个时候就来到了 Parcel 的 readBundle() 方法,源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

//Parcel.java
public final Bundle readBundle(ClassLoader loader) {
int length = readInt();
if (length < 0) {
if (Bundle.DEBUG) Log.d(TAG, "null bundle: length=" + length);
return null;
}

final Bundle bundle = new Bundle(this, length);
if (loader != null) {
bundle.setClassLoader(loader);
}
return bundle;
}

在 Parcel 的 readBundle() 方法中,直接 new 了一个 Bundle 对象并返回,这里需要注意的是,传入了一个类型为 ClassLoader 的对象,其主要用途是用来实例化类的。紧接着又来到了 Bundle 的构造函数,这里既然提到了 Bundle 就有必要说说它了。

顾名思义,Bundle 是用来存储数据用的,我们通过 Intent 传递的数据,无论是基本类型还是 Parcel 类型的数据,都是存储在 Bundle 中的,并且这些数据都是以 K-V 的方式存储的,Key 是 String 类型,Bundle 继承自 BaseBundle,其核心代码都是 BaseBundle 中,源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62

//Bundle.java
public Bundle(Parcel parcelledData, int length) {
super(parcelledData, length);
//省略部分代码...
}

//BaseBundle.java
ArrayMap<String, Object> mMap = null;

Parcel mParcelledData = null;

BaseBundle(Parcel parcelledData, int length) {
readFromParcelInner(parcelledData, length);
}

private void readFromParcelInner(Parcel parcel, int length) {
if (length < 0) {
throw new RuntimeException("Bad length in parcel: " + length);

} else if (length == 0) {
//没有数据 直接 return
mParcelledData = NoImagePreloadHolder.EMPTY_PARCEL;
mParcelledByNative = false;
return;
}

final int magic = parcel.readInt();
final boolean isJavaBundle = magic == BUNDLE_MAGIC;
final boolean isNativeBundle = magic == BUNDLE_MAGIC_NATIVE;
if (!isJavaBundle && !isNativeBundle) {
throw new IllegalStateException("Bad magic number for Bundle: 0x"
+ Integer.toHexString(magic));
}

if (parcel.hasReadWriteHelper()) {
// If the parcel has a read-write helper, then we can't lazily-unparcel it, so just
// unparcel right away.
//大致意思为,如果 hasRedWriteHelper() 为 true,就不会使用懒加载,而是立即加载数据
synchronized (this) {
initializeFromParcelLocked(parcel, /*recycleParcel=*/ false, isNativeBundle);
}
return;
}

// Advance within this Parcel
int offset = parcel.dataPosition();
parcel.setDataPosition(MathUtils.addOrThrow(offset, length));

//创建一个新的 Parcel 对象
Parcel p = Parcel.obtain();
p.setDataPosition(0);
//将 parcel 中的数据添加到 p 中
p.appendFrom(parcel, offset, length);
p.adoptClassCookies(parcel);
if (DEBUG) Log.d(TAG, "Retrieving " + Integer.toHexString(System.identityHashCode(this))
+ ": " + length + " bundle bytes starting at " + offset);
p.setDataPosition(0);

mParcelledData = p;
mParcelledByNative = isNativeBundle;
}

从上面的代码我们可以得知,BaseBundle 在默认情况下采用的是懒加载,当 Bundle 对象刚创建的时候 mMap 中是没有数据的,数据都在 mParcelledData 对象中。当我们尝试获取数据的时候,就会调用 BaseBundle 的 unparcle() 方法,源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68

//BaseBundle.java

//所有的 putXXX() 和 getXXX() 方法都会调用 unparcel() 方法
public Object get(String key) {
unparcel();
return mMap.get(key);
}

void unparcel() {
synchronized (this) {
final Parcel source = mParcelledData;
if (source != null) {
initializeFromParcelLocked(source, /*recycleParcel=*/ true, mParcelledByNative);
}
}
}

private void initializeFromParcelLocked(@NonNull Parcel parcelledData, boolean recycleParcel,
boolean parcelledByNative) {

//判断 parcelledData 有没有数据
if (isEmptyParcel(parcelledData)) {
if (mMap == null) {
mMap = new ArrayMap<>(1);
} else {
mMap.erase();
}
mParcelledData = null;
mParcelledByNative = false;
return;
}

final int count = parcelledData.readInt();

if (count < 0) {
return;
}
ArrayMap<String, Object> map = mMap;
if (map == null) {
map = new ArrayMap<>(count);
} else {
map.erase();
map.ensureCapacity(count);
}
try {
if (parcelledByNative) {
parcelledData.readArrayMapSafelyInternal(map, count, mClassLoader);
} else {
//一般执行这里
parcelledData.readArrayMapInternal(map, count, mClassLoader);
}
} catch (BadParcelableException e) {
if (sShouldDefuse) {
Log.w(TAG, "Failed to parse Bundle, but defusing quietly", e);
map.erase();
} else {
throw e;
}
} finally {
mMap = map;
if (recycleParcel) {
recycleParcel(parcelledData);
}
mParcelledData = null;
mParcelledByNative = false;
}
}

在 BaseBundle 中的所有 getXXX() 和 putXXX() 方法中,都会调用 unparcel(),这个方法的作用有点像拆包,我们可以把 Parcel 理解为它将数据都包在了一起,而这个 unparcel 就是把包在一起的数据拆开。

拆的具体构成在 Parcel 的 readArrayMapInternal() 方法中,在看这个方法之前我们先看看 initializeFromParcelLocked() 的最后,它将 mParcelledData 设置成了 null,这个时候 ParcelledData 这个包里面的数据就被转换成了 mMap,所以后面在调用 unparcel() 就不会在拆了。

接着进入到 Parcel 的 readArrayMapInternal() 方法,源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13

//Parcel.java
void readArrayMapInternal(ArrayMap outVal, int N,
ClassLoader loader) {
while (N > 0) {
if (DEBUG_ARRAY_MAP) startPos = dataPosition();
String key = readString();
Object value = readValue(loader);
outVal.append(key, value);
N--;
}
outVal.validate();
}

可以看到,在 Parcel 的 readArraymapInternal() 方法中,循环读取 Key 和 Value 并添加 outVal 中,具体读取值的方法是 native 的这里我们不深入研究,这里有一个比较有趣的地方,就是上面调用的是 ArrayMap 的 append() 方法而不是 put(),而在方法的最后调用了 ArrayMap 的 validate() 方法,很显然这里的 append() + validate() 的方式肯定要比 put() 效率高,可惜这两个方法是 @hide 的,开发者无法调用,估计是 Google 程序员为了偷懒写的,但是又不想让开发者用XD。

到这里,Intent.CREATE.createFromParcel() 就分析完了,下一个轮到 Intent 的 writeToParcel(),这个方法的主要作用就是将 Intent 的数据写入到 Parcel 对象中,源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55

//Intent.java
public void writeToParcel(Parcel out, int flags) {
//省略部分代码
out.writeBundle(mExtras);
}

//Parcel.java
public final void writeBundle(Bundle val) {
if (val == null) {
writeInt(-1);
return;
}

val.writeToParcel(this, 0);
}

//Bundle.java
public void writeToParcel(Parcel parcel, int flags) {
final boolean oldAllowFds = parcel.pushAllowFds((mFlags & FLAG_ALLOW_FDS) != 0);
try {
super.writeToParcelInner(parcel, flags);
} finally {
parcel.restoreAllowFds(oldAllowFds);
}
}

//BaseBundle.java
void writeToParcelInner(Parcel parcel, int flags) {
//省了部分代码...
int startPos = parcel.dataPosition();
//重点在这
parcel.writeArrayMapInternal(map);
int endPos = parcel.dataPosition();

// Backpatch length
parcel.setDataPosition(lengthPos);
int length = endPos - startPos;
parcel.writeInt(length);
parcel.setDataPosition(endPos);
}

//Parcel.java
void writeArrayMapInternal(ArrayMap<String, Object> val) {
if (val == null) {
writeInt(-1);
return;
}
final int N = val.size();
writeInt(N);
for (int i=0; i<N; i++) {
writeString(val.keyAt(i));
writeValue(val.valueAt(i));
}
}

如上,就是 Intent 的 writeToParcel() 的流程,总体上来看和 Intent.CREATOR 的 createFromParcel() 的流程差不太多。自此,Intent 的数据写入和读取的流程就分析完了。

总结

总结一下,如果想让一个类在 Activity 之间传递,那么一定要实现 Parcelable 接口,实现方式分为两个步骤,第一步是需要实现 Parcelable 接口的方法,第二步是在类中声明一个公共的静态对象,对象名称必须叫做 CREATOR,对象需要实现 Parcelable.Creator 接口。

在使用 Parcel 读取数据的时候,读取的顺序要和存储的顺序保持一致,从这一点我们可以大概的猜出 Parcel 的底层机制就是将 Java 中的数据按照顺序写入到一个值里面,然后在接收的时候在按照顺序从这个值中读出数据。

AIDL 文件本质其实就是告诉 IDE 帮我们生成生成什么样的 Binder 实现类,就算没有 AIDL 我们完全可以自己写一个 Binder 的实现类。

Bundle 的本质其实就是一个 ArrayMap,只不过前者可以跨进程使用。