Android中AIDL的创建流程

摘要

虽然一直很想努力去记住AIDL的流程,但每次碰到这个问题依然要去网上搜索,特此记录自己对AIDL的理解,加深印象。

Android IPC简介

IPC是Inter-Process Communication的缩写,含义为进程间通信或者跨进程通信,是指两个进城之间进程数据交换的过程。AIDL属于进程间通信的一种方式。

Binder

介绍AIDL之前首先要了解Binder,以下摘自《Android开发艺术探索第二章IPC机制》

直观来说,Binder是Android中的一个类,它实现了IBinder接口;

从IPC角度来说,Binder是Android中的一种跨进程成通信方式,Binder还可以理解为一种虚拟的物理设备,它的设备驱动是/dev/binder,该通信方式在Linux中没有;

从Android Framework角度来说,Binder是ServiceManager连接各种Manager(ActivityManager、WindowManager等等)和相应ManagerService的桥梁;

从Android应用层来说,Binder是客户端和服务端进程通信的媒介,当bindService的时候,服务端会返回一个包含了服务端业务调用的Binder对象,通过这个Binder对象,客户端就可以获取服务器端提供的服务或数据,这里的服务包括普通服务和基于AIDL的服务。

用AIDL来分析Binder的工作机制

我们创建一个AIDL示例。

1.先建立一个android工程,用作服务端。

新建三个文件,Student.java、Student.aidl、IMyService.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
// Student.java
package com.example.xiongcen.aidlservice;
import android.os.Parcel;
import android.os.Parcelable;
import java.util.Locale;
public final class Student implements Parcelable {
public static final int SEX_MALE = 1;
public static final int SEX_FEMALE = 2;
public int sno;
public String name;
public int sex;
public int age;
public Student() {
}
public static final Parcelable.Creator<Student> CREATOR = new
Parcelable.Creator<Student>() {
public Student createFromParcel(Parcel in) {
return new Student(in);
}
public Student[] newArray(int size) {
return new Student[size];
}
};
private Student(Parcel in) {
readFromParcel(in);
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(sno);
dest.writeString(name);
dest.writeInt(sex);
dest.writeInt(age);
}
public void readFromParcel(Parcel in) {
sno = in.readInt();
name = in.readString();
sex = in.readInt();
age = in.readInt();
}
@Override
public String toString() {
return String.format(Locale.ENGLISH, "Student[ %d, %s, %d, %d ]", sno, name, sex, age);
}
}
// Student.aidl
package com.example.xiongcen.aidlservice;
parcelable Student;
// IMyService.aidl
package com.example.xiongcen.aidlservice;
import com.example.xiongcen.aidlservice.Student;
interface IMyService {
List<Student> getStudent();
void addStudent(in Student student);
}

服务端工程在IDE(AndroidStudio)中的结构如下,

说明:

aidl中支持的参数类型为:基本类型(int,long,char,boolean等),String,CharSequence,List,Map,其他类型必须使用import导入,即使它们可能在同一个包里,比如上面的Student,尽管它和IMyService在同一个包中,但是还是需要显示的import进来。

