Android知识点总结(四)进程间通信
进程间通讯方式
Android进程通讯方式有很多,如Socket、ContentProvider、共享文件(这种方式的缺点是不支持并发写,同时需要手动操作IO)、AIDL、Messenger(底层实现也是AIDL)等。
Messenger实现
Messenger是一种轻量级的IPC方案,可以在不同进程中传递Message对象。不适用与并发服务场景。下面是其常用方法源码
/**
* 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.
*/
public Messenger(Handler target) {
mTarget = target.getIMessenger();
}
/**
* Create a Messenger from a raw IBinder, which had previously been
* retrieved with {@link #getBinder}.
*/
public Messenger(IBinder target) {
mTarget = IMessenger.Stub.asInterface(target);
}
/**
* Send a Message to this Messenger's Handler.
*/
public void send(Message message) throws RemoteException {
mTarget.send(message);
}
/**
* Retrieve the IBinder that this Messenger is using to communicate with
* its associated Handler.
*/
public IBinder getBinder() {
return mTarget.asBinder();
}
它的实现要比直接使用AIDL简单的多:
1. 服务端进程
首先,在服务端创建一个Service,同时创建一个Handler并通过它来创建一个Messenger对象,然后在Service的onBind中返回Messenger底层的Binder即可。
2. 客户端进程
客户端进程,首先绑定服务端的Service,然后利用服务端返回的IBinder的代理对象创建一个Messenger,通过这个Messenger对象就可以向服务端发送消息了。消息类型为Message对象,跨进程发送要使用Bundle传送,否则可能会崩溃:Can't marshal non-Parcelable objects across processes.
服务端代码如下:
public class MyService extends Service {
private Handler handler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
if (msg != null) {
switch (msg.what) {
case MSG:
Log.d("from customer: ", msg.getData().getString("msg"));
break;
default:
break;
}
}
return false;
}
});
private Messenger mMessenger = new Messenger(handler);
public MyService() {
}
@Override
public IBinder onBind(Intent intent) {
return mMessenger.getBinder();
}
}
客户端代码如下
public class MainActivity extends AppCompatActivity {
private ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Messenger mMessenger = new Messenger(service);
Message obtain = Message.obtain(null, MyUtils.MSG);
Bundle bundle = new Bundle();
bundle.putString("msg", "hello, i am customer");
obtain.setData(bundle);
try {
mMessenger.send(obtain);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.tv1).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(this, MyService.class);
bindService(intent, serviceConnection,Context.BIND_AUTO_CREATE);
}
});
}
@Override
protected void onDestroy() {
super.onDestroy();
unbindService(serviceConnection);
}
}
另外,注意给远程Service设置进程:
<service
android:name=".service.MyService"
android:enabled="true"
android:exported="true"
android:process=":scy"
/>
最后查看服务端日志的时候,注意在AS的Logcat上切换到服务进程查看:
04-01 16:08:08.244 16677-16677/com.scy.component.arithmetictest:scy D/
from customer:: hello, i am customer
到这里实现了单向的Client -> Service的跨进程通信,服务端要想实现回复,只需稍作修改就行:
首先,在客户端要再创建一个Messenger和Handler对象,用于接收消息,在发送消息前将这个Messenger对象赋值给Message的replyTo属性即可。
然后,在服务端的Handler里取出消息后,紧接着用Message的replyTo属性获取到客户端的Messenger对象并用其发送消息即可。
如果要查看日志,记得切换进程。
Message有多种创建方式,建议使用Message.obain系列进行创建,相对于直接new Message()的创建方式可以有效避免对象的重复创建
Message的obj字段在Android2.2版本以后支持实现Parcelable接口的对象进行跨进程传递,但是不支持非系统内置即自定义的Parcelable对象。所以跨进程通信尽量使用Bundle方式传递
Messenger是以串行方式处理消息的,即便大量的消息同时发送到服务端,服务端仍然只能一个个处理,所以Messenger不适用于并发请求,对于高并发场景可以使用AIDL来实现跨进程通信
Messenger的作用主要是为了传递消息,如果需要跨进程调用服务端的方法,Messenger就无法做到了,AIDL可以做到