20180503_android简单使用AIDL实现IPC进程间通信

学习下使用AIDL实现IPC进程间通信。

[TOC]

几个概念

No. Name Detail
1 AIDL Android Interface Definition Language,即Android接口定义语言。【百度】
2 IPC Inter-Process Communication,进程间通信【百度】
3 Binder android的一个类,是android用来实现IPC的方式

原理图

网上各种文章说的很详细了,我们简单理解其实就是两个进程间通过共享内存的方式来进行通信或者说数据交换。

比如下图,为了保证安全性独立性,进程1和进程2之间是独立的,进程1可以向共享内存写入数据,然后进程2就可以读取共享内存来或者数据了。android的实现方式就是Binder。

img

代码实现

服务端

aidl

android studio中新建工程,然后对工程右键,新建aidl文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// ILoginAidlInterface.aidl
package com.qefee.androidaidlserver;

// Declare any non-default types here with import statements

interface ILoginAidlInterface {
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/

void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
double aDouble, String aString)
;


/**
* 自定义注册接口
*/

String register(String name, String password);
/**
* 自定义登录接口
*/

String login(String name, String password);
}

然后编译工程(Make Project),这个时候会帮你生成一个ILoginAidlInterface

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
public interface ILoginAidlInterface extends android.os.IInterface
{

/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements com.qefee.androidaidlserver.ILoginAidlInterface
{

private static final java.lang.String DESCRIPTOR = "com.qefee.androidaidlserver.ILoginAidlInterface";
/** Construct the stub at attach it to the interface. */
public Stub()
{

this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.qefee.androidaidlserver.ILoginAidlInterface interface,
* generating a proxy if needed.
*/

public static com.qefee.androidaidlserver.ILoginAidlInterface asInterface(android.os.IBinder obj)
{

if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.qefee.androidaidlserver.ILoginAidlInterface))) {
return ((com.qefee.androidaidlserver.ILoginAidlInterface)iin);
}
return new com.qefee.androidaidlserver.ILoginAidlInterface.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_basicTypes:
{
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
long _arg1;
_arg1 = data.readLong();
boolean _arg2;
_arg2 = (0!=data.readInt());
float _arg3;
_arg3 = data.readFloat();
double _arg4;
_arg4 = data.readDouble();
java.lang.String _arg5;
_arg5 = data.readString();
this.basicTypes(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5);
reply.writeNoException();
return true;
}
case TRANSACTION_register:
{
data.enforceInterface(DESCRIPTOR);
java.lang.String _arg0;
_arg0 = data.readString();
java.lang.String _arg1;
_arg1 = data.readString();
java.lang.String _result = this.register(_arg0, _arg1);
reply.writeNoException();
reply.writeString(_result);
return true;
}
case TRANSACTION_login:
{
data.enforceInterface(DESCRIPTOR);
java.lang.String _arg0;
_arg0 = data.readString();
java.lang.String _arg1;
_arg1 = data.readString();
java.lang.String _result = this.login(_arg0, _arg1);
reply.writeNoException();
reply.writeString(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements com.qefee.androidaidlserver.ILoginAidlInterface
{

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;
}
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/

@Override public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) 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.writeInt(anInt);
_data.writeLong(aLong);
_data.writeInt(((aBoolean)?(1):(0)));
_data.writeFloat(aFloat);
_data.writeDouble(aDouble);
_data.writeString(aString);
mRemote.transact(Stub.TRANSACTION_basicTypes, _data, _reply, 0);
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
@Override public java.lang.String register(java.lang.String name, java.lang.String password) throws android.os.RemoteException
{

android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.lang.String _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeString(name);
_data.writeString(password);
mRemote.transact(Stub.TRANSACTION_register, _data, _reply, 0);
_reply.readException();
_result = _reply.readString();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override public java.lang.String login(java.lang.String name, java.lang.String password) throws android.os.RemoteException
{

android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.lang.String _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeString(name);
_data.writeString(password);
mRemote.transact(Stub.TRANSACTION_login, _data, _reply, 0);
_reply.readException();
_result = _reply.readString();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
static final int TRANSACTION_basicTypes = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_register = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
static final int TRANSACTION_login = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
}
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/

public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException;
public java.lang.String register(java.lang.String name, java.lang.String password) throws android.os.RemoteException;
public java.lang.String login(java.lang.String name, java.lang.String password) throws android.os.RemoteException;
}

其中有个内部类Stub,就是继承了Binder的,用来实现IPC。

然后我们实现一个service,绑定后返回Binder,不要忘记在manifest中注册服务

1
2
3
4
5
6
7
8
9
<service
android:name=".LoginService"
android:enabled="true"
android:exported="true">

<intent-filter>
<action android:name="com.qefee.androidaidlserver.LoginService" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</service>

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
public class LoginService extends Service {
// 略

@Override
public IBinder onBind(Intent intent) {
Log.i(TAG, "onBind: ");
return loginBinder;
}

private IBinder loginBinder = new ILoginAidlInterface.Stub() {
@Override
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {

}

@Override
public String register(String name, String password) throws RemoteException {
int code;
String msg;
if (TextUtils.isEmpty(name)) {
code = -1;
msg = "用户名为空";
} else if (TextUtils.isEmpty(password)) {
code = -2;
msg = "密码为空";
} else if (userMap.containsKey(name)) {
code = -3;
msg = "用户已经被注册";
} else {
code = 0;
msg = "注册成功";

userMap.put(name, password);
}
JSONObject obj = new JSONObject();
try {
obj.put("code", code);
obj.put("msg", msg);
} catch (JSONException e) {
throw new RemoteException("json化异常");
}
return obj.toString();
}

@Override
public String login(String name, String password) throws RemoteException {
int code;
String msg;

if (TextUtils.isEmpty(name)) {
code = -1;
msg = "用户名为空";
} else if (TextUtils.isEmpty(password)) {
code = -2;
msg = "密码为空";
} else if (!userMap.containsKey(name)) {
code = -3;
msg = "用户还未注册";
} else {

String pass = userMap.get(name);
if (TextUtils.equals(pass, password)) {
code = 0;
msg = "登录成功";
} else {
code = -4;
msg = "密码不正确";
}
}
JSONObject obj = new JSONObject();
try {
obj.put("code", code);
obj.put("msg", msg);
} catch (JSONException e) {
throw new RemoteException("json化异常");
}
return obj.toString();
}
};
}

客户端

aidl

android studio中新建工程,然后把服务端的aidl文件复制过来,重新编译工程(Make Project)

Activity

客户端剩下的就是绑定服务,然后进行通信了,直接看代码

UI上就几个控件

  1. 绑定服务按钮
  2. 解绑服务按钮
  3. 注册按钮
  4. 登录按钮
  5. name输入框
  6. password输入框
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
public class MainActivity extends AppCompatActivity {
/**
* log tag for MainActivity
*/

private static final String TAG = "MainActivity";

private ServiceConnection serviceConnection;
private ILoginAidlInterface loginAidlInterface;
private EditText et_name;
private EditText et_password;
private boolean bindService;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);

FloatingActionButton fab = findViewById(R.id.fab);
fab.setOnClickListener(this::onClickFab);

Button btn_bind = findViewById(R.id.btn_bind);
btn_bind.setOnClickListener(this::onClickBind);

Button btn_unbind = findViewById(R.id.btn_unbind);
btn_unbind.setOnClickListener(this::onClickUnBind);

Button btn_register = findViewById(R.id.btn_register);
btn_register.setOnClickListener(this::onClickRegister);

Button btn_login = findViewById(R.id.btn_login);
btn_login.setOnClickListener(this::onClickLogin);

et_name = findViewById(R.id.et_name);
et_password = findViewById(R.id.et_password);

serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.i(TAG, "onServiceConnected: ");
loginAidlInterface = ILoginAidlInterface.Stub.asInterface(service);
}

@Override
public void onServiceDisconnected(ComponentName name) {
Log.i(TAG, "onServiceDisconnected: ");
}
};
}

