码迷,mamicode.com
首页 > 移动开发 > 详细

Android IPC之Messenger浅谈

时间:2016-11-15 20:23:54      阅读:310      评论:0      收藏:0      [点我收藏+]

标签:component   不同   ide   nbsp   简单   others   undle   cts   cti   

      之前写过一篇有关 IPC之AIDL浅谈的文章,详情请看Android IPC之AIDL浅谈。今天再来介绍另一种 IPC-Messenger。

一、概述。

首先看Messenger介绍,

 Reference to a Handler, which others can use to send messages to it.
This allows for the implementation of message-based communication across
  processes, by creating a Messenger pointing to a Handler in one process,
 and handing that Messenger to another process.
大概意思是说,通过Handler来发送消息。允许在进程中基于消息通信实现,在一个进程中创建一个信使指向一个Handler,并将该信使传递给另外一个进程。

换句话说,就是Messenger通过Handler来发送消息,可以在不同的进程间通信。

下面通过一张图来说明,

技术分享

该图基本说明了Messenger的上层工作流程。小结如下:

1.Messenger的使用需要有服务端和客户端;

2.客户端需要绑定远程连接,绑定成功后,就能获取远程的IBinder对象,通过该IBinder就可以得到远程的Messenger对象;

3.向服务端发送消息时,需要使用服务端的Messenger对象,并且可以把客户端的Messenger对象赋值给消息的‘replyTo’属性,一并传递到服务端;

4.向客户端发送消息,需要使用客户端的Messenger对象,该对象是从客户端发送的消息的‘replyTo’属性中获取的。

下面依旧通过一个实例来演示Messenger的整个流程。

二、实例。

创建两个应用,一个是服务端,一个客户端。

1.服务端实现。

创建应用后,创建一个Service,

/**
 * 服务端
 */
public class MessengerService extends Service {
    private static final String TAG = "MessengerService";
    private static final int WHAT = 0x101;
    Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what) {
                case WHAT:
                    //接受从客户端传来的消息
                    Bundle bundle= (Bundle) msg.obj;
                    String str = (String) bundle.get("str");
                    Log.e(TAG, "服务端已接受到客户端的消息");
                    Log.e(TAG, "消息是---->" + str);
                    //发送数据给客户端
                    Message message = Message.obtain();
                    Bundle bundle1= new Bundle();
                    bundle1.putString("str","我已经知道了,加油,看好你!");
                    message.obj = bundle1;
                    message.what=0x102;
                    try {
                        //向客户端发送消息
                        msg.replyTo.send(message);//首先获取从客户端传递的Messenger对象,然后调用该Messenger对象的send()方法
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                    break;
            }
        }
    };
    private Messenger messenger = new Messenger(mHandler);

    @Override
    public void onCreate() {
        super.onCreate();
        Log.d(TAG, "onCreate: ");
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return messenger.getBinder();
    }


}

实现该Service的onBind()方法,调用‘messenger.getBinder()’返回一个IBinder,返回的IBinder我们将会在客户端接收并使用。创建一个Handler,用于接收客户端传递的消息,当接收到消息后,然后向客户端发送了一个消息。服务端就是这些内容!

      一定要记得,发送消息传递数据时不能把数据直接扔给消息,否则会报错,如下所示:

java.lang.RuntimeException: Can‘t marshal non-Parcelable objects across processes.
因为Binder事务传递的数据被称为包裹(Parcel),必须实现Parcelable接口,否则无法在两个应用之间进行通信。所以,如果需要在消息中携带数据,请使用Bundle对象,因为给类以及实现了Parcelable接口。
      还要记得注册该服务。

... 
 <service
            android:name=".MessengerService"
            android:exported="true">
            <intent-filter>
                <action android:name="cn.xinxing.messengerservice"></action>
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </service>
...
其中android:exported的作用是“是否支持其它应用调用当前组件”,true允许被启动;false不允许被启动。并且设置了“action”属性,便于我们隐式调用该服务。

