借助AIDL实现IPC通信
一、代码实操---与远端进程的Service绑定
上面的代码都是在当前进程内跟Service通信,现在我们来实现一下,不同进程内Service如何绑定。
AIDL:Android Interface Definition Language,即Android接口定义语言。
Service跨进程传递数据需要借助aidl,主要步骤是这样的:
- 编写aidl文件,AS自动生成的java类实现IPC通信的代理
- 继承自己的aidl类,实现里面的方法
- 在onBind()中返回我们的实现类,暴露给外界
- 需要跟Service通信的对象通过bindService与Service绑定,并在ServiceConnection接收数据。
我们通过代码来实现一下:
1、首先我们需要新建一个Service
1
2
3
4
5
6
7
8
|
public class MyRemoteService extends Service { @Nullable @Override public IBinder onBind(Intent intent) { Log.e( "MyRemoteService" , "MyRemoteService thread id = " + Thread.currentThread().getId()); return null ; } } |
2、在manifest文件中声明我们的Service同时指定运行的进程名,这里并是不只能写remote进程名,你想要进程名都可以
1
2
3
|
< service android:name = ".service.MyRemoteService" android:process = ":remote" /> |
3、新建一个aidl文件用户进程间传递数据。
AIDL支持的类型:八大基本数据类型、String类型、CharSequence、List、Map、自定义类型。List、Map、自定义类型放到下文讲解。
里面会有一个默认的实现方法,删除即可,这里我们新建的文件如下:
1
2
3
4
5
6
|
package xxxx; //aidl所在的包名 //interface之前不能有修饰符 interface IProcessInfo { //你想要的通信用的方法都可以在这里添加 int getProcessId(); } |
4、实现我们的aidl类
1
2
3
4
5
6
|
public class IProcessInfoImpl extends IProcessInfo.Stub { @Override public int getProcessId() throws RemoteException { return android.os.Process.myPid(); } } |
5、在Service的onBind()中返回
1
2
3
4
5
6
7
8
9
|
public class MyRemoteService extends Service { IProcessInfoImpl mProcessInfo = new IProcessInfoImpl(); @Nullable @Override public IBinder onBind(Intent intent) { Log.e( "MyRemoteService" , "MyRemoteService thread id = " + Thread.currentThread().getId()); return mProcessInfo; } } |
6、绑定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
|
mTvRemoteBind.setOnClickListener( new View.OnClickListener() { @Override public void onClick(View v) { Intent intent = new Intent(MainActivity. this , MyRemoteService. class ); bindService(intent, mRemoteServiceConnection, BIND_AUTO_CREATE); } }); mRemoteServiceConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { Log.e( "MainActivity" , "MyRemoteService onServiceConnected" ); // 通过aidl取出数据 IProcessInfo processInfo = IProcessInfo.Stub.asInterface(service); try { Log.e( "MainActivity" , "MyRemoteService process id = " + processInfo.getProcessId()); } catch (RemoteException e) { e.printStackTrace(); } } @Override public void onServiceDisconnected(ComponentName name) { Log.e( "MainActivity" , "MyRemoteService onServiceDisconnected" ); } }; |
只要绑定成功就能在有log打印成MyRemoteService所在进程的进程id。这样我们就完成了跟不同进程的Service通信的过程。
二、代码实操---调用其他app的Service
跟调同app下不同进程下的Service相比,调用其他的app定义的Service有一些细微的差别
1、由于需要其他app访问,所以之前的bindService()使用的隐式调用不在合适,需要在Service定义时定义action
我们在定义的线程的App A 中定义如下Service:
1
2
3
4
5
6
7
|
< service android:name = ".service.ServerService" > < intent-filter > //这里的action自定义 < action android:name = "com.jxx.server.service.bind" /> < category android:name = "android.intent.category.DEFAULT" /> </ intent-filter > </ service > |
2、我们在需要bindService的App B 中需要做这些处理
- 首先要将A中定义的aidl文件复制到B中,比如我们在上面定义的IProcessInfo.aidl这个文件,包括路径在内需要原封不动的复制过来。
- 在B中调用Service通过显式调用
1
2
3
4
5
6
7
8
9
|
mTvServerBind.setOnClickListener( new View.OnClickListener() { @Override public void onClick(View v) { Intent intent = new Intent(); intent.setAction( "com.jxx.server.service.bind" ); //Service的action intent.setPackage( "com.jxx.server" ); //App A的包名 bindService(intent, mServerServiceConnection, BIND_AUTO_CREATE); } }); |
aidl中自定义对象的传递
主要步骤如下:
- 定义自定对象,需要实现Parcelable接口
- 新建自定义对象的aidl文件
- 在传递数据的aidl文件中引用自定义对象
- 将自定义对象以及aidl文件拷贝到需要bindService的app中,主要路径也要原封不动
我们来看一下具体的代码:
1、定义自定义对象,并实现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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
|
public class ServerInfo implements Parcelable { public ServerInfo() { } String mPackageName; public String getPackageName() { return mPackageName; } public void setPackageName(String packageName) { mPackageName = packageName; } protected ServerInfo(Parcel in) { mPackageName = in.readString(); } public static final Creator<ServerInfo> CREATOR = new Creator<ServerInfo>() { @Override public ServerInfo createFromParcel(Parcel in) { return new ServerInfo(in); } @Override public ServerInfo[] newArray( int size) { return new ServerInfo[size]; } }; @Override public int describeContents() { return 0 ; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeString(mPackageName); } //使用out或者inout修饰时需要自己添加这个方法 public void readFromParcel(Parcel dest) { mPackageName = dest.readString(); } } |
2、新建自定义对象的aidl文件
1
2
3
|
package com.jxx.server.aidl; //注意parcelable 是小写的 parcelable ServerInfo; |
3、引用自定义对象
1
2
3
4
5
6
7
|
package com.jxx.server.aidl; //就算在同一包下,这里也要导包 import com.jxx.server.aidl.ServerInfo; interface IServerServiceInfo { ServerInfo getServerInfo(); void setServerInfo(inout ServerInfo serverinfo); } |
注意这里的set方法,这里用了inout,一共有3种修饰符
- in:客户端写入,服务端的修改不会通知到客户端
- out:服务端修改同步到客户端,但是服务端获取到的对象可能为空
- inout:修改都收同步的
当使用out和inout时,除了要实现Parcelable外还要手动添加readFromParcel(Parcel dest)
4、拷贝自定义对象以及aidl文件到在要引用的App中即可。
5、引用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
mServerServiceConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { IServerServiceInfo serverServiceInfo = IServerServiceInfo.Stub.asInterface(service); try { ServerInfo serviceInfo = serverServiceInfo.getServerInfo(); Log.e( "MainActivity" , "ServerService packageName = " + serviceInfo.getPackageName()); } catch (RemoteException e) { e.printStackTrace(); } } @Override public void onServiceDisconnected(ComponentName name) { Log.e( "MainActivity" , "ServerService onServiceDisconnected" ); } }; |
List、Map中引用的对象也应该是符合上面要求的自定义对象,或者其他的几种数据类型。
使用Messenger实现IPC通信
步骤是这样的:
- 在Server端新建一个Messenger对象,用于响应Client端的注册操作,并在onBind()中传递出去
- 在Client端的ServiceConnection中,将Server端传递过来的Messenger对象进行保存
- 同时Client端也新建一个Messenger对象,通过Server传递过来的Messenger注册到Server端,保持通信用。
- 不管是否进行unbindService()操作,只要Client保有Server端的Messenger对象,仍然能和Server端进行通信。
一、Server端代码
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
|
public class MessengerService extends Service { static final int MSG_REGISTER_CLIENT = 1 ; static final int MSG_UNREGISTER_CLIENT = 2 ; static final int MSG_SET_VALUE = 3 ; //这个是给client端接收参数用的 static final int MSG_CLIENT_SET_VALUE = 4 ; static class ServiceHandler extends Handler { private final List<Messenger> mMessengerList = new ArrayList<>(); @Override public void handleMessage(Message msg) { switch (msg.what) { case MSG_REGISTER_CLIENT: mMessengerList.add(msg.replyTo); break ; case MSG_UNREGISTER_CLIENT: mMessengerList.remove(msg.replyTo); break ; case MSG_SET_VALUE: int value = msg.arg1; for (Messenger messenger : mMessengerList) { try { messenger.send(Message.obtain( null , MSG_CLIENT_SET_VALUE, value, 0 )); } catch (RemoteException e) { e.printStackTrace(); } } break ; default : super .handleMessage(msg); } } } private Messenger mMessenger = new Messenger( new ServiceHandler()); @Nullable @Override public IBinder onBind(Intent intent) { return mMessenger.getBinder(); } } |
二、Client端代码
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
|
public class MessengerClientActivity extends AppCompatActivity { //这些类型要和Server端想对应 static final int MSG_REGISTER_CLIENT = 1 ; static final int MSG_UNREGISTER_CLIENT = 2 ; static final int MSG_SET_VALUE = 3 ; static final int MSG_CLIENT_SET_VALUE = 4 ; class ClientHandler extends Handler { @Override public void handleMessage(Message msg) { if (msg.what == MSG_CLIENT_SET_VALUE) { mTvValue.setText(msg.arg1 + "" ); } else { super .handleMessage(msg); } } } TextView mTvServerBind; TextView mTvServerUnbind; TextView mTvValue; TextView mTvSend; ServiceConnection mServerServiceConnection; Messenger mServerMessenger; @Override protected void onCreate( @Nullable Bundle savedInstanceState) { super .onCreate(savedInstanceState); setContentView(R.layout.activity_messenger); mTvServerBind = findViewById(R.id.tv_server_bind); mTvServerUnbind = findViewById(R.id.tv_server_unbind); mTvValue = findViewById(R.id.tv_value); mTvSend = findViewById(R.id.tv_send); mTvServerBind.setOnClickListener( new View.OnClickListener() { @Override public void onClick(View v) { Intent intent = new Intent(); intent.setAction( "jxx.com.server.service.messenger" ); intent.setPackage( "jxx.com.server" ); bindService(intent, mServerServiceConnection, BIND_AUTO_CREATE); } }); mTvServerUnbind.setOnClickListener( new View.OnClickListener() { @Override public void onClick(View v) { //就算这里我们unbindService,只要我们还保留有mServerMessenger对象, //我们就能继续与Server通信 unbindService(mServerServiceConnection); } }); mTvSend.setOnClickListener( new View.OnClickListener() { @Override public void onClick(View v) { if (mServerMessenger != null ) { try { //测试一下能否设置数据 Message test = Message.obtain( null , MSG_SET_VALUE, new Random().nextInt( 100 ), 0 ); mServerMessenger.send(test); } catch (RemoteException e) { e.printStackTrace(); } } } }); mServerServiceConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { //服务端的messenger mServerMessenger = new Messenger(service); //现在开始构client用来传递和接收消息的messenger Messenger clientMessenger = new Messenger( new ClientHandler()); try { //将client注册到server端 Message register = Message.obtain( null , MSG_REGISTER_CLIENT); register.replyTo = clientMessenger; //这是注册的操作,我们可以在上面的Server代码看到这个对象被取出 mServerMessenger.send(register); Toast.makeText(MessengerClientActivity. this , "绑定成功" , Toast.LENGTH_SHORT).show(); } catch (RemoteException e) { e.printStackTrace(); } } @Override public void onServiceDisconnected(ComponentName name) { } }; } |
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持服务器之家。
原文链接:https://juejin.im/post/5c715d90e51d4520f01783f4