clean工程,我们可以看到系统为IMyService.aidl生产的Binder类,在/build/generated/source/aidl/debug/com.example.xiongcen.aidlservice.IMyService.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
package com.example.xiongcen.aidlservice;
// IMyService.java类继承了IInterface接口,同时自己也还是个接口,所有可以在Binder中传输的
// 接口都需要继承android.os.IInterface接口。
public interface IMyService extends android.os.IInterface {
/**
* Local-side IPC implementation stub class.
*/
public static abstract class Stub extends android.os.Binder implements com.example.xiongcen.aidlservice.IMyService {
// Binder的唯一标识
private static final java.lang.String DESCRIPTOR = "com.example.xiongcen.aidlservice.IMyService";
/**
* Construct the stub at attach it to the interface.
*/
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.example.xiongcen.aidlservice.IMyService interface,
* generating a proxy if needed.
*/
// 用于将服务端的Binder对象转换成客户端所需的AIDL接口类型的对象,
// 这种转换过程是区分进程的,如果客户端和服务端位于同一进程,
// 那么此方法返回的就是服务端的Stub对象本身,
// 否则返回的是由系统封装后的Stub.Proxy对象
public static com.example.xiongcen.aidlservice.IMyService asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.example.xiongcen.aidlservice.IMyService))) {
return ((com.example.xiongcen.aidlservice.IMyService) iin);
}
return new com.example.xiongcen.aidlservice.IMyService.Stub.Proxy(obj);
}
// 此方法用于返回当前Binder对象
@Override
public android.os.IBinder asBinder() {
return this;
}
// 该方法运行在服务端中的Binder线程池中。
// 当客户端发起跨进程请求时,远程请求会通过系统底层封装后交由此方法来处理。
// 服务端通过code可以确定客户端请求的目标方法是什么,
// 接着从data中取出目标方法所需的参数(如果目标方法有参数的话),
// 然后执行目标方法,
// 当目标方法执行完毕后,就向reply中写入返回值(如果目标方法有返回值的话)。
// 需要注意的是,如果该方法返回false,那么客户端的请求会失败。
// 因此可以利用这个特性来做权限校验。
@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_getStudent: {
data.enforceInterface(DESCRIPTOR);
java.util.List<com.example.xiongcen.aidlservice.Student> _result = this.getStudent();
reply.writeNoException();
reply.writeTypedList(_result);
return true;
}
case TRANSACTION_addStudent: {
data.enforceInterface(DESCRIPTOR);
com.example.xiongcen.aidlservice.Student _arg0;
if ((0 != data.readInt())) {
_arg0 = com.example.xiongcen.aidlservice.Student.CREATOR.createFromParcel(data);
} else {
_arg0 = null;
}
this.addStudent(_arg0);
reply.writeNoException();
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements com.example.xiongcen.aidlservice.IMyService {
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;
}
// 该方法运行在客户端。当客户端远程调用此方法时,
// 首先创建该方法所需要的输入型Parcel对象_data,
// 输出型Parcel对象_reply和返回值对象List,
// 然后把该方法的参数信息写入_data中(如果有参数的话),
// 接着调用transact方法来发起RPC(远程过程调用)请求,同时当前线程挂起,
// 然后服务端的onTransact方法会被调用,直到RPC过程返回后,当前线程继续执行,
// 并从_reply中取出RPC过程的返回结果,最后返回_reply中的数据。
@Override
public java.util.List<com.example.xiongcen.aidlservice.Student> getStudent() throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.util.List<com.example.xiongcen.aidlservice.Student> _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getStudent, _data, _reply, 0);
_reply.readException();
_result = _reply.createTypedArrayList(com.example.xiongcen.aidlservice.Student.CREATOR);
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
// 同上分析,只不过addStudent方法没有返回值,所以它不需要从_reply中取出返回值
@Override
public void addStudent(com.example.xiongcen.aidlservice.Student student) 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 ((student != null)) {
_data.writeInt(1);
student.writeToParcel(_data, 0);
} else {
_data.writeInt(0);
}
mRemote.transact(Stub.TRANSACTION_addStudent, _data, _reply, 0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
}
// IMyService.java类声明两个整型的id分别用于标识这两个方法,
// 这两个id用于标识在transact过程中客户端所请求的到底是哪个方法。
static final int TRANSACTION_getStudent = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_addStudent = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
}
// IMyService.java类声明两个方法getStudent()和addStudent(),
// 这显然就是我们在IMyService.aidl中所声明的方法。
public java.util.List<com.example.xiongcen.aidlservice.Student> getStudent() throws android.os.RemoteException;
public void addStudent(com.example.xiongcen.aidlservice.Student student) throws android.os.RemoteException;
}

通过注释分析,Binder的工作机制已经很清楚了。还需要说明一下的是:

  1. 当客户端发起远程请求时,由于当前线程会被挂起直至服务端进程返回数据,所以如果这个远程方法很耗时,则不能在UI线程中发起此远程请求;
  2. 由于服务端的Binder方法运行在Binder的线程池中,所以Binder方法不管是否耗时都应该采用同步的方法去实现,因为它已经运行在一个线程中了。

下面是一个Binder的工作机制图:

我们必须明白一点,AIDL文件并不是实现Binder的必需品,我们完全可以自己手写Binder,AIDL文件的本质是系统为我们提供了一种快速实现Binder的工具。

2.在服务端的android工程内创建服务端service