一切Ok的话,就可以运行该程序了!

PS:

      IBinder是一个接口。 IBinder是远程对象的基本接口,是为高性能而设计的轻量级远程调用机制的核心部分。但它不仅用于远程调用,也用于进程内调用。这个接口定义了与远程对象交互的协议。不要直接实现这个接口,而应该从Binder派生。

      IBinder的主要API是transact(),与它对应另一方法是Binder.onTransact()。第一个方法使你可以向远端的IBinder对象发送发出调用,第二个方法使你自己的远程对象能够响应接收到的调用。IBinder的API都是同步执行的,比如transact()直到对方的Binder.onTransact()方法调用完成后才返回。调用发生在进程内时无疑是这样的,而在进程间时,在IPC的帮助下,也是同样的效果。

2.客户端实现。

public class MainActivity extends AppCompatActivity {

    private static final String TAG = "MainActivity";
    private static final int WHAT = 0x102;
    private boolean isConn;
    Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what) {
                case WHAT:
                    //接受从服务端发送的消息
                    Bundle bundle= (Bundle) msg.obj;
                    String str = (String) bundle.get("str");
                    Log.e(TAG, "客户端服已接受到务端的消息");
                    Log.e(TAG, "消息是---->" + str);
                    break;
            }
        }
    };

    private Messenger mClientMessenger = new Messenger(mHandler);
    private Messenger mServiceMessenger;
    
    ServiceConnection serviceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            Log.e(TAG, "连接Service成功---->");
            mServiceMessenger = new Messenger(service);
            isConn = true;
            Message message = Message.obtain();
            Bundle bundle=new Bundle();
            bundle.putString("str","你好!我是程序猿!");
            message.obj=bundle;
            message.what = 0x101;
            message.replyTo = mClientMessenger;
            try {
                mServiceMessenger.send(message);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            Log.e(TAG, "连接Service---->disconnected");
            mServiceMessenger = null;
            isConn = false;
        }
    };


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        findViewById(R.id.btn_start_service).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                bindService();
            }
        });
    }

    /**
     * 绑定远程连接服务
     */
    private void bindService() {
        Intent intent = new Intent();
        intent.setAction("cn.xinxing.messengerservice");
        bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (isConn) {
            unbindService(serviceConnection);
        }
    }
}

屏幕中有一个按钮,点击按钮时,绑定远程连接服务;绑定成功后,发送消息;Handler用于接收消息;当Activity销毁时,解除绑定。

整个实现还是比较简单的。

运行客户端后,点击按钮,看Log输出,

技术分享
远程连接服务绑定成功,接着服务端输出Log,

技术分享

最后客户端Log输出,

技术分享

小结:整个流程是,点击按钮时,绑定远程连接服务,绑定成功后,并且会拿到远程服务的Messenger对象,然后使用Messenger对象发送消息,并且将客户端的Messenger对象也发送到了服务端,当服务端收到消息后,会输出客户端发送的消息内容,并且拿到客户端的Messenger对象,然后使用该Messenger对象向客户端发送消息,客户端收到消息后,会输出Log。

整体上,使用Messenger实现IPC,相比AIDL来说,还是比较简单的。下面我们通过Messenger的源码来分析,它的内部实现。

三、源码解析。

先看Messenger的构造方法,

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

构造方法传递一个Handler,然后将HandlerIMessenger对象传递给mTarget对象(MessengerImpl)。下面是Handler的getIMessenger()具体实现,

    final IMessenger getIMessenger() {
        synchronized (mQueue) {
            if (mMessenger != null) {
                return mMessenger;
            }
            mMessenger = new MessengerImpl();
            return mMessenger;
        }
    }

实例化一个MessengerImpl对象,然后返回了一个IMessenger对象。其中MessengerImpl的源码如下,

    private final class MessengerImpl extends IMessenger.Stub {
        public void send(Message msg) {
            msg.sendingUid = Binder.getCallingUid();
            Handler.this.sendMessage(msg);
        }
    }
