0%

Android-IPC机制

概述

IPC(IPC,InterProcess Communication):在Android系统中一个应用至少有一个进程,每个进程都有自己独立的资源和内存空间,其它进程不能任意访问当前进程的内存和资源。如果进程之间想要进行通信,则需要使用IPC手段。

每个Android的进程,只能运行在自己进程所拥有的虚拟地址空间。对于用户空间,不同进程之间彼此是不能共享的,而内核空间却是可共享的。Client进程向Server进程通信,恰恰是利用进程间可共享的内核内存空间来完成底层通信工作的,Client端与Server端进程往往采用ioctl等方法跟内核空间的驱动进行交互。

Parcelable

Android中实现序列化有两个选择:一是实现Serializable接口(是JavaSE本身就支持的),一是实现Parcelable接口(是Android特有功能,效率比实现Serializable接口高效,可用于Intent数据传递,也可以用于进程间通信(IPC))。实现Serializable接口非常简单,声明一下就可以了,而实现Parcelable接口稍微复杂一些,但效率更高,推荐用这种方法提高性能。

Android中Intent传递对象有两种方法:一是Bundle.putSerializable(Key,Object),另一种是Bundle.putParcelable(Key,Object)。当然这些Object是有一定的条件的,前者是实现了Serializable接口,而后者是实现了Parcelable接口。

  1. 在使用内存的时候,Parcelable比Serializable性能高,所以推荐使用Parcelable。
  2. Serializable在序列化的时候会产生大量的临时变量,从而引起频繁的GC。
  3. Parcelable不能使用在要将数据存储在磁盘上的情况,因为Parcelable不能很好的保证数据的持续性在外界有变化的情况下。尽管Serializable效率低点,但此时还是建议使用Serializable 。

步骤:

  1. implements Parcelable
  2. 重写writeToParcel方法,将你的对象序列化为一个Parcel对象,即:将类的数据写入外部提供的Parcel中,打包需要传递的数据到Parcel容器保存,以便从 Parcel容器获取数据
  3. 重写describeContents方法,内容接口描述,默认返回0就可以
  4. 实例化静态内部对象CREATOR实现接口Parcelable.Creator
1
public static final Parcelable.Creator<T> CREATOR

注:其中public static final一个都不能少,内部对象CREATOR的名称也不能改变,必须全部大写。需重写本接口中的两个方法:createFromParcel(Parcel in) 实现从Parcel容器中读取传递数据值,封装成Parcelable对象返回逻辑层,newArray(int size) 创建一个类型为T,长度为size的数组,仅一句话即可(return new T[size]),供外部类反序列化本类数组使用。

简而言之:通过writeToParcel将你的对象映射成Parcel对象,再通过createFromParcel将Parcel对象映射成你的对象。也可以将Parcel看成是一个流,通过writeToParcel把对象写到流里面,在通过createFromParcel从流里读取对象,只不过这个过程需要你来实现,因此写的顺序和读的顺序必须一致。

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
public class Person implements Serializable {
private static final long serialVersionUID = -7060210544600464481L;
private String name;
private int age;

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}
}

public class Book implements Parcelable {
private String bookName;
private String author;
private int publishDate;

public Book() {

}

public String getBookName() {
return bookName;
}

public void setBookName(String bookName) {
this.bookName = bookName;
}

public String getAuthor() {
return author;
}

public void setAuthor(String author) {
this.author = author;
}

public int getPublishDate() {
return publishDate;
}

public void setPublishDate(int publishDate) {
this.publishDate = publishDate;
}

@Override
public int describeContents() {
return 0;
}

@Override
public void writeToParcel(Parcel out, int flags) {
out.writeString(bookName);
out.writeString(author);
out.writeInt(publishDate);
}

public static final Parcelable.Creator<Book> CREATOR = new Creator<Book>() {
@Override
public Book[] newArray(int size) {
return new Book[size];
}

@Override
public Book createFromParcel(Parcel in) {
return new Book(in);
}
};

public Book(Parcel in) {
bookName = in.readString();
author = in.readString();
publishDate = in.readInt();
}
}

Bundle

Bundle 实现了 parcelable 接口。基于这一点,当启动另一个进程时,可使用 Bundle 传输能够被序列化的数据。如 Activity,BroadcastReceiver。

文件共享

