RPC & AIDL

RPC & AIDL

一般创建的服务并不能被其他的应用程序访问。为了使其他的应用程序也可以访问本应用程序提供的服务,Android系统采用了远程过程调用(Remote Procedure Call,RPC)方式来实现。
Android使用AIDL来实现。

每一个进程都有自己的Dalvik VM实例,都有自己的一块独立的内存,都在自己的内存上存储自己的数据,执行着自己的操作,都在自己的那片狭小的空间里过完自己的一生。每个进程之间都你不知我,我不知你,就像是隔江相望的两座小岛一样,都在同一个世界里,但又各自有着自己的世界。而AIDL,就是两座小岛之间沟通的桥梁。相对于它们而言,我们就好像造物主一样,我们可以通过AIDL来制定一些规则,规定它们能进行哪些交流。

何时使用AIDL是必要的:只有你允许客户端从不同的应用程序为了进程间的通信而去访问你的service。

Binder RPC 机制

clipboard.png
1.client通过获得一个server的代理接口,对server进行直接调用;
2.代理接口中定义的方法与server中定义的方法是一一对应的;
3.client调用某个代理接口中的方法时,代理接口的方法会将client传递的参数打包成为Parcel对象;
4.代理接口将该Parcel发送给内核中的binder driver.
5.server会读取binder driver中的请求数据,如果是发送给自己的,解包Parcel对象,处理并将结果返回;
6.整个的调用过程是一个同步过程,在server处理的时候,client会block住。

应用层AIDL的使用方法

服务端步骤步骤
1.建立AIDL,自动生成java接口

Java包目录中建立一个扩展名为A.aidl的文件,并写下需要的接口。如果aidl文件的内容是正确的,ADT会在gen目录下自动生成一个A.Java接口文件。

2. 实现service

建立一个服务类(Service的子类)

public class MyService extends Service 

创建的服务类中创建一个内部类,实现由aidl文件生成的Java接口

MyServiceImpl 

服务类的onBind方法返回时,将实现aidl接口的内部类对象返回出去

return new MyServiceImpl();
public class MyService extends Service {        
@Override      
public IBinder onBind(Intent arg0) { 
    return new MyServiceImpl();  
}        
public class MyServiceImpl extends IMyService.Stub 
{      
    public String funcXXX
    {
    }

}  
    
}

3.AndroidManifest.xml文件中配置AIDL服务

<action>标签中android:name的属性值就是客户端要引用该服务的ID,也就是Intent类的参数值

客户端端步骤步骤
1.绑定服务
// 绑定远程服务端服务         
Intent serviceIntent = new Intent("name_XXX_refer_service_AndroidManifest");     

bindService(serviceIntent, mConnection, Context.BIND_AUTO_CREATE);

2.获得服务对象
IMyService mIMyService;

private ServiceConnection mConnection = new ServiceConnection() 
{            
public void onServiceConnected(ComponentName name, IBinder service) 
{              
mIMyService = IMyService.Stub.asInterface(service);
}
public void onServiceDisconnected(ComponentName name) 
{             
// 解除绑定    
mIMyService = null;     
} 
};

framework层 AIDL的使用方法

1. android/os目录下添加aidl
interface IDoorService
{
    void setCameraLight(boolean on);
    void door_open_close(int open_close);
}

编译会在out目录下生成Java文件

继承android.os.IInterface
里面有Stub类和自定义的方法,Stub类继承Binder

asInterface / Proxy 就是客户端使用

asBinder 就是service自己

/*
 * This file is auto-generated.  DO NOT MODIFY.
 * Original file: frameworks/base/core/java/android/os/IDoorService.aidl
 */