/**
* 点击了绑定服务按钮
*
* @param view view
*/

private void onClickBind(View view) {
Intent intent = new Intent();
intent.setPackage("com.qefee.androidaidlserver");
intent.setAction("com.qefee.androidaidlserver.LoginService");
intent.setComponent(new ComponentName("com.qefee.androidaidlserver", "com.qefee.androidaidlserver.LoginService"));
bindService = bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);

Log.i(TAG, String.format("onClickBind: bind = %b", bindService));
}

/**
* 点击了解绑服务按钮
*
* @param view view
*/

private void onClickUnBind(View view) {
Log.i(TAG, "onClickUnBind: ");
if (bindService) {
unbindService(serviceConnection);
}
}

private void onClickFab(View view) {
Log.i(TAG, "onClickFab: ");
}

/**
* 点击了注册按钮
*
* @param view view
*/

private void onClickRegister(View view) {
Log.i(TAG, "onClickRegister: ");
if (loginAidlInterface != null) {
String name = et_name.getText().toString().trim();
String password = et_password.getText().toString().trim();
try {
String json = loginAidlInterface.register(name, password);
Snackbar.make(view, json, Snackbar.LENGTH_SHORT).show();
} catch (RemoteException e) {
e.printStackTrace();
Snackbar.make(view, e.getMessage(), Snackbar.LENGTH_SHORT).show();
}
} else {
Snackbar.make(view, "还没有连接服务器", Snackbar.LENGTH_SHORT).show();
}
}

/**
* 点击了登录按钮
*
* @param view view
*/

private void onClickLogin(View view) {
Log.i(TAG, "onClickLogin: ");
if (loginAidlInterface != null) {
String name = et_name.getText().toString().trim();
String password = et_password.getText().toString().trim();
try {
String json = loginAidlInterface.login(name, password);
Snackbar.make(view, json, Snackbar.LENGTH_SHORT).show();
} catch (RemoteException e) {
e.printStackTrace();
Snackbar.make(view, e.getMessage(), Snackbar.LENGTH_SHORT).show();
}
} else {
Snackbar.make(view, "还没有连接服务器", Snackbar.LENGTH_SHORT).show();
}
}
}

代码中可以看到,绑定服务成功的话onServiceConnected,我们可以获取到IBinder,然后通过asInterface转换为ILoginAidlInterface,使用这个对象我们就可以进行远程调用方法了,比如我们在aidl中定义的register和login方法。

源代码

AndroidAIDLStudy

参考文章

Fork me on GitHub