文件共享适合在对数据同步要求不高的进程间进行通信。从本质上说,SharedPreference 也是文件,但由于系统对它的读写有一定的缓存策略,因此不建议在多进程间使用 SharedPreference。

ContentProvider

BroadcastReceiver

Socket

Binder

Android-Binder实战

Messenger

概述

Messenger 是一种轻量级的 IPC 方案,它的底层实现是 AIDL。 因为它对 AIDL 做了封装,所以使用起来非常简单,就类似于我们绑定一个 Service 一样。同时,由于它一次处理一个请求,所以在服务端我们不用考虑线程同步的问题,因为在服务端中不存在并发执行的情况。Messenger 和 Message 都实现了 Parcelable 接口,因此可以跨进程传输。简单来说, Message 中所支持的数据类型就是 Messenger 所支持的传输类型。

Messenger 比较适用于低并发的一对多的通信。

构造函数:

1
2
3
4
5
6
7
8
9
private final IMessenger mTarget;

public Messenger(Handler target) {
mTarget = target.getIMessenger();
}

public Messenger(IBinder target) {
mTarget = IMessenger.Stub.asInterface(target);
}

服务端进程:

  1. 创建一个 Service 来处理客户端的连接请求;
  2. 创建一个 Handler 并通过它来创建一个 Messenger 对象;
  3. 在 Service 的 onBind 函数中返回这个 Messenger 对象底层的 Binder ;

客户端进程:

  1. 绑定服务端的Service;
  2. 绑定成功后用服务端返回的 IBinder 对象创建一个 Messenger;
  3. 通过这个 Messenger 向服务端发送消息,消息类型为 Message 对象;
  4. 创建一个 Handler 并创建一个新的 Messenger 对象;
  5. 将这个新的 Messenger 对象通过 Message 的 replyTo 参数传递给服务端,服务端就可以通过这个 replyTo 参数回应客户端了;

实例

客户端:

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
public class MainActivity extends AppCompatActivity {

private Messenger messenger;

private static class MessengerHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 1:
Log.i("LLL", "client received: " + msg.getData().getString("reply"));
break;
}
}
}

private Messenger replyMessenger = new Messenger(new MessengerHandler());

private ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
Messenger messenger = new Messenger(iBinder);
//创建完成Messenger后就可以通过Messenger 来发送 Message 了
Message message = Message.obtain();
message.what = 0;
Bundle bundle = new Bundle();
bundle.putString("client", "this is message from client.");
message.setData(bundle);
message.replyTo = replyMessenger;

try {
messenger.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
}

@Override
public void onServiceDisconnected(ComponentName componentName) {

}
};

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

Intent intent = new Intent(this, MessengerService.class);
bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
}

@Override
protected void onDestroy() {
super.onDestroy();
unbindService(serviceConnection);
}
}

服务端:

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
public class MessengerService extends Service {

private static class MessengerHandle extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 0:
Log.i("LLL", "server received: " + msg.getData().getString("client"));

Messenger replyMessenger = msg.replyTo;
Message replyMessage = Message.obtain();
Bundle bundle = new Bundle();
bundle.putString("reply", "serve has received msg.");
replyMessage.what = 1;
replyMessage.setData(bundle);
try {
replyMessenger.send(replyMessage);
} catch (RemoteException e) {
e.printStackTrace();
}
break;
}
}
}

//此Messenger将客户端发送的消息传递给 MessengerHandler
private Messenger messenger = new Messenger(new MessengerHandle());

@Nullable
@Override
public IBinder onBind(Intent intent) {
return messenger.getBinder();
}
}

Service注册:

1
2
<service android:name=".MessengerService"
android:process=":remote"/>

源码

IMessenger.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
81
82
83
84
85
86
87
88
89
90
91
92
93
94
public interface IMessenger extends android.os.IInterface {
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements android.os.IMessenger {

private static final java.lang.String DESCRIPTOR = "android.os.IMessenger";

/** Construct the stub at attach it to the interface. */
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}

/**
* Cast an IBinder object into an android.os.IMessenger interface,
* generating a proxy if needed.
*/
public static android.os.IMessenger asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = (android.os.IInterface) obj
.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof android.os.IMessenger))) {
return ((android.os.IMessenger) iin);
}
return new android.os.IMessenger.Stub.Proxy(obj);
}

public android.os.IBinder asBinder() {
return this;
}

