Android 基于Message的进程间通讯 Messenger彻底解析

1、概述

说到Android进程间通讯,你们确定能想到的是编写aidl文件,而后经过aapt生成的类方便的完成服务端,以及客户端代码的编写。若是你对这个过程不熟悉,能够查看Android aidl Binder框架浅析html

固然今天要说的通讯方式确定不是经过编写aidl文件的方式,那么有请今天的主角:Messenger。ok,这是什么样的一个类呢?咱们看下注释java

This allows for the implementation of message-based communication across processesandroid

容许实现基于消息的进程间通讯的方式。服务器

那么,什么叫基于消息的进程间通讯方式呢?看个图理解下:app

能够看到,咱们能够在客户端发送一个Message给服务端,在服务端的handler中会接收到客户端的消息,而后进行对应的处理,处理完成后,再将结果等数据封装成Message,发送给客户端,客户端的handler中会接收处处理的结果。框架

这样的进程间通讯是否是很爽呢?dom

  • 基于Message,相信你们都很熟悉
  • 支持回调的方式,也就是服务端处理完成长任务能够和客户端交互
  • 不须要编写aidl文件

此外,还支持,记录客户端对象的Messenger,而后能够实现一对多的通讯;甚至做为一个转接处,任意两个进程都能经过服务端进行通讯,这个后面再说。ide

看到这,有没有一些的小激动,咱们能够不写aidl文件,方便的实现进程间通讯了,是否是又能够装一下了。哈,下面看个简单的例子。源码分析


2、通讯实例

这个例子,经过两个apk演示,一个apk是Server端,一个是Client端;布局

(1) Server端

代码

