上一篇文章我们讲解了怎样通过AIDL实现Service更新UI的功能,今天要讲的是另外一种方式:Messenger。它可以通过与Handler配合实现不同进程之间的通信,可以跨进程使用Handler发送消息。它的使用不像直接使用AIDL那么复杂,它只需要实现一个Handler对象来处理消息,其他的和使用普通的Service差不多。两个进程间可以通过Messenger来发送Message进行通信,在服务端使用Handler创建一个Messenger,客户端持有这个Messenger就可以与服务端通信了。
我们之前使用Handler和Message都是在同一个进程,子线程持有一个主线程的Handler对象,并向主线程发送消息。Android可以使用Binder机制进行跨进程通信,所以我们当然可以将Handler与Binder结合起来进行跨进程发送消息,Messenger就是基于这个原理。
使用方法:
1、创建一个Handler对象,以它为参数再创建一个Messenger对象
mMessenger = new Messenger(mHandler)
2、客户端使用bindService绑定远程服务
3、远程服务的onBind()方法返回一个Binder对象
return mMessenger.getBinder();
4、客户端使用远程返回的Binder对象得到Messenger对象
public void onServiceConnected(ComponentName name, IBinder service) {
rMessenger = new Messenger(service);
}
5、客户端使用这个Messenger对象向远程发送消息
rMessenger.send(msg);
这样远程服务端的Handler对象就能收到消息,然后在其handleMessage(Message msg)方法中处理消息。
我们已经实现了消息的单向传递,即从客户端向服务端的传递,那么如何实现双向传递呢?
在第5步中,在send(msg)前通过msm.replyTo = mMessenger将自己的Messenger对象设置到消息中,这样服务端接收到消息时同时也得到了客户端的Messenger对象了,然后服务端可以通过客户端的Messenger对象向它发送消息。通过Messenger实现的双向通信就完成了。
下面来看全部的实现代码,首先是Service端:
public class MessengerService extends Service {
private Messenger activityMessenger;
private MessengerHandler messengerHandler;
private int count = 0;
private static volatile boolean isRunning;
public MessengerService() {
messengerHandler = new MessengerHandler();
}
@Override
public IBinder onBind(Intent intent) {
return new Messenger(messengerHandler).getBinder();
}
@Override
public boolean onUnbind(Intent intent) {
isRunning = false;
return super.onUnbind(intent);
}
private class MessengerHandler extends Handler {
@Override
public void handleMessage(Message msg) {
if (msg.replyTo != null) {
activityMessenger = msg.replyTo;
notifyActivity();
}
super.handleMessage(msg);
}
}
private void notifyActivity(){
isRunning = true;
new Thread(new Runnable() {
@Override
public void run() {
while(isRunning) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
count++;
Message message = Message.obtain();
message.arg1 = count;
try {
activityMessenger.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
}).start();
}
}
然后是Activity端:
public class ServiceUpdateActivity extends Activity implements View.OnClickListener {
private Button btn, btnStop;
private TextSwitcher switcher;
private int mCount = 0;
Intent intent;
Messenger mServiceMessenger;
Messenger mActivityMessenger;
private boolean isMessengerServiceConnected = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_service_update_main);
btn = (Button) findViewById(R.id.btn);
btnStop = (Button) findViewById(R.id.method_stop);
btn.setOnClickListener(this);
btnStop.setOnClickListener(this);
switcher = (TextSwitcher) findViewById(R.id.number_switcher);
switcher.setFactory(new ViewSwitcher.ViewFactory() {
@Override
public View makeView() {
TextView textView = new TextView(ServiceUpdateActivity.this);
textView.setGravity(Gravity.CENTER);
textView.setTextSize(30);
return textView;
}
});
switcher.setInAnimation(AnimationUtils.loadAnimation(this, R.anim.switcher_text_in));
switcher.setOutAnimation(AnimationUtils.loadAnimation(this, R.anim.switcher_text_out));
updateCount();
mActivityMessenger = new Messenger(mMessengerHandler);
intent = new Intent(this, MessengerService.class);
}
@Override
protected void onDestroy() {
stopAll();
super.onDestroy();
}
private void startMessengerMethod() {
bindService(intent, messengerServiceConnection, Service.BIND_AUTO_CREATE);
}
/**
* 刷新数字
*/
private void updateCount() {
//由于从binder调用回来是在子线程里,需要post到主线程调用
new Handler(Looper.getMainLooper()).post(new Runnable() {
@Override
public void run() {
switcher.setText(Integer.toString(mCount));
}
});
}
private void stopAll() {
if (isMessengerServiceConnected) {
unbindService(messengerServiceConnection);
isMessengerServiceConnected = false;
}
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn:
startMessengerMethod();
break;
case R.id.method_stop:
stopAll();
break;
}
}
//messenger使用
private ServiceConnection messengerServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
isMessengerServiceConnected = true;
mServiceMessenger = new Messenger(service);
Message message = Message.obtain();
message.replyTo = mActivityMessenger;
try {
mServiceMessenger.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
private Handler mMessengerHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
mCount = msg.arg1;
updateCount();
super.handleMessage(msg);
}
};
}
上面的代码实现了Activity和Service的双向通信,也就实现了Service更新Activity的UI的功能。可能有的同学会觉得奇怪,Messenger是怎么做到跨进程发送消息的。
首先来看一个问题 ,在Service中的onBind()方法中,返回了一个Messenger对象,
@Override
public IBinder onBind(Intent intent) {
return new Messenger(messengerHandler).getBinder();
}
而在Activity中的onServiceConnected方法中,却new了一个新的Messenger对象,那就奇怪了,两个不同的对象怎么做到通信的?
<span style="white-space:pre"> </span>@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mServiceMessenger = new Messenger(service);
Message message = Message.obtain();
message.replyTo = mActivityMessenger;
try {
mServiceMessenger.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
}
我们看一下Messenger的构造函数,还是首先看看Service端,
public Messenger(Handler target) {
mTarget = target.getIMessenger();
}
其中mTarget是一个IMessenger的aidl接口,getIMessenger()做了什么呢
final IMessenger getIMessenger() {
synchronized (mQueue) {
if (mMessenger != null) {
return mMessenger;
}
mMessenger = new MessengerImpl();
return mMessenger;
}
}
private final class MessengerImpl extends IMessenger.Stub {
public void send(Message msg) {
msg.sendingUid = Binder.getCallingUid();
Handler.this.sendMessage(msg);
}
}
可以看到getIMessenger()返回了一个IMessenger类型的Binder对象,Messenger本身也是通过AIDL实现的。而且我们在调用send(msg)方法的时候,其实是调用了与这个Binder关联的Handler的sendMessage()方法,继而会触发这个Handler的handleMessage()的调用。
通过上面的分析,大概能猜出来,Activity端就是拿到这个IMessenger类型的Binder的代理对象,通过代理对象的send()方法来发送消息到Service中。
public Messenger(IBinder target) {
mTarget = IMessenger.Stub.asInterface(target);
}
至此,关于跨进程的Service通知Activity更新UI的分析就讲完了,可以发现无论是哪种实现,最终都是通过AIDL的方式,只是做了不同的封装和处理罢了。也说明实现一个问题的方式有很多,但也许表面上看起来不同的实现,底层的原理都是相似的。
欢迎关注我的公众号一起交流学习