package android.os;
public interface IDoorService extends android.os.IInterface
{
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements android.os.IDoorService
{
private static final java.lang.String DESCRIPTOR = "android.os.IDoorService";
/** Construct the stub at attach it to the interface. */
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/**
 * Cast an IBinder object into an android.os.IDoorService interface,
 * generating a proxy if needed.
 */
public static android.os.IDoorService asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof android.os.IDoorService))) {
return ((android.os.IDoorService)iin);
}
return new android.os.IDoorService.Stub.Proxy(obj);
}
@Override public android.os.IBinder asBinder()
{
return this;
}
@Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
{
switch (code)
{
case INTERFACE_TRANSACTION:
{
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_setCameraLight:
{
data.enforceInterface(DESCRIPTOR);
boolean _arg0;
_arg0 = (0!=data.readInt());
this.setCameraLight(_arg0);
reply.writeNoException();
return true;
}
case TRANSACTION_door_open_close:
{
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
this.door_open_close(_arg0);
reply.writeNoException();
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements android.os.IDoorService
{
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote)
{
mRemote = remote;
}
@Override public android.os.IBinder asBinder()
{
return mRemote;
}
public java.lang.String getInterfaceDescriptor()
{
return DESCRIPTOR;
}
@Override public void setCameraLight(boolean on) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(((on)?(1):(0)));
mRemote.transact(Stub.TRANSACTION_setCameraLight, _data, _reply, 0);
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
@Override public void door_open_close(int open_close) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(open_close);
mRemote.transact(Stub.TRANSACTION_door_open_close, _data, _reply, 0);
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
}
static final int TRANSACTION_setCameraLight = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_door_open_close = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
}
public void setCameraLight(boolean on) throws android.os.RemoteException;
public void door_open_close(int open_close) throws android.os.RemoteException;
}


2.在android/server目录下实现 这个服务
package com.android.server;

import android.content.Context;
import com.android.server.LightsService;
import android.os.IDoorService;
import android.util.Slog;

public class DoorService extends IDoorService.Stub
{
    private static final String TAG = "DoorService";

    private int mPtr = 0;
    private LightsService mLightsService;
    private LightsService.Light mCameraLight;
     
    DoorService(Context context,LightsService ls)
    {
        mLightsService = ls;
        mCameraLight = mLightsService.getLight(LightsService.LIGHT_ID_CAMERA);
        mPtr = init_native();

        if(mPtr == 0)
            Slog.e(TAG, "Fail to initialize DoorService111");
    }

    public void door_open_close(int open_close)
    {
        if(mPtr == 0)
        {
            Slog.e(TAG, "DoorService is not initialize");
            return;
        }

        door_open_close_native(mPtr, open_close);
            
    }

        public void setCameraLight(boolean on) {
         LightsService.Light light;

            light = mCameraLight;

            light.setBrightness(on?255:0);
        }
    private static native int init_native();
        private static native void door_open_close_native(int ptr, int open_close);
}

DoorService没有继承Service
所以这时他不是一个Service.

它是在SystemServer中注册成为服务的。

       Slog.i(TAG, "Door Service");
       door = new DoorService(context,lights);
       ServiceManager.addService("door", door);

把DoorService对象(也就是Stub)作为一个Service注册给了ServiceManager。并且注册的名字是“door”

virtual status_t addService(const String16& name, const sp<IBinder>& service,
        bool allowIsolated)
{
    Parcel data, reply; //Parcel是数据通信包
    data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());   //写入RPC头信息
    data.writeString16(name);        // name为 "media.player"
    data.writeStrongBinder(service); // MediaPlayerService对象
    data.writeInt32(allowIsolated ? 1 : 0); // allowIsolated= false
    status_t err = remote()->transact(ADD_SERVICE_TRANSACTION, data, &reply); //【见流程4】
    return err == NO_ERROR ? reply.readExceptionCode() : err;
}

addService服务端在service_manager.c实现。

int do_add_service(struct binder_state *bs,uint16_t *s,unsigned len,void *ptr,unsigned uid,int allow_isolated)

ServiceManager是一个linux级的进程,Native层Service的管理员。

需要特别注意的是ServiceManager本身也是一个Service,他需要先向Binder注册自己,而且要把自己注册为“管理员”

serviceManager自己有binder.c直接和Binder驱动来通信,并且只有一个循环binder_loop来进行读取和处理事务,这样的好处是简单而高效。

serviceManager工作主要就两个:查询和注册服务。

int binder_become_context_manager(struct binder_state *bs)    
{              
return ioctl(bs->fd, BINDER_SET_CONTEXT_MGR, 0);    
}
3.获得这个服务
IDoorService.Stub.asInterface
    registerService("door", new ServiceFetcher() {  
            public Object createService(ContextImpl ctx) {  
                IBinder b = ServiceManager.getService("door");  
                return new DoorManager(ctx, IDoorService.Stub.asInterface(b));  
    }}); 
4.应用如何通过context来获取服务的
Context.getSystemService(name)

Context的实现类其实是:ContextImpl

在ContextImpl中定义如下一个HashMap,

private static final HashMap SYSTEM_SERVICE_MAP = new HashMap();

SYSTEM_SERVICE_MAP的初始化.
在ContextImp中,有一段static的代码块,用于初始化所有服务的ServiceFetcher,并且加入SYSTEM_SERVICE_MAP里面. 里面定义了createservice

registerService("door", new ServiceFetcher() {  
        public Object createService(ContextImpl ctx) {  
            IBinder b = ServiceManager.getService("door");  
            return new DoorManager(ctx, IDoorService.Stub.asInterface(b));  
}}); 

这是一个静态的/final的HashMap,其中保存的是每一个服务的ServiceFetcher.

public Object getSystemService(String name) {
    ServiceFetcher fetcher = SYSTEM_SERVICE_MAP.get(name);
    return fetcher == null ? null : fetcher.getService(this);
}

当通过getService()来获取服务对象的时候,首先判断这个服务对象是否已经存在:如果已经存在,则立即返回,否则调用他的一个虚的方法createService()来创建,并保存创建的这个服务对象

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

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 172,108评论 25 707
  • 用两张图告诉你,为什么你的 App 会卡顿? - Android - 掘金 Cover 有什么料? 从这篇文章中你...
    hw1212阅读 12,723评论 2 59
  • 原文链接: http://weishu.me/2016/01/12/binder-index-for-newer/...
    miniminiming阅读 725评论 1 6
  • 小编导读 十人九湿,寒湿缠身百病生,不少人体内都有湿气,多因喜食冷饮、贪吃生冷瓜果等寒凉之物,损伤人体阳气所致。有...
    阅读中医阅读 2,713评论 0 1
  • 开心吃西餐的一天,陪家人玩了一个下午还可以回家睡下午觉,感觉很幸福。 晚上醒来后,陪家人继续看电视到夜晚,有时候这...
    DeathKnightR阅读 101评论 0 0