package com.imooc.messenger_server; import android.app.Service; import android.content.Intent; import android.os.Handler; import android.os.IBinder; import android.os.Message; import android.os.Messenger; import android.os.RemoteException; public class MessengerService extends Service { private static final int MSG_SUM = 0x110; //最好换成HandlerThread的形式 private Messenger mMessenger = new Messenger(new Handler() { @Override public void handleMessage(Message msgfromClient) { Message msgToClient = Message.obtain(msgfromClient);//返回给客户端的消息 switch (msgfromClient.what) { //msg 客户端传来的消息 case MSG_SUM: msgToClient.what = MSG_SUM; try { //模拟耗时 Thread.sleep(2000); msgToClient.arg2 = msgfromClient.arg1 + msgfromClient.arg2; msgfromClient.replyTo.send(msgToClient); } catch (InterruptedException e) { e.printStackTrace(); } catch (RemoteException e) { e.printStackTrace(); } break; } super.handleMessage(msgfromClient); } }); @Override public IBinder onBind(Intent intent) { return mMessenger.getBinder(); } } 
  • 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

服务端就一个Service,能够看到代码至关的简单,只须要去声明一个Messenger对象,而后onBind方法返回mMessenger.getBinder();

而后坐等客户端将消息发送到handleMessage想法,根据message.what去判断进行什么操做,而后作对应的操做,最终将结果经过 msgfromClient.replyTo.send(msgToClient);返回。

能够看到咱们这里主要是取出客户端传来的两个数字,而后求和返回,这里我有意添加了sleep(2000)模拟耗时,注意在实际使用过程当中,能够换成在独立开辟的线程中完成耗时操做,好比和HandlerThread结合使用。

注册文件

<service android:name=".MessengerService" android:enabled="true" android:exported="true"> <intent-filter> <action android:name="com.zhy.aidl.calc"></action> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </service>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

别忘了注册service,写完之后直接安装。

(二)客户端

Activity

package com.imooc.messenger_client; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.Message; import android.os.Messenger; import android.os.RemoteException; import android.support.v7.app.AppCompatActivity; import android.util.Log; import android.view.View; import android.widget.Button; import android.widget.LinearLayout; import android.widget.TextView; public class MainActivity extends AppCompatActivity { private static final String TAG = "MainActivity"; private static final int MSG_SUM = 0x110; private Button mBtnAdd; private LinearLayout mLyContainer; //显示链接状态 private TextView mTvState; private Messenger mService; private boolean isConn; private Messenger mMessenger = new Messenger(new Handler() { @Override public void handleMessage(Message msgFromServer) { switch (msgFromServer.what) { case MSG_SUM: TextView tv = (TextView) mLyContainer.findViewById(msgFromServer.arg1); tv.setText(tv.getText() + "=>" + msgFromServer.arg2); break; } super.handleMessage(msgFromServer); } }); private ServiceConnection mConn = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { mService = new Messenger(service); isConn = true; mTvState.setText("connected!"); } @Override public void onServiceDisconnected(ComponentName name) { mService = null; isConn = false; mTvState.setText("disconnected!"); } }; private int mA; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //开始绑定服务 bindServiceInvoked(); mTvState = (TextView) findViewById(R.id.id_tv_callback); mBtnAdd = (Button) findViewById(R.id.id_btn_add); mLyContainer = (LinearLayout) findViewById(R.id.id_ll_container); mBtnAdd.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { try { int a = mA++; int b = (int) (Math.random() * 100); //建立一个tv,添加到LinearLayout中 TextView tv = new TextView(MainActivity.this); tv.setText(a + " + " + b + " = caculating ..."); tv.setId(a); mLyContainer.addView(tv); Message msgFromClient = Message.obtain(null, MSG_SUM, a, b); msgFromClient.replyTo = mMessenger; if (isConn) { //往服务端发送消息 mService.send(msgFromClient); } } catch (RemoteException e) { e.printStackTrace(); } } }); } private void bindServiceInvoked() { Intent intent = new Intent(); intent.setAction("com.zhy.aidl.calc"); bindService(intent, mConn, Context.BIND_AUTO_CREATE); Log.e(TAG, "bindService invoked !"); } @Override protected void onDestroy() { super.onDestroy(); unbindService(mConn); } } 
  • 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

代码也不复杂,首先bindService,而后在onServiceConnected中拿到回调的service(IBinder)对象,经过service对象去构造一个mService =new Messenger(service);而后就可使用mService.send(msg)给服务端了。

咱们消息的发送在Btn.onclick里面:

Message msgFromClient = Message.obtain(null, MSG_SUM, a, b); msgFromClient.replyTo = mMessenger; if (isConn) { //往服务端发送消息 mService.send(msgFromClient); } 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

那么服务端会收到消息,处理完成会将结果返回,传到Client端的mMessenger中的Handler的handleMessage方法中。

布局文件

<LinearLayout android:id="@+id/id_ll_container" xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context=".MainActivity"> <TextView android:id="@+id/id_tv_callback" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Messenger Test!"/> <Button android:id="@+id/id_btn_add" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="add"/> </LinearLayout> 
  • 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

效果图

能够看到,咱们每点击一次按钮,就往服务器发送个消息,服务器拿到消息执行完成后,将结果返回。

整个通讯的代码看起来仍是至关的清爽的,那么你们有没有对其内部的原理有一丝的好奇呢?下面咱们就来看下其内部是如何实现的。

对了,源码分析前,这里插一句,你们经过代码能够看到服务端往客户端传递数据是经过msg.replyTo这个对象的。那么服务端彻底能够作到,使用一个List甚至Map去存储全部绑定的客户端的msg.replyTo对象,而后想给谁发消息均可以。甚至能够把A进程发来的消息,经过B进程的msg.replyTo发到B进程那里去。相关代码呢,能够参考官方的文档:service,注意下拉找:Remote Messenger Service Sample。


3、源码分析

其实Messenger的内部实现的,实际上也是依赖于aidl文件实现的。

(一)首先咱们看客户端向服务端通讯

服务端

服务端的onBind是这么写的:

public IBinder onBind(Intent intent) { return mMessenger.getBinder(); }
  • 1
  • 2
  • 3
  • 4

那么点进去:

public IBinder getBinder() { return mTarget.asBinder(); }
  • 1
  • 2
  • 3

能够看到返回的是mTarget.asBinder();

mTarget是哪来的呢?

别忘了咱们前面去构造mMessenger对象的代码:new Messenger(new Handler())

public Messenger(Handler target) { mTarget = target.getIMessenger(); }
  • 1
  • 2
  • 3

原来是Handler返回的,咱们继续跟进去

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);
        }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

mTarget是一个MessengerImpl对象,那么asBinder其实是返回this,也就是MessengerImpl对象;

这是个内部类,能够看到继承自IMessenger.Stub,而后实现了一个send方法,该方法就是将接收到的消息经过 Handler.this.sendMessage(msg);发送到handleMessage方法。

看到这,你们有没有想到什么,难道不以为extends IMessenger.Stub这种写法异常的熟悉么?

咱们传统写aidl文件,aapt给咱们生成什么,生成IXXX.Stub类,而后咱们服务端继承IXXX.Stub实现接口中的方法。

没错,其实这里内部其实也是依赖一个aidl生成的类,这个aidl位于:frameworks/base/core/java/android/os/IMessenger.aidl.

package android.os; import android.os.Message; /** @hide */ oneway interface IMessenger { void send(in Message msg); } 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

看到这,你应该明白了,Messenger并无什么神奇之处,实际上,就是依赖该aidl文件生成的类,继承了IMessenger.Stub类,实现了send方法,send方法中参数会经过客户端传递过来,最终发送给handler进行处理。这里不理解,请详细看下Android aidl Binder框架浅析

客户端

客户端首先经过onServiceConnected拿到sevice(Ibinder)对象,这里没什么特殊的,咱们平时的写法也是这样的,只不过咱们平时会这么写:

IMessenger.Stub.asInterface(service)拿到接口对象进行调用;

而,咱们的代码中是

mService = new Messenger(service);

跟进去,你会发现:

public Messenger(IBinder target) { mTarget = IMessenger.Stub.asInterface(target); }
  • 1
  • 2
  • 3

soga,和咱们平时的写法如出一辙!

到这里就能够明白,客户端与服务端通讯,实际上和咱们平时的写法没有任何区别,经过编写aidl文件,服务端onBind利用Stub编写接口实现返回;客户端利用回调获得的IBinder对象,使用IMessenger.Stub.asInterface(target)拿到接口实例进行调用(内部实现,参考Android aidl Binder框架浅析)。

(2)服务端与客户端通讯

那么,客户端与服务端通讯的确没什么特殊的地方,咱们彻底也能够编写个相似的aidl文件实现;那么服务端是如何与客户端通讯的呢?

还记得,客户端send方法发送的是一个Message,这个Message.replyTo指向的是一个mMessenger,咱们在Activity中初始化的。

那么将消息发送到服务端,确定是经过序列化与反序列化拿到Message对象,咱们看下Message的反序列化的代码:

# Message

private void readFromParcel(Parcel source) { what = source.readInt(); arg1 = source.readInt(); arg2 = source.readInt(); if (source.readInt() != 0) { obj = source.readParcelable(getClass().getClassLoader()); } when = source.readLong(); data = source.readBundle(); replyTo = Messenger.readMessengerOrNullFromParcel(source); sendingUid = source.readInt(); }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

主要看replyTo,调用的是Messenger.readMessengerOrNullFromParcel

public static Messenger readMessengerOrNullFromParcel(Parcel in) { IBinder b = in.readStrongBinder(); return b != null ? new Messenger(b) : null; } public static void writeMessengerOrNullToParcel(Messenger messenger, Parcel out) { out.writeStrongBinder(messenger != null ? messenger.mTarget.asBinder() : null); }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

经过上面的writeMessengerOrNullToParcel能够看到,它将客户端的messenger.mTarget.asBinder()对象进行了恢复,客户端的message.mTarget.asBinder()是什么?

客户端也是经过Handler建立的Messenger,因而asBinder返回的是:

public Messenger(Handler target) { mTarget = target.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); } } public IBinder getBinder() { return mTarget.asBinder(); } 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

那么asBinder,实际上就是MessengerImpl extends IMessenger.Stub中的asBinder了。

#IMessenger.Stub

@Override public android.os.IBinder asBinder() { return this; }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

那么其实返回的就是MessengerImpl对象本身。到这里能够看到message.mTarget.asBinder()其实返回的是客户端的MessengerImpl对象。

最终,发送给客户端的代码是这么写的:

msgfromClient.replyTo.send(msgToClient);

public void send(Message message) throws RemoteException { mTarget.send(message); } 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

这个mTarget实际上就是对客户端的MessengerImpl对象的封装,那么send(message)(屏蔽了transact/onTransact的细节),这个message最终确定传到客户端的handler的handleMessage方法中。

好了,到此咱们的源码分析就结束了~~

总结下:

  • 客户端与服务端通讯,利用的aidl文件,没什么特殊的
  • 服务端与客户端通讯,主要是在传输的消息上作了处理,让Messager.replyTo指向的客户端的Messenger,而Messenger又持有客户端的一个Binder对象(MessengerImpl)。服务端正是利用这个Binder对象作的与客户端的通讯。

能够考虑本身编写aidl文件,实现下服务端对客户端的回调。

相关文章
相关标签/搜索