也就是目录结构中的MyService.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
package com.example.xiongcen.aidlservice;
import android.app.NotificationManager;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.Parcel;
import android.os.RemoteException;
import android.util.Log;
import java.util.ArrayList;
import java.util.List;
public class MyService extends Service {
private final static String TAG = "MyService";
private static final String PACKAGE_SAYHI = "com.example.xiongcen.aidlclient";
private NotificationManager mNotificationManager;
private boolean mCanRun = true;
private List<Student> mStudents = new ArrayList<Student>();
//这里实现了aidl中的抽象函数
private final IMyService.Stub mBinder = new IMyService.Stub() {
@Override
public List<Student> getStudent() throws RemoteException {
// 使用synchronized关键字的原因:由于服务端的Binder方法运行在Binder的线程池中,
// 所以Binder方法不管是否耗时都应该采用同步的方法去实现,因为它已经运行在一个线程中了。
synchronized (mStudents) {
Log.d(TAG, "MyService mBinder getStudent()");
return mStudents;
}
}
@Override
public void addStudent(Student student) throws RemoteException {
synchronized (mStudents) {
if (!mStudents.contains(student)) {
mStudents.add(student);
}
}
}
// 在这里可以做权限认证,return false意味着客户端的调用就会失败,比如下面,
// 只允许包名为com.example.xiongcen.aidlclient的客户端通过,其他apk将无法完成调用过程
public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
throws RemoteException {
String packageName = null;
String[] packages = MyService.this.getPackageManager().
getPackagesForUid(getCallingUid());
if (packages != null && packages.length > 0) {
packageName = packages[0];
}
Log.d(TAG, "onTransact: " + packageName);
if (!PACKAGE_SAYHI.equals(packageName)) {
return false;
}
return super.onTransact(code, data, reply, flags);
}
};
@Override
public void onCreate() {
Log.d(TAG, "onCreate..."+ android.os.Process.myPid());
Thread thr = new Thread(null, new ServiceWorker(), "BackgroundService");
thr.start();
synchronized (mStudents) {
for (int i = 1; i < 6; i++) {
Student student = new Student();
student.name = "student#" + i;
student.age = i * 5;
mStudents.add(student);
}
}
mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
super.onCreate();
}
@Override
public IBinder onBind(Intent intent) {
Log.d(TAG, String.format("on bind,intent = %s", intent.toString()));
return mBinder;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
mCanRun = false;
super.onDestroy();
}
class ServiceWorker implements Runnable {
long counter = 0;
@Override
public void run() {
// do background processing here.....
while (mCanRun) {
Log.d(TAG, "" + counter);
counter++;
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}

3.在服务端的android工程内的AndroidMenifest中声明service

1
2
3
4
5
6
7
8
<service
android:name="com.example.xiongcen.aidlservice.MyService"
android:process=":remote"
android:exported="true" >
<intent-filter>
<action android:name="com.example.xiongcen.aidlservice.MyService" />
</intent-filter>
</service>

4.建立一个android工程,用作客户端。

将服务端工程中的com.example.xiongcen.aidlservice包整个拷贝到客户端工程的src下,这个时候,客户端com.example.xiongcen.aidlservice包是和服务端工程完全一样的。如果客户端工程中不采用服务端的包名,客户端将无法正常工作。

客户端工程在IDE(AndroidStudio)中的结构如下,

客户端MainActivity绑定service代码如下:

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
package com.example.xiongcen.aidlclient;
import android.app.AlertDialog;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import com.example.xiongcen.aidlservice.IMyService;
import com.example.xiongcen.aidlservice.Student;
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
private static final String ACTION_BIND_SERVICE = "com.example.xiongcen.aidlservice.MyService";
private IMyService mIMyService;
private ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceDisconnected(ComponentName name) {
mIMyService = null;
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
//通过服务端onBind方法返回的binder对象得到IMyService的实例,得到实例就可以调用它的方法了
mIMyService = IMyService.Stub.asInterface(service);
try {
Student student = mIMyService.getStudent().get(0);
showDialog(student.toString());
} catch (RemoteException e) {
e.printStackTrace();
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button button1 = (Button) findViewById(R.id.btn_ok);
button1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (view.getId() == R.id.btn_ok) {
Intent intentService = new Intent(ACTION_BIND_SERVICE);
intentService.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
MainActivity.this.bindService(intentService, mServiceConnection, BIND_AUTO_CREATE);
}
}
});
Button button2 = (Button) findViewById(R.id.btn_cancel);
button2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (view.getId() == R.id.btn_cancel) {
MainActivity.this.unbindService(mServiceConnection);
}
}
});
Log.d(TAG, "onCreate..."+ android.os.Process.myPid());
}
public void showDialog(String message) {
new AlertDialog.Builder(MainActivity.this)
.setTitle("scott")
.setMessage(message)
.setPositiveButton("确定", null)
.show();
}
@Override
protected void onDestroy() {
if (mIMyService != null) {
unbindService(mServiceConnection);
}
super.onDestroy();
}
}

运行代码我们可以发现,点击ok按钮,弹出dialog,并且2s一次的计数正常打印,点击cancel按钮,服务停止,计数停止。

demo地址

服务端代码写了MainActivity其实完全没有必要,自己只是为了方便展示看到run起来了而已。

参考

  1. 《Android开发艺术探索》
  2. android跨进程通信(IPC):使用AIDL