【Android】IPC、AIDL、Binder

IPC:Inter Process Communication,跨进程间通信。

AIDL:Android Interface Definition Language,Android接口定义语言,用于生成可以在Android设备上两个进程之间进行进程间通信的代码。

Binder:是Android中一种跨进程通信的方式。从Android FrameWork角度来说,Binder是ServiceManager连接各种Manager和相应ManagerService的桥梁。从Android应用层的角度来说, BInder是客户端和服务端进行通信的媒介。

三者的关系:
AIDL是基于Binder机制实现Android上的IPC。

一、AIDL实践

先从例子出发,从例子理解aidl。AIDL涉及一个服务端和一个客户端。
1、服务端
(1)新建一个工程,AIDLServer。
(2)右键新建一个AIDL File,删掉里面的方法,写上自己的方法声明。

// IMyAidlInterface.aidl
package com.cm.aidlserver;

interface IMyAidlInterface {
    void callF1();
    int callF2();
}

(3)选择Build -> Rebuild Project,会自动根据接口生成Java文件。位置在app/build/generated/source/aidl下面。可以先大概看一下生成的内容,本质还是一个继承了接口的接口。里面的内容等下解释。
(4)上面生成的只是个接口,是必须要实现的。
新建一个服务service。

package com.cm.aidlserver;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;

public class MyService extends Service {
    public MyService() {
    }

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

    private IBinder myBinder = new IMyAidlInterface.Stub() {
        @Override
        public void callF1() throws RemoteException {
            Log.i("chenming server", "callerF1");
        }

        @Override
        public int callF2() throws RemoteException {
            Log.i("chenming server", "callerF2");
            return 0;
        }
    };
}

在onBind方法中返回对应的binder对象,binder对象为IMyAidlInterface.Stub的实例对象,实现了其中的抽象方法,即我们定义的aidl中声明的方法。

为了可以在客户端调用,需要在xml中将外界调用设置为true。

<service
    android:name=".MyService"
    android:enabled="true"
    android:exported="true">
</service>

2、客户端
(1)新建一个工程,AIDLClient。
(2)将服务器的AIDL文件复制到该工程中,选择Build -> Rebuild Project,会自动根据接口生成和服务端一样的Java接口文件。
(3)为了可以调用服务端的方法,需要通过binder进行调用,因此在客户端中要定义该binder。

IMyAidlInterface iMyAidlInterface;

(4)然后绑定服务端的提供的服务。

Intent intent = new Intent();
intent.setComponent(new ComponentName("com.cm.aidlserver", "com.cm.aidlserver.MyService"));
bindService(intent, conn, BIND_AUTO_CREATE);

(5)绑定成功之后会回调对应的方法,在方法中初始化binder。

private ServiceConnection conn = new ServiceConnection() {
    @Override
    public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
        iMyAidlInterface = IMyAidlInterface.Stub.asInterface(iBinder);
    }

    @Override
    public void onServiceDisconnected(ComponentName componentName) {
        iMyAidlInterface = null;
    }
};

通过IMyAidlInterface.Stub.asInterface(iBinder)初始化binder。
(6)调用对应的方法,以调用callF1方法为例,调用如下。

private void callF1() {
    try {
        iMyAidlInterface.callF1();
    } catch (RemoteException e) {
        e.printStackTrace();
    }
}

从log可以看出,可以通过客户端调用服务端的方法。

注意:原生系统中,可以通过这个方法进行保活,即当Client端启动的时候,会通过binder service拉起Server端,但是由于国产手机的系统都经过了优化,会拦截关联启动,解决方法有两个,一个是先启动server再用client去调用,另一个是打开手机里面的关联启动允许即可(以华为手机为例,打开手机管家->启动管理,找到AIDLServer改成手动管理,允许关联启动)。

二、AIDL原理分析

客户端怎么跨进程调用server端的方法的呢?

这个需要从AIDL自动生成的Java类分析。

查看生成的Java类IMyAidlInterface.java,接下来一行行解读。

首先,该类是一个继承自接口的接口,从上面实现可以知道具体的是实现是在service中。

