进程间通信----Messenger

来源:互联网 发布:网络手机卡资费对比 编辑:程序博客网 时间:2024/06/13 10:59

在上次,我写到了利用aidl来进行进程间通信的方式,并且满足了基本需求,然后,使用aidl方式进行进程间通信始终比较麻烦,需要写aidl文件等等,那么是否有比较简单的通信方式呢,是有的那就是Messenger

本篇的内容主要是参考着

http://blog.csdn.net/lmj623565791/article/details/47017485; 
【张鸿洋的博客】

来完成的,我只是在看了之后进行了自己的编写测试,并且针对我在项目中需要传递自定义对象扩展了一下


使用Messenger进行通信的方式要比aidl写起来方便的多,这种方式大概的流程为:



这种方式可以做到双向通信


那么下面就是代码了,首先是服务端:我自己的测试的项目在另一台电脑上,所以就拿借鉴的博主的代码来了,其实

我自己写的是差不多的

package com.example.aidlClientdemo;import android.app.Service;import android.content.Intent;import android.os.Handler;import android.os.HandlerThread;import android.os.IBinder;import android.os.Message;import android.os.Messenger;import android.os.RemoteException;public class ProcessCommonicationService extends Service{private static final int MSG_SUM = 0x110;HandlerThread handlerThread = new HandlerThread("myHandlerThread");       //这里在handler构造器内传入handlerThread的Lopper,可以让handleMessage在handlerThread的线程内队列执行    private Messenger mMessenger = new Messenger(new Handler(handlerThread.getLooper())    {        @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);        }    });        @Overridepublic void onCreate() {super.onCreate();//使用handlerThread可以进行队列执行handlerThread.start();}    @Override    public IBinder onBind(Intent intent)    {        return mMessenger.getBinder();    }}

客户端代码结束之后就是服务端的代码

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);    }}


在上面的例子中,是传递了基本类型的参数,而我们实际使用中则很多都会需要用到传递自定义类型,这种时候只需要这么做就行了

Message msgFromClient = Message.obtain(null, MSG_SUM, a, b);        msgFromClient.replyTo = mMessenger;        Bundle bundle = new Bundle();        bundle.putParcelable("data", new User());        msgFromClient.setData(bundle);                if (isConn)        {            //往服务端发送消息            mService.send(msgFromClient);        }
就是通过在Message中添加Bundle来传递我们自定义的对象,这个对象需要实现Parcelable接口


通过上面的方式可以方便的进行进程间的通讯


那么,为什么通过这种方式可以进行进程间通讯呢?这一点在我解读的文章中也有描述


之前我了解过aidl的进程通讯方式,在aidl的通讯方式可以发现与这里非常相似


1、bindService的方式

2、ServiceConnection的使用

3、aidl接口的对象获取,其实可以发现这里也是一样的

这里调用的通讯方式为Messenger mService = new Messenger(service);

而aidl的接口对象获取方式为:ICalcAIDL mCalcAidl = ICalcAIDL.Stub.asInterface(service);

表面上看起来好像有点不同,但是,如果我们去看Messenger的构造器

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


这里返回的mTarget对象为

private final IMessenger mTarget;

跟aidl客户端的方式一模一样,在调用通讯方法send(Message message)我们再去看

public void send(Message message) throws RemoteException {        mTarget.send(message);    }
很明显,其实内部的逻辑方式就是使用的aidl



那么客户端我们发现了是一样的,再去看服务端呢?

在服务端我们的代码是

    private Messenger mMessenger = new Messenger(new Handler(handlerThread.getLooper())    {        @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();    }


而aidl的通讯方式是

private final ICalcAIDL.Stub mBinder = new ICalcAIDL.Stub()      {            @Override          public int add(int x, int y) throws RemoteException          {              return x + y;          }            @Override          public int min(int x, int y) throws RemoteException          {              return x - y;          }        };  

然后

    public IBinder onBind(Intent t)      {          Log.e(TAG, "onBind");          return mBinder;      }  
就是将一个实现了我们自定义的aidl对象的Stub接口的对象作为通讯方式返回


那么,现在的Messenger方式又是怎样的呢

我们首先来看Messenger的构造器,来看看这这里面做了什么

 public Messenger(Handler target) {        mTarget = target.getIMessenger();    }
这里从我们传入的handler中取出了IMessenger对象


这个IMessenger,其实是依赖一个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);  }  

这样,在服务端就只差一个东西了,就是实现了IMessenger.Stub的对象,这个对象我们去哪里找?

然后我们去看

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

这里的target.getMessenger()方法,我们进去看一下,可以看到

    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);        }    }
找到了!这个MessengerImpl对象就是实现了IMessenger.Stub的对象


这样一来,需要的要素就都齐了,按照现在的代码来推测的话,不难推测出

    @Override    public IBinder onBind(Intent intent)    {        return mMessenger.getBinder();    }
这里返回的mMessenger.getBinder()应该是MessengerImpl对象,不过我们还是得去确认一下


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

这里调用了asBinder()方法,再进去看

@Override public android.os.IBinder asBinder(){return this;}
可以看到,asBinder方法就是返回的mTarget对象自己,就是MessengerImpl对象


这里这个asBinder方法的源码是aidl文件在gen路径下生成的类里面可以看到的,如果读者想看的话,可以在aidl的例子中查看,就可以看到


那么Messenger配合Handler的进程间通信的原理到这里就分析结束了,从这个看来的话,其实我们完全也可以自己写一个类似于Messenger的对象用来进行进程间通信,并且利用Message可以传递数据的特性来进行数据的传递,可以说还是非常方便的
















0 0