@Override
public boolean onTransact(int code, android.os.Parcel data,
android.os.Parcel reply, int flags)
throws android.os.RemoteException {
switch (code) {
case INTERFACE_TRANSACTION: {
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_send: { // 接收的code和发送的code都是TRANSACTION_send
data.enforceInterface(DESCRIPTOR);
android.os.Message _arg0;
if ((0 != data.readInt())) { // data.readInt为1(当msg不为空时)
_arg0 = android.os.Message.CREATOR.createFromParcel(data);
} else {
_arg0 = null;
}
this.send(_arg0);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}

private static class Proxy implements android.os.IMessenger {
private android.os.IBinder mRemote;

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

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

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

public void send(android.os.Message msg)
throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
if ((msg != null)) {
_data.writeInt(1);
msg.writeToParcel(_data, 0);// 将msg所有字段(what,obj)写到_data中去
} else {
_data.writeInt(0);
}
mRemote.transact(Stub.TRANSACTION_send, _data, null, // // 接收的code和发送的code都是TRANSACTION_send
android.os.IBinder.FLAG_ONEWAY);
} finally {
_data.recycle();
}
}
}

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

public void send(android.os.Message msg) throws android.os.RemoteException;
}

Messenger.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
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
public final class Messenger implements Parcelable {
private final IMessenger mTarget;

/**
* Create a new Messenger pointing to the given Handler. Any Message
* objects sent through this Messenger will appear in the Handler as if
* {@link Handler#sendMessage(Message) Handler.sendMessage(Message)} had
* been called directly.
*
* @param target The Handler that will receive sent messages.
*/
public Messenger(Handler target) {
mTarget = target.getIMessenger();
}

/**
* Send a Message to this Messenger's Handler.
*
* @param message The Message to send. Usually retrieved through
* {@link Message#obtain() Message.obtain()}.
*
* @throws RemoteException Throws DeadObjectException if the target
* Handler no longer exists.
*/
public void send(Message message) throws RemoteException {
mTarget.send(message);
}

/**
* Retrieve the IBinder that this Messenger is using to communicate with
* its associated Handler.
*
* @return Returns the IBinder backing this Messenger.
*/
public IBinder getBinder() {
return mTarget.asBinder();
}

/**
* Comparison operator on two Messenger objects, such that true
* is returned then they both point to the same Handler.
*/
public boolean equals(Object otherObj) {
if (otherObj == null) {
return false;
}
try {
return mTarget.asBinder().equals(((Messenger)otherObj)
.mTarget.asBinder());
} catch (ClassCastException e) {
}
return false;
}

public int hashCode() {
return mTarget.asBinder().hashCode();
}

public int describeContents() {
return 0;
}

public void writeToParcel(Parcel out, int flags) {
out.writeStrongBinder(mTarget.asBinder());
}

public static final Parcelable.Creator<Messenger> CREATOR
= new Parcelable.Creator<Messenger>() {
public Messenger createFromParcel(Parcel in) {
IBinder target = in.readStrongBinder();
return target != null ? new Messenger(target) : null;
}

public Messenger[] newArray(int size) {
return new Messenger[size];
}
};

/**
* Convenience function for writing either a Messenger or null pointer to
* a Parcel. You must use this with {@link #readMessengerOrNullFromParcel}
* for later reading it.
*
* @param messenger The Messenger to write, or null.
* @param out Where to write the Messenger.
*/
public static void writeMessengerOrNullToParcel(Messenger messenger,
Parcel out) {
out.writeStrongBinder(messenger != null ? messenger.mTarget.asBinder()
: null);
}

/**
* Convenience function for reading either a Messenger or null pointer from
* a Parcel. You must have previously written the Messenger with
* {@link #writeMessengerOrNullToParcel}.
*
* @param in The Parcel containing the written Messenger.
*
* @return Returns the Messenger read from the Parcel, or null if null had
* been written.
*/
public static Messenger readMessengerOrNullFromParcel(Parcel in) {
IBinder b = in.readStrongBinder();
return b != null ? new Messenger(b) : null;
}

/**
* Create a Messenger from a raw IBinder, which had previously been
* retrieved with {@link #getBinder}.
*
* @param target The IBinder this Messenger should communicate with.
*/
public Messenger(IBinder target) {
mTarget = IMessenger.Stub.asInterface(target);
}
}

ContentProvider&Binder

