IPC(Inter-Process-Communication)即进程间通讯,小型的开发项目或许很少会用到这些知识,但当项目很大,或某些模块由于特殊的原因需求的时候,就会采取多个进程来共同管理。
Part.1
要开启一个APP的多进程十分简单,只需要在AndroidManiFests里面对所需要的部件配置android:process = “true”属性即可。但开启多进程也会给我们带来许多问题
- 静态与单例模式完全失效。
- 线程同步机制完全失效。
- SharePreferences完全失效
- Application会多次创建
第一个问题出现的原因是因为Android会为每个独立的进程分配一个独立的虚拟机,而我们的静态对象只是相对于自己的虚拟机共享而不是两个虚拟机之间,所以当然会失效。
第二个问题的原因与第一个问题类似,因为对处于不同的虚拟机内存不同,故锁对象也就失效了。
第三个则是因为SharePreference不支持多进程读写,其底层是通过读/写XML实现的,故并发写有可能出现问题。
第四个问题的原因是新的进程拥有新的虚拟机,故APP肯定必须重启一遍,那么Application必然会被重新创建。
虽然多进程存在很多问题,但是这种方式也给我们提供了好处,我们的APP的生命周期也是通过与系统服务ActivityManagerService的IPC完成的。
Part.2
若要使用IPC,我们很大程度上就要利用到Intent与Binder这两样工具,而我们所需要传输的数据却未必能满足这两个工具的要求——如一个对象需要被传递。
因此我们就需要使用另外的工具将对象持久化,Android中为我们提供了Serializable 与 Parcelable接口协助我们完成此项工作。
2.1.Serializable 接口
该接口是一个空接口,使用该接口十分简单,只需要让所需要的model接入该接口即可完成对象的序列化,当然为了我们的工作更好的完成,我们还需要完成一点其他的事情:
1
private static final long serialVersionUID = 8711368828010083044L;
定义一个类似上面的标识即可,该标识用于辅助系统的反序化。在序列化的时候,系统会将上述UID写入文件中,当进行反序列化的时候会对上述UID进行校验,若一致就证明序列化的类与当前类的版本一致,否则无法序列化。
如果我们不手动定义一个UID的话,系统在每次反序列化的时候都会生成一个UID,若我们反序列化之前曾对该类进行过修改(如增加或者删除一些变量),那么序列化就会失败。但是如果我们定义了UID,反序列化校验可以通过,即便我们只是稍微的变动了一下类的结构,那么系统还是能够最大限度的帮我们还原对象。
PS.当类名发生改变的时候,尽管通过了UID的校验但也无法反序列化
通过writeObject()方法将接入了Serializable的对象写出
通过readObject()方法将接入了Serializable的对象写入
2.2.Parcelable 接口
该接口是Android特有的一个序列化接口,被广泛的应用在IPC中,其原因是因为Serializable接口的序列/反序列化会进行大量的I/O操作,而Parcelable接口则是通过对内存的读写对对象进行序列化,而内存读写的效率显然要比硬盘的读写效率高得多,因此在Android传输序列化对象的时候我们推荐使用Parcelable接口,而做数据持久化的时候就应该使用Serializable接口。
使用方法:
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
public class User implements Parcelable {
String name;
int age;
boolean isMale;
Book book;
public User(){}
public User(Parcel in){
name = in.readString();
age = in.readInt();
isMale = in.readInt() == 1;
book = in.readParcelable(Thread.currentThread().getContextClassLoader());
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(name);
dest.writeInt(age);
dest.writeInt(isMale? 1 : 0);
dest.writeParcelable(book,0);
}
public static final Creator<User> CREATOR = new Creator<User>() {
@Override
public User createFromParcel(Parcel source) {
return new User(source);
}
@Override
public User[] newArray(int size) {
return new User[size];
}
};
}
- 要使用该接口,必须覆写writeToParcel ( Parcel , flag )方法,其格式如上所示
- describeContents()方法的返回值通常不需要修改。
- Book是另一个接入了Parcelable接口的对象,因而需要用Parcel.readPareclable(ClassLoader)方法读取,注意ClassLoader一定是当前线程的上下文类加载器。
- 要创建一个Parcel.Creator类的对象负责回调。
尽管Parcel接口使用起来十分复杂,但其性能值得我们去完成这些工作,且这个完成的过程是一个套路。
Part.3 AIDL & Binder
3.1.AIDL
AIDL:Android Interface Definition Language,即Android接口定义语言。
Android系统中的进程之间不能共享内存,因此,需要提供一些机制在不同进程之间进行数据通信。
为了使其他的应用程序也可以访问本应用程序提供的服务,Android系统采用了远程过程调用(Remote Procedure Call,RPC)方式来实现。与很多其他的基于RPC的解决方案一样,Android使用一种接口定义语言(Interface Definition Language,IDL)来公开服务的接口。我们知道4个Android应用程序组件中的3个(Activity、BroadcastReceiver和ContentProvider)都可以进行跨进程访问,另外一个Android应用程序组件Service同样可以。因此,可以将这种可以跨进程访问的服务称为AIDL(Android Interface Definition Language)服务。
以上为百度百科所言
个人理解AIDL就是interface的一种,不过它比较特殊,主要用于服务端和客户端的方法的定义,以辅助IPC,AIDL的使用十分简单,但又十分复杂。简单是因为我们只需要建立一个AIDL的文件,用类似java的语法定义我们所需的方法,然后实现该接口即可,底层的实现编译器会替我们直接完成,而说它复杂是因为其实现的方式是十分复杂的,需要认真的去读才能够理解。
并不是所有的数据类型都能被应用在AIDL中,能够使用的如下:
- 基本数据类型(int long char boolean double等)
- String和CharSquecnce
- List:ArryList,且内部每个元素都要被AIDL支持
- Map:HashMap,且内部每个元素都要被AIDL支持
- Parcelable
- AIDL
另外AIDL中用到的Parcelable对象与AIDL对象必须显式的import进来,即便它们在同一个包下。还有就是所有用到的Parcelable对象必须新建一个同名的AIDL文件并在内将其生命为Parcelable类型。
3.2.Binder
本人对Binder的了解也没有十分深入,下面引用《Android 开发艺术探索》中的一段话
直观的说,Binder是Android中的一个类,他实现了IBinder接口。从IPC的角度来说,Binder是Android中一种跨进程的通讯方式,还可以理解成一种虚拟的物理设备,其驱动位于/dev/binder。该通讯方式在Linux中没有。从Android Framework的角度来说,Binder是ServiceManager连接各种Manager的桥梁;从Android应用层来说,Binder是客户端和服务端进行通信的媒介,当bindService的时候,服务端会返回一个包含了服务端业务调用的Binder对象,客户端可以通过它获取服务端提供的服务或者数据,这其中包括普通形式的和AIDL形式的。
实际上在我们使用AIDL的时候会生成对应的.Java文件,里面就包含了一个Binder类,系统正是通过这个Binder类来辅助服务端和客户端的交互。
Part.4 Android中IPC的方式
4.1.使用AIDL
使用AIDL十分简单,我们只需要按照规定的格式声明AIDL文件即可,但仍需要进行一些特殊的配置,接下来以一个基于AIDL的水果商店IPC来举例子。
在Android Studio中的java文件夹右键,在new中找到aidl文件新建即可,需要注意的是,我们需要在Gradle文件中添加以下代码
1
2
3
4
5
sourceSets{
main{
java.srcDirs = ['src/main/java','src/main/aidl']
}
}
然后rebuild project即可。在这里我推荐将aidl用到的model类与aidl文件打包在一起比较好,这样在大项目的开发的时候可以单独维护aidl文件,不同的开发人员可以同步更新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
//Fruit,接入Parcelable接口
public class Fruit implements Parcelable{
String name;
int price;
public Fruit(String name, int price) {
this.name = name;
this.price = price;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(this.name);
dest.writeInt(this.price);
}
protected Fruit(Parcel in) {
this.name = in.readString();
this.price = in.readInt();
}
public static final Creator<Fruit> CREATOR = new Creator<Fruit>() {
@Override
public Fruit createFromParcel(Parcel source) {
return new Fruit(source);
}
@Override
public Fruit[] newArray(int size) {
return new Fruit[size];
}
};
}
然后由于我们的水果类会被用于IPC的数据传输,所以我们要有对应的AIDL文件
1
2
3
4
5
// Fruit.aidl
package org.sin.fruit.aidl;
parcelable Fruit;
最后我们定一个水果商店的add和get的AIDL接口
1
2
3
4
5
6
7
8
9
// IFruitManager.aidl
package org.sin.fruit.aidl;
import org.sin.fruit.aidl.Fruit;
interface IFruitManager{
void addFruit(in Fruit fruit);
List<Fruit> getFruits();
}
至此,我们的AIDL部分已经完成了定义,并可以直接使用了。
现在我们新建一个FruitService类,它将运行在一个独立的进程中,这个服务将作为我们的商店客户端,它维护着总的水果商品list,我们的客户端通过Service返回的binder与它实施IPC通信。
1
2
3
<!--AndroidManiFest文件中FruitService的配置-->
<service android:name=".FruitService"
android:process=":Remote"/>
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
//FruitService
public class FruitService extends Service {
public static final String TAG = "FruitService";
private CopyOnWriteArrayList<Fruit> fruits = new CopyOnWriteArrayList<>();
private Binder mBinder = new IFruitManager.Stub(){
@Override
public void addFruit(Fruit fruit) throws RemoteException {
fruits.add(fruit);
}
@Override
public List<Fruit> getFruits() throws RemoteException {
return fruits;
}
};
@Nullable
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
@Override
public void onCreate() {
super.onCreate();
Fruit apple = new Fruit("apple",3);
Fruit banana = new Fruit("banana",4);
Fruit peach = new Fruit("peach",5);
fruits.add(apple);
fruits.add(banana);
fruits.add(peach);
}
}
上面的Fruit类十分简单,关键的代码在新建Binder的地方,这个地方新建了我们刚才定义的接口,然后实现了对fruits的操作。最后我们把这个新建的binder通过onBinder()方法返回给调用bind的组件。
另外需要注意的是,我们曾经说过AIDL的数据传输只支持ArryList,但我们却使用了CopyOnWriteArrayList,这是因为CopyWriteArrayList支持并发的读/写,ADIL的方法是在服务端的Binder线程池中执行的,因此当多个客户端同时连接的时候,就会发生多个进程同时读/写的情况,所以我们选用CopyWriteArrayList。
虽然AIDL只支持ArrayList,但由于ArrayList与CopyWriteArrayList都是接入了List接口,而AIDL实际上是通过List接口去判别的,在AIDL中对list的读写也是按照List接口的规范进行读写的,因而没有问题。
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
//MainActivity
public class MainActivity extends AppCompatActivity {
public static final String TAG = "MainActivity";
//新建ServiceConnection对象负责与Service通信与回调
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
//获取定义的AIDL接口,此处即是Service返回的binder
IFruitManager iFruitManager = IFruitManager.Stub.asInterface(service);
try{
List<Fruit> fruitList = iFruitManager.getFruits();
for (Fruit f:fruitList) {
Log.i(TAG, "onServiceConnected: " + f.getName());
}
iFruitManager.addFruit(new Fruit("wechat",6));
fruitList = iFruitManager.getFruits();
for (Fruit f:fruitList) {
Log.i(TAG, "onServiceConnected: " + f.getName());
}
}catch (RemoteException e){
Log.e(TAG, "onServiceConnected: ", e);
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button bn = (Button)findViewById(R.id.bn_start);
bn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(MainActivity.this,FruitService.class);
bindService(intent,mConnection, Context.BIND_AUTO_CREATE);
}
});
}
@Override
protected void onDestroy() {
unbindService(mConnection);
super.onDestroy();
}
}
如此,一个IPC通信就完成了,详细代码可以参照我的github-Android_Study_AIDL
接下来我会稍微提一下其他的IPC方式,但由于本文重点讨论AIDL方式,故不会详细写。
4.2.使用Bundle
Bundle在我们的日常的代码中使用的十分频繁,Activity、Service、BroadcastReceiver甚至是Fragment之间几乎都是使用Bundle进行数据的交换。在IPC的场景中,我们只需要使用Bundle里面添加它允许的数据将,然后在一个进程启动另一个进程时将其附带上即可。
它的缺点也十分明显,只能传输Bundle所支持的数据类型。
4.3.使用文件共享
既然是通信,那么我们只要找到一个可以记录信息的载体不就可以了,换个思维,文件共享就是一个很好的IPC载体,当我们进程间通讯并并发率不高的时候,我们完全可以通过对同一个文件的读写来交换所需要的信息。
缺点正如上文所说,当两个进程间的并发读写率十分高的时候,文件内容就很容易出错,严重的话会导致信息的丢失。
4.4.使用Messenger
Messenger实际上是一种轻量级的AIDL方式,其底层就是AIDL,不过系统对其进行了封装使得我们能够更方便的使用它。
使用它十分简单:
1.服务端进程
在服务端创建一个Service对象,并且在其内部创建一个Handler并通过它创建一个Messenger对象,然后通过onBind()方法将其底层的Binder返回给客户端即可。
2.客户端要首先绑定到服务端的Service,绑定成功后利用返回的IBinder创建一个Messenger,然后我们便可以新建Message对象通过其向服务端发送信息了。
3.若是客户端想要回复的话,如服务端一般新建一个Handler对象并通过其新建一个Messenger对象,并把这个Messenger对象通过Message的replyTo参数传递给服务端,服务端便可以通过这个replyTo参数回应客户端。
具体代码请参考github-Android_Study_Messenger
4.5.使用ContentProvider
ContentProvider被设计用于不同应用间进行数据共享方式,因而它天生就是可以用于IPC的。和Messenger一样,ContentProvider的底层也是AIDL实现的,但是系统为其进行了高度的封装,使得这种方式十分简单的得以被应用。
要使用ContentProvider必须实现六个抽象方法:onCreate()、query、insert、delete、update、getType方法。
由于CRUD方法都是存在多线程和并发读写的,所以要做好线程同步。
4.6.使用Socket
Socket传输也是一种十分好的办法,不过要注意的是不能在主线程访问网络,而且要申请权限。采用Socket通信,我们可以在服务端打开一个While循环不断地等待客户端发送的信息即可。
Final Part.AIDL的底层结构
在我们新建AIDL文件的时候,实际上编译器会帮助我们在底层实现一个对应名称的.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
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
/*
* This file is auto-generated. DO NOT MODIFY.
* Original file: D:\android\AIDL\app\src\main\aidl\org\sin\fruit\aidl\IFruitManager.aidl
*/
package org.sin.fruit.aidl;
public interface IFruitManager extends android.os.IInterface{
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements org.sin.fruit.aidl.IFruitManager
{
private static final java.lang.String DESCRIPTOR = "org.sin.fruit.aidl.IFruitManager";
/** Construct the stub at attach it to the interface. */
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an org.sin.fruit.aidl.IFruitManager interface,
* generating a proxy if needed.
*/
public static org.sin.fruit.aidl.IFruitManager asInterface(android.os.IBinder obj){
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof org.sin.fruit.aidl.IFruitManager))) {
return ((org.sin.fruit.aidl.IFruitManager)iin);
}
return new org.sin.fruit.aidl.IFruitManager.Stub.Proxy(obj);
}
@Override
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_addFruit:
{
data.enforceInterface(DESCRIPTOR);
org.sin.fruit.aidl.Fruit _arg0;
if ((0!=data.readInt())) {
_arg0 = org.sin.fruit.aidl.Fruit.CREATOR.createFromParcel(data);
}
else {
_arg0 = null;
}
this.addFruit(_arg0);
reply.writeNoException();
return true;
}
case TRANSACTION_getFruits:
{
data.enforceInterface(DESCRIPTOR);
java.util.List<org.sin.fruit.aidl.Fruit> _result = this.getFruits();
reply.writeNoException();
reply.writeTypedList(_result);
return true;
}
case TRANSACTION_registerListener:
{
data.enforceInterface(DESCRIPTOR);
org.sin.fruit.aidl.IOnNewFruitArrivedListener _arg0;
_arg0 = org.sin.fruit.aidl.IOnNewFruitArrivedListener.Stub.asInterface(data.readStrongBinder());
this.registerListener(_arg0);
reply.writeNoException();
return true;
}
case TRANSACTION_unRegisterListener:
{
data.enforceInterface(DESCRIPTOR);
org.sin.fruit.aidl.IOnNewFruitArrivedListener _arg0;
_arg0 = org.sin.fruit.aidl.IOnNewFruitArrivedListener.Stub.asInterface(data.readStrongBinder());
this.unRegisterListener(_arg0);
reply.writeNoException();
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements org.sin.fruit.aidl.IFruitManager{
private android.os.IBinder mRemote;
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 addFruit(org.sin.fruit.aidl.Fruit fruit) 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 ((fruit!=null)) {
_data.writeInt(1);
fruit.writeToParcel(_data, 0);
}
else {
_data.writeInt(0);
}
mRemote.transact(Stub.TRANSACTION_addFruit, _data, _reply, 0);
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
@Override
public java.util.List<org.sin.fruit.aidl.Fruit> getFruits() throws android.os.RemoteException{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.util.List<org.sin.fruit.aidl.Fruit> _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getFruits, _data, _reply, 0);
_reply.readException();
_result = _reply.createTypedArrayList(org.sin.fruit.aidl.Fruit.CREATOR);
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override
public void registerListener(org.sin.fruit.aidl.IOnNewFruitArrivedListener listener) throws android.os.RemoteException{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeStrongBinder((((listener!=null))?(listener.asBinder()):(null)));
mRemote.transact(Stub.TRANSACTION_registerListener, _data, _reply, 0);
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
@Override
public void unRegisterListener(org.sin.fruit.aidl.IOnNewFruitArrivedListener listener) throws android.os.RemoteException{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeStrongBinder((((listener!=null))?(listener.asBinder()):(null)));
mRemote.transact(Stub.TRANSACTION_unRegisterListener, _data, _reply, 0);
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
}
static final int TRANSACTION_addFruit = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_getFruits = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
static final int TRANSACTION_registerListener = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
static final int TRANSACTION_unRegisterListener = (android.os.IBinder.FIRST_CALL_TRANSACTION + 3);
}
public void addFruit(org.sin.fruit.aidl.Fruit fruit) throws android.os.RemoteException;
public java.util.List<org.sin.fruit.aidl.Fruit> getFruits() throws android.os.RemoteException;
public void registerListener(org.sin.fruit.aidl.IOnNewFruitArrivedListener listener) throws android.os.RemoteException;
public void unRegisterListener(org.sin.fruit.aidl.IOnNewFruitArrivedListener listener) throws android.os.RemoteException;
}
该文件就是上一Part中IFruitManager中自动生成的一个.java文件,虽然很长,但是内容实际上十分清晰,我们一点一点分析。
首先,系统根据我们AIDL文件的定义,新建了一个类,类名就是我们定义的接口的名字IFruitManager,这个接口它继承了IInterface接口。
在这个接口的内部有两个东西,一个是我们定义的几个方法。另一个是一个静态抽象类Stub,它继承与Binder类并且接入了IFruitManager的接口。
同时,这个Stub类内部还有一个类私有的内部类Proxy,这个类也接入了IFruitManager接口。
到此为止,我们已经梳理清楚了整个AIDL的整个结构,接下来我们来分别看看每个方法都有什么用处。
1
private static final java.lang.String DESCRIPTOR = "org.sin.fruit.aidl.IFruitManager";
DESCRIPTOR属性是binder的唯一标识,一般以包名来命名
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an org.sin.fruit.aidl.IFruitManager interface,
* generating a proxy if needed.
*/
public static org.sin.fruit.aidl.IFruitManager asInterface(android.os.IBinder obj){
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof org.sin.fruit.aidl.IFruitManager))) {
return ((org.sin.fruit.aidl.IFruitManager)iin);
}
return new org.sin.fruit.aidl.IFruitManager.Stub.Proxy(obj);
}
从上述代码可以看出,Stub类实例化的时候会调用asInterface方法,而asInterface方法里面回去判别客户端和服务端是否在同一进程,如果是就直接调用IFruitManager,此时不存在IPC,若为IPC,则返回Stub的代理操作类Proxy。
下面我们来观察Proxy类
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
private static class Proxy implements org.sin.fruit.aidl.IFruitManager{
private android.os.IBinder mRemote;
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 addFruit(org.sin.fruit.aidl.Fruit fruit) 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 ((fruit!=null)) {
_data.writeInt(1);
fruit.writeToParcel(_data, 0);
}
else {
_data.writeInt(0);
}
mRemote.transact(Stub.TRANSACTION_addFruit, _data, _reply, 0);
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
@Override
public java.util.List<org.sin.fruit.aidl.Fruit> getFruits() throws android.os.RemoteException{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.util.List<org.sin.fruit.aidl.Fruit> _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getFruits, _data, _reply, 0);
_reply.readException();
_result = _reply.createTypedArrayList(org.sin.fruit.aidl.Fruit.CREATOR);
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override
public void registerListener(org.sin.fruit.aidl.IOnNewFruitArrivedListener listener) throws android.os.RemoteException{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeStrongBinder((((listener!=null))?(listener.asBinder()):(null)));
mRemote.transact(Stub.TRANSACTION_registerListener, _data, _reply, 0);
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
@Override
public void unRegisterListener(org.sin.fruit.aidl.IOnNewFruitArrivedListener listener) throws android.os.RemoteException{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeStrongBinder((((listener!=null))?(listener.asBinder()):(null)));
mRemote.transact(Stub.TRANSACTION_unRegisterListener, _data, _reply, 0);
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
}
static final int TRANSACTION_addFruit = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_getFruits = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
static final int TRANSACTION_registerListener = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
static final int TRANSACTION_unRegisterListener = (android.os.IBinder.FIRST_CALL_TRANSACTION + 3);
}
从构造上可以看出来,实际上Proxy类的作用也是通过Binder来进行我们所需要的操作,类内的方法就是我们在接口中定义的方法。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@Override
public void addFruit(org.sin.fruit.aidl.Fruit fruit) 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 ((fruit!=null)) {
_data.writeInt(1);
fruit.writeToParcel(_data, 0);
}
else {
_data.writeInt(0);
}
mRemote.transact(Stub.TRANSACTION_addFruit, _data, _reply, 0);
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
我们观察addFruit()方法,其中有_data变量与_reply变量,经过代码分析,_data变量的作用是将我们的Fruit对象序列化然后与 _reply对象一同提交给父类的transact(),在这时,我们的服务逻辑就从客户端跳转到了服务端。
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
@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_addFruit:
{
data.enforceInterface(DESCRIPTOR);
org.sin.fruit.aidl.Fruit _arg0;
if ((0!=data.readInt())) {
_arg0 = org.sin.fruit.aidl.Fruit.CREATOR.createFromParcel(data);
}
else {
_arg0 = null;
}
this.addFruit(_arg0);
reply.writeNoException();
return true;
}
....
return super.onTransact(code, data, reply, flags);
}
onTransact方法是运行在服务端的Binder线程池中的,会根据mRemote传来的第一个值来判断应该调用那个方法,之前我们addFruit方法中传进来的参数是(Stub.TRANSACTION_addFruit, _data, _reply, 0),故会直接进入case Stub.TRANSACTION_addFruit中,分析代码可以发现,该方法首先将我们序列化传过来的Fruit类对象反序列化,然后调用服务端的addFruit方法,此时这次IPC过程就完成了。
需要注意的是,由于addFruit方法并不需要返回值,因而我们传递进来的reply也没有用到,实际上观察getList方法在Transact方法的实现
1reply.writeTypedList(_result);
上述代码就是将本次运行的结果写回到_result中,即我们传递进来的_reply参数中,最后Proxy类中的方法再将这个值返回到我们的应用中。
其他的方法不再详细的解说,其过程如上述大部分相似。
还有两点需要注意,客户端向服务端请求调用远端接口方法的时候,客户端会将自己挂起,知道远端操作完毕后,客户端才会继续执行,因而如果一个方法十分耗时,我们不应该在UI线程中发起此次请求;其二,由于binder方法运行在Binder线程池中,因而这些方法必须同步执行,否则有可能导致数据的丢失或错误。
到这里我们可以发现,几乎每一种IPC的方法都用到了序列化和反序列化,因为只有这样,我们的数据才能够得以在不同的进程间传输。
以上就是如何通过AIDL和Binder进行IPC的方法。
感谢《Android 开发艺术探索》
最后
以上就是贪玩金毛最近收集整理的关于Android 中的IPC——AIDL&Binder的应用Part.1Part.2Part.3 AIDL & BinderPart.4 Android中IPC的方式Final Part.AIDL的底层结构的全部内容,更多相关Android内容请搜索靠谱客的其他文章。
发表评论 取消回复