然后,定义了一个抽象类Stub,这个类本质就是一个binder,这个类里面有以下的成员与方法:
(1)DESCRIPTOR成员,是Binder的唯一标识
(2)asInterface方法
传入参数为一个Binder,返回一个IMyAidlInterface,是用于将服务端的Binder对象转换成客户端所需的AIDL接口类型的对象。
里面先调用queryLocalInterface方法,这个方法可以从源码里面看见当客户端和服务端为同一个进程的时候,返回服务端的AIDL接口类型的对象,不同进程的时候返回null。所以根据iin进行判断,如果返回不为null,说明在同一个进程内调用,直接返回AIDL接口类型的对象,为null的时候说明不在同一个进程内调用,返回封装后的对象Stub.Proxy。
(3)asBinder方法,返回当前的binder对象。
(4)onTransact方法。当客户端发起跨进程请求的时候,远程请求会通过系统底层封装后交到该方法处理。原型如下:

public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags)

code为请求参数,根据这个参数来确定调用哪个方法,data用以传递方法参数,reply用以传递结果即返回值。该方法的返回值标记是否调用成功。
(5)对于每个方法定义一个code。

static final int TRANSACTION_callF1 = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_callF2 = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);

(6)从上面知道,如果是同一个进程调用的是Stub的方法,如果不同进程,调用的是Stub.Proxy里面的方法,以其中一个方法Stub.Proxy的callF1方法为例,分析如下。
首先创建输入参数_data和输出参数_reply,然后将该方法的参数写入_data,然后调用transact方法发送RPC(远程过程调用),同时线程挂起,然后服务端的onTransact会被调用,等到结果返回之后继续执行,将结果写入_reply中。

可以看见,AIDL是基于Binder机制实现的,通过AIDL自动生成的接口里面的Binder的子类实现远程接口调用,整个流程如下图所示。

注意:由于客户端在通过binder调用服务端的时候,会把线程挂起,因此,一般需要另开线程执行,如果在主线程执行,如果这个方法是耗时方法,会导致ANR情况。

调用流程图.png

三、深入理解Binder

上面从主要从应用层的角度分析AIDL,可以看出AIDL底层是基于Binder实现的,那么Binder底层原理是怎么样的呢?

首先,先理解进程内存空间的关系,如下图所示。

进程间地址空间关系.png

可以看见虽然不同进程的用户空间是非共享的,但是内核空间是共享的,Client进程向Server进程通信,恰恰是利用进程间可共享的内核内存空间来完成底层通信工作的。

Binder通信采用C/S架构,基本架构图如下图所示。

Binder架构图.jpg

Service Manager用于管理系统中的各种服务。图中Client/Server/ServiceManage之间的相互通信都是基于Binder机制,所以三个步骤都是C/S架构。

首先,注册服务,这个过程Server是客户端,ServiceManager是服务端。

然后,Client端进行服务获取,这个过程Client是客户端,ServiceManager是服务端。

最后,使用服务,Client根据得到的Service信息建立与Service所在的Server进程通信的通路,然后就可以直接与Service交互。该过程:client是客户端,server是服务端。

四、例子回顾

首先,启动AIDLServer应用后,MyService启动,向Service Manager注册。然后启动AIDLClient通过绑定服务获取对应的服务,服务连接成功之后,调用IMyAidlInterface.Stub.asInterface获取客户端所需的AIDL接口类型的对象,由于是不同的进程,因此会获取到对应的代理类。调用方法callF1方法的时候,会去调用对应service里面真正实现的callF1()。

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

推荐阅读更多精彩内容

  • 本文介绍Service与Activity之间的通信,文章包含以下内容: 一、Service基本用法 二、通过AID...
    developerzjy阅读 10,167评论 7 27
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,566评论 18 139
  • Jianwei's blog 首页 分类 关于 归档 标签 巧用Android多进程,微信,微博等主流App都在用...
    justCode_阅读 5,888评论 1 23
  • 有些人不必刻意联系, 只需一份惦记。 我一声,想你。 你一声,走起。 各自忙各自的, 掐好时间,就等你。 只愿老来...
    陈mini阅读 246评论 0 0
  • 人生中最难的可能就是坚持了。 我的腿已经不是我的腿了,我上楼都是四肢着地前行。能不抬腿尽量不抬,走路全靠拖。 话说...
    悬崖边上的金鱼公主阅读 236评论 1 0