可以使用ContentProvider结合AIDL的方式实现两个进程之间的通信,这种方式不需要通过bindService来建立两个进程之间的ServiceConnection。

MatrixCursor

在介绍上面的方式之前,可以先看一下 MatrixCursor 这个 Cursor 对象。

在一些使用 Cursor 的场景里,如果想得到一个 Cursor 对象,但又没有数据库返回一个 Cursor,此时可以通过 MatrixCursor 来返回一个伪造的 Cursor。比如一个程序在一般情况下用 context.getContentResolver().query() 从 ContentProvider 中查询数据,但是在一些特殊的场景里,需要返回的只有几条固定的已知记录,不需要从数据库查询,此时可以使用 MatrixCursor 来根据这些已知的记录构造一个 Cursor。

MatrixCursor 的用法如下:

  1. 首先创建一个字符数组,字符数组的值对应着表的字段:

    1
    val COLUMN_NAME = arrayOf("_id", "name", "age")
  2. 利用MatrixCursor的构造方法,构造一个MatrixCursor,传入的参数即是步骤1中创建的字段数组:

    1
    matrixCursor = MatrixCursor(COLUMN_NAME)
  3. 通过matrixCursor的addRow方法添加一行值,相当于向数据库中插入一条记录:

    1
    2
    3
    matrixCursor?.addRow(arrayOf<Any>(1, "hearing", 24))
    // 也可以通过构造一个MatrixCursor.RowBuilder来实现
    matrixCursor?.newRow()?.add(2)?.add("hhh")?.add(22)

通过上面三步即可完成 MatrixCursor 的构造,从 MatrixCursor 中取出数据的过程与 Cursor 相同。

接下来通过一个实例来看看怎么具体地借助 ContentProvider 和 Binder 来进行便捷的跨进程通信。

Server进程

服务端接口定义

在服务端进程或者服务端 App 中,定义 AIDL 接口文件:

1
2
3
interface IMyAidlInterface {
int getCount();
}

make 之后实现接口:

1
2
3
4
5
6
class CountServiceImpl : IMyAidlInterface.Stub() {
override fun getCount(): Int {
println("Server: getCount")
return 100
}
}

ContentProvider定义

接着注册并定义一个运行在服务端进程的 ContentProvider 对象:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class BinderProvider : ContentProvider() {
override fun onCreate(): Boolean = true

override fun query(
uri: Uri, projection: Array<out String>?, selection: String?, selectionArgs: Array<out String>?, sortOrder: String?
): Cursor {
return BinderCursor.binderCursor
}

override fun getType(uri: Uri): String? = null

override fun insert(uri: Uri, values: ContentValues?): Uri? = null

override fun delete(uri: Uri, selection: String?, selectionArgs: Array<out String>?): Int = 0

override fun update(uri: Uri, values: ContentValues?, selection: String?, selectionArgs: Array<out String>?): Int = 0
}

这里 query 方法中返回的 Cursor 对象是一个 BinderCursor.binderCursor, BinderCursor 继承自 MatrixCursor 类,其内保存有服务端 Binder 对象:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class BinderCursor private constructor() : MatrixCursor(arrayOf("service")) {
companion object {
private const val KEY_BINDER_COUNT = "key_binder_count"
val binderCursor = BinderCursor()

fun getCountService(cursor: Cursor?): IBinder? {
return cursor?.extras?.getBinder(KEY_BINDER_COUNT)
}
}

private val mBinderExtra = Bundle()

init {
mBinderExtra.putBinder(KEY_BINDER_COUNT, CountServiceImpl())
}

override fun getExtras(): Bundle {
return mBinderExtra
}
}

然后在 Manifest 中注册:

1
2
3
4
5
6
<provider
android:name=".server.BinderProvider"
android:authorities="com.hearing.binder.demo"
android:exported="true"
android:grantUriPermissions="true"
android:process=":provider" />

Client进程

客户端进程通过以下方法可以获取到 Binder 代理对象,并远程调用服务端接口:

1
2
3
4
5
6
7
8
9
val cursor = contentResolver.query(Uri.parse("content://com.hearing.binder.demo"), null, null, null, null)
try {
val service = IMyAidlInterface.Stub.asInterface(BinderCursor.getCountService(cursor))
println("Client: ${service?.count}")
} catch (e: Exception) {
e.printStackTrace()
} finally {
cursor?.close()
}

总结

通过一个AIDL文件实现通用的跨进程接口调用