Android通过Binder实现进程间通信

1、进程间通信

在Android应用开发里面,一般的APP都使用不到多进程开发,但一般来说,我们也接触过多进程相关的东西,比如说我们使用第三方推送的时候,这里的推送服务都是属于远程服务,APP并没有启动的时候,也会得到推送的消息。

进程间通信,也是比较模糊的概念,一直以来也不好理解,为什么进程间通信还要搞得那么复杂,当然这还得源于Android系统的安全性来考虑。

2、进程间通信的方式

进程间通信的方式比较多,Bundle、ContentProvider、AIDL、Messanger、共享文件、Socket等等。

AIDL是官方支持的,多进程、多线程,支持并发的进程间通信方式。也是本次要记录的IPC方式。另外,Messanger只支持单线程的进程间通信(内部也是通过AIDL实现)。

3、AIDL相关的知识点

通常说的进程间通信主要是指:Activity与远程服务Service的数据交互。那么这里涉及到几个知识点

1、Activity,如何绑定Service,在绑定的过程中如何拿到AIDL接口实现接口调用。

2、Service,AIDL接口的实现,在绑定成功之后,返回Binder引用到客户端。

3、Binder,进程间通信的桥梁,不同进程之间通信的提供协议。

4、AIDL,接口描述语言,描述接口,跟Java接口不同,不能定义变量,只可以定义方法。

5、传递对象,AIDL接口传参只允许传递基本数据类型,传递对象需要通过实现Parcelable接口。

6、Parcelable,对象想要实现进程间通信,就要实现Parcelable接口,才可以序列化后进行传递。

4、客户端如何发送消息给服务器端

4.1、定义AIDL接口

package com.example.aidl;
import com.example.aidl.data.CustomData;
import com.example.aidl.CustomReceiver;

interface CustomSender {
    void sendMessage(in CustomData customData);

    void register(CustomReceiver receiver);
    void unRegister(CustomReceiver receiver);
}

4.2、Activity首先绑定服务Service

private void startBindService() {
    Intent intent = new Intent(this, CustomService.class);
    bindService(intent, connection, Context.BIND_AUTO_CREATE);
}

4.3、在Service服务返回IBinder,Binder sender也需要在服务器端实现

@Override
public IBinder onBind(Intent intent) {
    return sender;
}

private IBinder sender = new CustomSender.Stub() {
    @Override
    public void sendMessage(CustomData customData) throws RemoteException {
        if (customData == null) {
            Log.i(TAG, "消息是空的");
        } else {
            String name = customData.getName();
            int id = customData.getId();
            Log.i(TAG, "收到客户端的消息:" + name + " " + id);
        }
    }
};

4.4、Activity绑定Service成功之后,通过返回来的IBinder,转换成为接口,从而实现了接口的调用

private ServiceConnection connection = new ServiceConnection() {
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        CustomData data = new CustomData();
        data.setName("我的消息");
        data.setId(6);

        try {
            customSender = CustomSender.Stub.asInterface(service);
            customSender.sendMessage(data);
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void onServiceDisconnected(ComponentName name) {

    }
};

5、服务的如何推送消息至客户端

5.1、Activity实现消息接收器的AIDL接口

//消息监听回调接口
private CustomReceiver customReceiver = new CustomReceiver.Stub() {

    @Override
    public void onMessageReceived(String message) throws RemoteException {
        Log.i(TAG, "onMessageReceived: " + message);
    }
};

5.2、在服务器连接成功的时候,需要注册,有点像是观察者模式。

customSender.register(customReceiver);

5.3、服务端实现的接口方法

/**
 * 用于处理消息监听的类
 **/
private RemoteCallbackList<CustomReceiver> listenerList = new RemoteCallbackList<>();

private IBinder sender = new CustomSender.Stub() {
    @Override
    public void register(CustomReceiver receiver) throws RemoteException {
        if (receiver != null) {
            listenerList.register(receiver);
        }
    }

    @Override
    public void unRegister(CustomReceiver receiver) throws RemoteException {
        if (receiver != null) {
            listenerList.unregister(receiver);
        }
    }
};

5.4、通常服务端会有一个线程与后台发生长连接,获取到数据之后,要通过遍历消息监听器列表,从而实现来此客户端的AIDL接口调用,这里模拟每3秒给客户端发送消息

private class MessageRunnable implements Runnable {
    @Override
    public void run() {
        while (!isDestroy.get()) {
            Log.i(TAG, "线程启动!");
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            try {
                int size = listenerList.beginBroadcast();
                for (int i = 0; i < size; i++) {
                    CustomReceiver customReceiver = listenerList.getBroadcastItem(i);
                    if (customReceiver != null) {
                        customReceiver.onMessageReceive("来自服务器端的消息。时间:" + System.currentTimeMillis());
                    }
                }
                listenerList.finishBroadcast();
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
    }
}

6、重连机制 DeathRecipient

6.1、实现DeathRecipient,监测服务死亡的情况,比如服务Service挂掉了,这个时候应该重启服务

private IBinder.DeathRecipient deathRecipient = new IBinder.DeathRecipient() {
    @Override
    public void binderDied() {
        if (customSender != null) {
            customSender.asBinder().unlinkToDeath(this, 0);
            customSender = null;
        }
        startBindService();
    }
};

6.2、在连接上的时候添加上

customSender.asBinder().linkToDeath(deathRecipient, 0);

7、如何做进程校验(初始化application,进程id,权限)

多进程会出现实现多个Application的情况,通常,我们Application里面的初始化却并不需要多次初始化,所以在Application的初始化onCreate()方法里面可以进行分进程而初始化,而区别进程的方法可以根据进程名称来实现。然后判断名称来实现相关的初始化。

private String getProcessName() {
    ActivityManager activityManager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
    List<ActivityManager.RunningAppProcessInfo> list = activityManager.getRunningAppProcesses();
    if (list == null) {
        return null;
    }

    for (ActivityManager.RunningAppProcessInfo runningAppProcessInfo : list) {
        if (runningAppProcessInfo != null && runningAppProcessInfo.pid == Process.myPid()) {
            return runningAppProcessInfo.processName;
        }
    }
    return null;
}

8、总结

进程间通信用的得当会变得相当强大,比如多个进程获得系统分配的内存也更大,可以实现的事件也更多,可以做的事情也是非常值得思考的,但使用不当,就只会给自己增加工作量,人还是要往前进步的,多一份知识多一条路。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,761评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,953评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,998评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,248评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,130评论 4 356
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,145评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,550评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,236评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,510评论 1 291
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,601评论 2 310
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,376评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,247评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,613评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,911评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,191评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,532评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,739评论 2 335

推荐阅读更多精彩内容