zoukankan      html  css  js  c++  java
  • Android Messenger

    说到Android进程间通信,大家肯定能想到的是编写aidl文件,然后通过aapt生成的类方便的完成服务端,以及客户端代码的编写。如果你对这个过程不熟悉,可以查看Android aidl Binder框架浅析

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

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

    允许实现基于消息的进程间通信的方式。

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

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

    这样的进程间通信是不是很爽呢?

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

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

    看到这,有没有一些的小激动,我们可以不写aidl文件,方便的实现进程间通信了,是不是又可以装一下了。哈,下面看个简单的例子。


    二、通信实例

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

    (1) Server端

    代码

     1 package com.imooc.messenger_server;
     2 
     3 import android.app.Service;
     4 import android.content.Intent;
     5 import android.os.Handler;
     6 import android.os.IBinder;
     7 import android.os.Message;
     8 import android.os.Messenger;
     9 import android.os.RemoteException;
    10 
    11 public class MessengerService extends Service
    12 {
    13 
    14     private static final int MSG_SUM = 0x110;
    15 
    16     //最好换成HandlerThread的形式
    17     private Messenger mMessenger = new Messenger(new Handler()
    18     {
    19         @Override
    20         public void handleMessage(Message msgfromClient)
    21         {
    22             Message msgToClient = Message.obtain(msgfromClient);//返回给客户端的消息
    23             switch (msgfromClient.what)
    24             {
    25                 //msg 客户端传来的消息
    26                 case MSG_SUM:
    27                     msgToClient.what = MSG_SUM;
    28                     try
    29                     {
    30                         //模拟耗时
    31                         Thread.sleep(2000);
    32                         msgToClient.arg2 = msgfromClient.arg1 + msgfromClient.arg2;
    33                         msgfromClient.replyTo.send(msgToClient);
    34                     } catch (InterruptedException e)
    35                     {
    36                         e.printStackTrace();
    37                     } catch (RemoteException e)
    38                     {
    39                         e.printStackTrace();
    40                     }
    41                     break;
    42             }
    43 
    44             super.handleMessage(msgfromClient);
    45         }
    46     });
    47 
    48     @Override
    49     public IBinder onBind(Intent intent)
    50     {
    51         return mMessenger.getBinder();
    52     }
    53 }

    服务端就一个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>
    

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

    (二)客户端

    Activity

      1 package com.imooc.messenger_client;
      2 
      3 import android.content.ComponentName;
      4 import android.content.Context;
      5 import android.content.Intent;
      6 import android.content.ServiceConnection;
      7 import android.os.Bundle;
      8 import android.os.Handler;
      9 import android.os.IBinder;
     10 import android.os.Message;
     11 import android.os.Messenger;
     12 import android.os.RemoteException;
     13 import android.support.v7.app.AppCompatActivity;
     14 import android.util.Log;
     15 import android.view.View;
     16 import android.widget.Button;
     17 import android.widget.LinearLayout;
     18 import android.widget.TextView;
     19 
     20 public class MainActivity extends AppCompatActivity
     21 {
     22     private static final String TAG = "MainActivity";
     23     private static final int MSG_SUM = 0x110;
     24 
     25     private Button mBtnAdd;
     26     private LinearLayout mLyContainer;
     27     //显示连接状态
     28     private TextView mTvState;
     29 
     30     private Messenger mService;
     31     private boolean isConn;
     32 
     33 
     34     private Messenger mMessenger = new Messenger(new Handler()
     35     {
     36         @Override
     37         public void handleMessage(Message msgFromServer)
     38         {
     39             switch (msgFromServer.what)
     40             {
     41                 case MSG_SUM:
     42                     TextView tv = (TextView) mLyContainer.findViewById(msgFromServer.arg1);
     43                     tv.setText(tv.getText() + "=>" + msgFromServer.arg2);
     44                     break;
     45             }
     46             super.handleMessage(msgFromServer);
     47         }
     48     });
     49 
     50 
     51     private ServiceConnection mConn = new ServiceConnection()
     52     {
     53         @Override
     54         public void onServiceConnected(ComponentName name, IBinder service)
     55         {
     56             mService = new Messenger(service);
     57             isConn = true;
     58             mTvState.setText("connected!");
     59         }
     60 
     61         @Override
     62         public void onServiceDisconnected(ComponentName name)
     63         {
     64             mService = null;
     65             isConn = false;
     66             mTvState.setText("disconnected!");
     67         }
     68     };
     69 
     70     private int mA;
     71 
     72     @Override
     73     protected void onCreate(Bundle savedInstanceState)
     74     {
     75         super.onCreate(savedInstanceState);
     76         setContentView(R.layout.activity_main);
     77 
     78         //开始绑定服务
     79         bindServiceInvoked();
     80 
     81         mTvState = (TextView) findViewById(R.id.id_tv_callback);
     82         mBtnAdd = (Button) findViewById(R.id.id_btn_add);
     83         mLyContainer = (LinearLayout) findViewById(R.id.id_ll_container);
     84 
     85         mBtnAdd.setOnClickListener(new View.OnClickListener()
     86         {
     87             @Override
     88             public void onClick(View v)
     89             {
     90                 try
     91                 {
     92                     int a = mA++;
     93                     int b = (int) (Math.random() * 100);
     94 
     95                     //创建一个tv,添加到LinearLayout中
     96                     TextView tv = new TextView(MainActivity.this);
     97                     tv.setText(a + " + " + b + " = caculating ...");
     98                     tv.setId(a);
     99                     mLyContainer.addView(tv);
    100 
    101                     Message msgFromClient = Message.obtain(null, MSG_SUM, a, b);
    102                     msgFromClient.replyTo = mMessenger;
    103                     if (isConn)
    104                     {
    105                         //往服务端发送消息
    106                         mService.send(msgFromClient);
    107                     }
    108                 } catch (RemoteException e)
    109                 {
    110                     e.printStackTrace();
    111                 }
    112             }
    113         });
    114 
    115     }
    116 
    117     private void bindServiceInvoked()
    118     {
    119         Intent intent = new Intent();
    120         intent.setAction("com.zhy.aidl.calc");
    121         bindService(intent, mConn, Context.BIND_AUTO_CREATE);
    122         Log.e(TAG, "bindService invoked !");
    123     }
    124 
    125     @Override
    126     protected void onDestroy()
    127     {
    128         super.onDestroy();
    129         unbindService(mConn);
    130     }
    131 
    132 
    133 }

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

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

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

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

    布局文件

     1 <LinearLayout android:id="@+id/id_ll_container"
     2               xmlns:android="http://schemas.android.com/apk/res/android"
     3               xmlns:tools="http://schemas.android.com/tools"
     4               android:layout_width="match_parent"
     5               android:layout_height="match_parent"
     6               android:orientation="vertical"
     7               android:paddingBottom="@dimen/activity_vertical_margin"
     8               android:paddingLeft="@dimen/activity_horizontal_margin"
     9               android:paddingRight="@dimen/activity_horizontal_margin"
    10               android:paddingTop="@dimen/activity_vertical_margin"
    11               tools:context=".MainActivity">
    12 
    13     <TextView
    14         android:id="@+id/id_tv_callback"
    15         android:layout_width="match_parent"
    16         android:layout_height="wrap_content"
    17         android:text="Messenger Test!"/>
    18 
    19     <Button android:id="@+id/id_btn_add"
    20             android:layout_width="wrap_content"
    21             android:layout_height="wrap_content"
    22             android:text="add"/>
    23 
    24 </LinearLayout>

    效果图

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

    整个通信的代码看起来还是相当的清爽的,那么大家有没有对其内部的原理有一丝的好奇呢?下面我们就来看下其内部是如何实现的。

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


    三、源码分析

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

    (一)首先我们看客户端向服务端通信

    服务端

    服务端的onBind是这么写的:

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

    那么点进去:

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

    可以看到返回的是mTarget.asBinder();

    mTarget是哪来的呢?

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

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

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

     1 final IMessenger getIMessenger() {
     2         synchronized (mQueue) {
     3             if (mMessenger != null) {
     4                 return mMessenger;
     5             }
     6             mMessenger = new MessengerImpl();
     7             return mMessenger;
     8         }
     9     }
    10 
    11      private final class MessengerImpl extends IMessenger.Stub {
    12         public void send(Message msg) {
    13             msg.sendingUid = Binder.getCallingUid();
    14             Handler.this.sendMessage(msg);
    15         }
    16     }

    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.

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

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

    客户端

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

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

    而,我们的代码中是

    mService = new Messenger(service);

    跟进去,你会发现:

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

    soga,和我们平时的写法一模一样!

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

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

    那么,客户端与服务端通信的确没什么特殊的地方,我们完全也可以编写个类似的aidl文件实现;那么服务端是如何与客户端通信的呢?

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

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

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

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

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

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

    客户端也是通过Handler创建的Messenger,于是asBinder返回的是:

     1 public Messenger(Handler target) {
     2         mTarget = target.getIMessenger();
     3     }
     4  final IMessenger getIMessenger() {
     5         synchronized (mQueue) {
     6             if (mMessenger != null) {
     7                 return mMessenger;
     8             }
     9             mMessenger = new MessengerImpl();
    10             return mMessenger;
    11         }
    12     }
    13 
    14     private final class MessengerImpl extends IMessenger.Stub {
    15         public void send(Message msg) {
    16             msg.sendingUid = Binder.getCallingUid();
    17             Handler.this.sendMessage(msg);
    18         }
    19     }
    20 
    21    public IBinder getBinder() {
    22         return mTarget.asBinder();
    23     }

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

    1 #IMessenger.Stub
    2 
    3 @Override 
    4 public android.os.IBinder asBinder()
    5 {
    6 return this;
    7 }
    那么其实返回的就是MessengerImpl对象自己。到这里可以看到message.mTarget.asBinder()其实返回的是客户端的MessengerImpl对象。

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

    1 msgfromClient.replyTo.send(msgToClient);
    2 
    3 public void send(Message message) throws RemoteException {
    4         mTarget.send(message);
    5     }

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

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

    总结下:

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

    可以考虑自己编写aidl文件,实现下服务端对客户端的回调。

  • 相关阅读:
    剑指OFFER----面试题17- 打印从1到最大的n位数
    剑指OFFER----面试题16. 数值的整数次方
    剑指OFFER----面试题15. 二进制中1的个数
    剑指OFFER----面试题14- II. 剪绳子II
    07 多层if判断
    08 while循环
    06 if 流程控制
    03 身份运算符、逻辑运算符
    04 位运算符、运算符优先级
    02 赋值运算符、成员运算符
  • 原文地址:https://www.cnblogs.com/linghu-java/p/8798374.html
Copyright © 2011-2022 走看看