Android之进程间通信(IPC)-Messenger

知识点:

进程间通信的几种方式;
Android中利用messenger信使进行通信;
新名词记录{IPC机制;ContentProvider;Socket;AIDL;Messenger}

概述

众说周知,如果需要跨进程通讯,有几个方法来实现:IPC机制(Android利用Binder),Bundle/Intent传递数据,文件共享,Messenger,ContentProvider,Socket和AIDL。

其中Bundle/Intent我们用的最多,基本上都是在页面之间传递数据。但是只能够传递基本的数据类型,而且都是要实现Serializable或Parcellable接口的数据结构。利用bundle和intent传递是对数据进行序列化和反序列化的,实现了Serializable或Parcellable接口的类,都能够进行序列化和反序列化操作。查看Java序列化和反序列化

文件共享,相同进程或者不同进程间对同一个文件进行读写操作,从而可以达到进程通信或者是进程数据共享。

AIDL通过定义服务端暴露的接口,以提供给客户端来调用,AIDL使服务器可以并行处理。

其它的就不讲了,大伙可以自个去搜一下。

Messenger进程间通信

这里我就只讲Messenger信使进行进程间通信。Messenger是基于AIDL实现的,服务端(被动方)提供一个Service来处理客户端(主动方)连接,维护一个Handler来创建Messenger,在onBind时返回Messenger的binder。双方用Messenger来发送数据,用Handler来处理数据。Messenger处理数据依靠Handler,所以是串行的,也就是说,Handler接到多个message时,就要排队依次处理。

下面是实战过程。

首先是远程进程。这里的远程进程,我们利用Androidmanifest文件里面的process属性来设置。如下所示:

<service
            android:name=".db.share.MessengerService"
            android:enabled="true"
            android:exported="true"
            android:process=":messenger"
            android:permission="com.yaojt" />

name:就是继承自service的远程服务类。就是下面的MessengerService.java类;

android:process = 告诉系统我要运行在messenger的进程中。

android:permission=”com.yaojt” -定义此服务的权限,否则所有的进程都能够访问此服务,这是不安全的。

package com.yaojt.db.share;

/**
 * desc:服务端的service
 * <p>
 * author:kuyu.yaojt (tanksu)
 * <p>
 * email:yaojt@kuyumall.com
 * <p>
 * blog:http://blog.csdn.net/qq_16628781
 * <p>
 * date:17/4/11
 */
public class MessengerService extends Service {
    public MessengerService() {
    }

    @Override
    public void onCreate() {
        super.onCreate();
        long curId = Thread.currentThread().getId();
        CommonLog.logInfo("---->>", "MessengerService的线程id(onCreate)= " + curId +
                "\n 线程名字:" + Thread.currentThread().getName());
    }

    /**
     * 创建一个Messenger,并与一个handler关联
     */
    private Messenger messenger = new Messenger(new ServerHandler());

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