看到这里IMessenger.Stub,貌似很熟悉啊!这不就是AIDL的实现吗!bingo!是的!Messenger的底层其实就AIDL!只不过,Messenger已经为我们封装了好了,不需要我们自己关心AIDL的具体细节。

IMessenger的源码位于‘ \base\core\java\android\os\IMessenger.aidl’,可以看到它是一个aidl文件,下面是它的源码,

package android.os;

import android.os.Message;

/** @hide */
oneway interface IMessenger {
    void send(in Message msg);
}
内部只有一个方法,用于发送消息。这里更加证实了,Messenger的底层其实就AIDL!有关如何实现一个AIDL,可以参考 Android IPC之AIDL浅谈

当发送消息时,调用Messengersend()方法,

    public void send(Message message) throws RemoteException {
        mTarget.send(message);
    }
调用了mTarget的send()方法,而mTarget是MessengerImpl。通过上文可以得知MessengerImpl类实现了send()方法,该方法是发送一个消息,可以看到它其实还是通过Handler发送消息。有关Handler发送消息,可以参考 Android 源码解析Handler处理机制(二)

服务端有一个onBind()方法,会返回一个IBinder对象,该对象来自于Messenger,

    public IBinder onBind(Intent intent) {
        return messenger.getBinder();
    }

继续跟下去,

    public IBinder getBinder() {
        return mTarget.asBinder();
    }
    
返回mTarget对象的asBinder(),而“mTarget.asBinder()”到底是什么呢?
我们先看看使用AIDL时,AAPT自动生成的AIDL类中的asBinder()方法,
@Override public android.os.IBinder asBinder()
{
return this;
}
      该方法返回的是自己,即xxxx.Stub类。那么我们此时再看看mTarget对象,mTarget是MessengerImpl,而MessengerImpl是继承自IMessenger.Stub,那么mTarget.asBinder()返回的就是Messenger.Stub.asBinder(),而Messenger.Stub.asBinder()返回的又是Messenger.Stub,而MessengerImpl是继承自IMessenger.Stub,所以绕了一圈,“mTarget.asBinder()”返回的就是MessengerImpl对象自己。也就是说“mTarget.asBinder()”返回的是当前Messenger中的MessengerImpl对象。

      当在服务端时,“mTarget.asBinder()”返回的是服务端的MessengerImpl对象,使用它来向服务端发送消息;当在客户端时,“mTarget.asBinder()”返回的是客户端的MessengerImpl对象,使用它来向客户端发送消息。

客户端绑定远程连接成功后,会获取到一个远程IBinder对象,通过该IBinder对象,就可以获取到服务端的Messenger对象,即调用下面的方法,

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

这不是和AIDL的写法一模一样吗!下面是AIDL的代码,

 //远程连接
    private ServiceConnection conn = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            calculateAidl = CalculateAidl.Stub.asInterface(service);
            isBindSuccess=true;
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            calculateAidl = null;
            isBindSuccess=false;
        }
    };

没啥好多的!有关AIDL,详情请看Android IPC之AIDL浅谈

好了,有关Messenger的源码分析就这么多!

四、小结。

1.Messenger的底层还是使用的AIDL;

2.服务端和客户端通信,向服务端发现消息,使用的是服务端的Messenger(该对象来自于远程连接成功通过IBinder获取),向客户端发送消息,使用的是客户端的Messenger(该对象是向服务端发送的消息中携带的-replyTo属性携带的参数);

3.在IPC中,要传递参数,需要实现Parcelable接口,建议使用Bundle封装参数。

4.Messenger会将所有Service请求入队列,所以它不支持多线程通信。如果你要支持多线程,那么请使用AIDL。

Android IPC之Messenger浅谈

标签:component   不同   ide   nbsp   简单   others   undle   cts   cti   

原文地址:http://blog.csdn.net/zxw136511485/article/details/53168501

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!