    /**
     * 接收和处理客户端发来的信息
     */
    public static class ServerHandler extends Handler {
        public final static int MSG_FROM_CLIENT = 0;
        public final static int MSG_FROM_SERVER = 1;

        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            if (msg.what == MSG_FROM_CLIENT) {
                Bundle bundleGet = msg.getData();
                String strFromClient = bundleGet.getString("clientKey");
                CommonLog.logInfo("----->server.handler", strFromClient);
                Messenger clientMessenger = msg.replyTo;
                Message replyMsg = Message.obtain(null, MSG_FROM_SERVER);
                Bundle bundle = new Bundle();
                bundle.putString("serverKey", "note: this message come from server!");
                replyMsg.setData(bundle);
                try {
                    clientMessenger.send(replyMsg);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

在远端的进程中,首先打印出当前线程的id和name。因为选择bindservice()方法来启动服务,所以只重写onBind(Intent intent)方法。
我们先注意到这个语句:

private Messenger messenger = new Messenger(new ServerHandler());

Messenger是直接实现了Parcelable接口,所以他得以序列化和反序列化。因为service通过binder进行通信,所以信使类Messenger在onbind的时候,返回IBinder类或者其子类。

在ServerHandler类里面进行处理另一个进程传过来的信息。可以利用msg.getData()方法得到bundle,依次取出数据。同样的,在另一进程中,还将信使利用msg.replyTo传递过来了。我们可以获取到这个信使,发送消息出去,最后与传递过来的信使关联的handler就可以接收到消息并进行处理了。信使调用send()方法,其实和handler调用sendMessage()方法是一样的。

这里快速复习下:启动一个service,有两种方法:startservice()和bindservice()。startservice()首先会调用service里面的oncreate()方法,然后调用onStart()方法,在启动它的组件销毁之后,service会继续运行,和组件不是同生共死。bindservice()方法,如果service已经启动,那么就不会再调用oncreate()方法了,会直接调用service里面的onBind()方法。
如何结束service:bindservice()的可以不用管理,启动它的组件被销毁了,那么service也会随之销毁。如果是startservice()启动的service,在完成service的任务之后,一般是耗时操作,我们可以调用stopSelf()方法停止该service了。
然后我们就在activity里面启动该service,注意因为我们设置了改service是运行在另一个进程中的。

package com.yaojt.db.share;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Build;
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.annotation.Nullable;

import com.yaojt.sdk.java.utils.CommonLog;
import com.yaojt.ui.BaseActivity;

/**
 * desc:
 * <p>
 * author:kuyu.yaojt (tanksu)
 * <p>
 * email:yaojt@kuyumall.com
 * <p>
 * blog:http://blog.csdn.net/qq_16628781
 * <p>
 * date:17/4/11
 */

public class ServiceClientActivity extends BaseActivity {
    private final String TAG = this.getClass().getSimpleName();
    /**
     * 给服务端发消息的Messenger
     */
    private Messenger mMessenger;
    /**
     * 接收服务端消息的Messenger
     *
     */
    private Messenger mClientMessager = new Messenger(new ClientHandler());

    private ServiceConnection mServiceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            CommonLog.logInfo("连接服务成功!");
            onConnected(name, service);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            CommonLog.logInfo("连接服务失败!");
        }
    };

    private void onConnected(ComponentName name, IBinder service) {
        Messenger mMessenger = new Messenger(service);
        //参数2是what的静态方法
        Message msg = Message.obtain(null, MessengerService.ServerHandler.MSG_FROM_CLIENT);
        //在客户端带入数据
        Bundle bundle = new Bundle();
        bundle.putString("clientKey", "note: this message come from client!");
        msg.setData(bundle);
        msg.replyTo = mClientMessager;
        try {
            mMessenger.send(msg);
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            super.onCreate(savedInstanceState);
        }
        long curId = Thread.currentThread().getId();
        CommonLog.logInfo("---->>", "ServiceClientActivity的线程id= " + curId +
                "\n 线程名字:" + Thread.currentThread().getName());
        initData();
    }

    public void initData() {
        Intent intent = new Intent(this, MessengerService.class);
        /**
         * 参数1:intent
         * 参数2:service的通断的监听
         * 参数3:标志。BIND_AUTO_CREATE:自动创建service;
         * 
         */
        bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
    }

    /**
     * 接收和处理服务端发来的信息的handler
     */
    public static class ClientHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            if (msg.what == MessengerService.ServerHandler.MSG_FROM_SERVER) {
                //来自服务的数据
                Bundle bundle = msg.getData();
                String strFromServer = bundle.getString("serverKey");
                CommonLog.logInfo("---->client.handler", strFromServer);
            }
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        //需要解绑服务,否则内存泄漏
        unbindService(mServiceConnection);
    }
}

这里,和远程进程相类似的,只是我们这里调用了bindservice()方法启动服务,然后我们这里给启动的结果设置一个成功和失败的监听。我们只要在成功的回调里面给远程进程发送数据。

我们看到这里

private Messenger mClientMessager = new Messenger(new ClientHandler());

这里是定义一个此进程用来处理远端进程返回来的消息的信使,对应的代码是msg.replyTo = mClientMessager;远端进程正获取此进程的信使实体类,然后进行数据发送的。通过mClientMessager发送的消息,就和调用sendMessage()方法发送消息一样。

注意:是重要记得解绑服务,unbindService(),否则IDE会报一个内存泄漏的异常
关于bindService()方法参数3的说明:

BIND_DEBUG_UNBIND:仅在debug时候使用,因为设置此标志,就不会调用unbindService()方法释放服务,

它的生命周期就和APP一样长了,极有可能造成内存泄漏;
BIND_ABOVE_CLIENT:设置服务的重要性比app要大;这是一个保活的策略之一哈;

BIND_WAIVE_PRIORITY:设置此服务像APP一样,运行在后台;

BIND_IMPORTANT:提升服务的优先级别为前台进程;

BIND_ADJUST_WITH_ACTIVITY:提升服务的优先级和activity一样高;

BIND_ALLOW_WHITELIST_MANAGEMENT:提升服务优先级为白名单,最后你懂滴;

还有好多个,有兴趣的可以去看源码。

最后记得在Androidmanifest文件注册上面的activity

<activity
            android:permission="com.yaojt"
            android:name=".db.share.ServiceClientActivity"
            android:screenOrientation="portrait" />

最后来看一下运行的截图;
activity进程:
在这里插入图片描述
远端进程:
在这里插入图片描述

总结

我们知道了跨进程间通信有哪几个方式,这里介绍了利用信使messenger来进行通信。说白了,这里是通过传递信使messenger,利用获取到的messenger进行发送message,那么与获取到的messenger相关连的handler就可以接收到消息了。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值