Binder 之 内核剖析 读笔

注: 所有图片来自  内核剖析中!


Binder 意味 别针,回形针.  在Android中, Binder用于完成 进程间通讯(IPC)



Binder框架 : Binder是一种架构,  分为 服务端. Binder驱动,客户端3个模块,    一个Binder服务端就相当于一个Binder类对象,一旦该对象被创建,就会启动一个隐藏线程, 该线程会接受Binder驱动发来的消息, 收到消息后,会执行Binder的onTransact()函数. 并且按照参数执行不同的代码. So~, 要实现Binder 必须重载onTransact().



onTransact()函数主要是把 onTransact()函数中的参数转换为服务器的参数, 而onTransact()函数中的数据来源是客户端调用transact()时输入的. So~  transact 按一定格式输入的话.  onTransact()就有一定格式的输出.



Binder驱动: 任意一个服务端的Binder对象被创建时,同时会在Binder驱动中创建一个mRemote对象,mRemote类型也是Binder类,客户端要访问远程服务时,都是通过这个mRemote对象.




获取到mRemote对象后,调用其transact()方法. 在Binder驱动中,  mRemote也重载了 transact()方法,重载的内容如下.  1.以线程间通信的模式,像服务端发送客户端传送过来的参数.

 2.挂起当前线程, 当前线程正式客户端线程,并等待服务端线程执行完服务函数后通知(notify)

  3.接受到服务端线程通知,然后继续执行客户端线程,返回客户端代码块



关于服务端

 只要基于Binder类创建一个服务即可.



     重载onTransact方法,读取客户端传递过来的data参数,   但是我们如何读取data在变量中的位置?.

So~ 这就需要双方有个约定.



code变量用于标示客户端期望调用服务端的哪个函数,So~,双方需要约定一个 int值. 不同的值代表服务端不同函数.

该函数和 客户端transact()中第一个参数 code是一致的.

enforceInterface 校验作用,与客户端writeInterface对应.

readString()用于从包裹中取出一个字符串.

如果服务端想要返回数据,可以再返回包裹reply中调用Parcel的相关函数写入结果


Binder 客户端

 要想使用服务端,必须获得Binder驱动中的mRemote引用, 然后调用 transact()方法



其中data表示传递给远程Binder服务的包裹(Parcel),远程服务所需要的函数必须放在这个包裹中. 包裹中只能放入特定类型的变量, 如原子类型,String、int 、long等. 可以参考Parcel类查看支持的全部类型!

此外,Parcel还提供了一个writeParcel()方法,可以再包裹里面在套一个小包裹. 在进行远程Binder服务调用时,参数要么是原子类型, 要么是集成Parcel类. 否则不能进行传递.


对于服务端的 客户端而言,可以调用如下transact()方法.


     

      包裹我们是通过Parcel.obtain获取的.  reply用于 服务端返回数据  把数据放入reply包中.

    writeInterfaceToken 标注远程服务名称,该方法其实不是必须写的. 因为客户端已经获取了指定服务端 驱动的binder.

    该名称将作为Binder驱动确保客户端 的确像调用指定的服务端.

    writeString()方法用于向包裹内添加一个变量, 变量顺序是约定好的. 即 客户端会按约定好的顺序写数据, 服务端按约定顺序取数据

    接着调用mRemote对象的 transact()方法, 然后客户端进入Binder驱动,Binder驱动就会挂起当前线程. 发送消息给服务端, 消息内就包含了客户端传入的数据, 然后服务端接到消息 开启隐藏线程 执行 所对应的服务端函数, 执行完通知(notify)  Binder驱动,  从而使客户端线程从Binder驱动代码 返回到客户端代码区

      transace()最后一个参数 是IPC调用的模式,有2种, 参数分别为01.   0代表双向,既有返回值.  1单向,无返回值

      最后就可以从reply里面拿取 服务端返回的数据.


以上手写存在两个问题

1.如何获取Binder驱动的 mRemote引用,

2.客户端和服务端必须事先约定好规则. (1)服务端函数的参数在包裹中的顺序,(2)int类型函数标示.



如何获取mRemote引用 ?

1/  AMS(ActivityManagerService)提供了startService()函数用于启动服务.

 而客户端可以使用startService(Intent intent) 与 bindService(Intent service,ServiceConnection conn, int flags) 

 来与服务端进行连接, 该方法在android.app.ContextImpl内.


关于startService: 该intent用于启动指定服务, 但是还没有获得Binder引用, 所以还无法调用服务的功能

关于bindService: 该intent用于绑定一个服务, 而第二个参数 是一个interface 如下所示:


当客户端请求AMS启动 某个Service后,如Service正常启动,AMS就会调用ActivityThread类中的ApplicationThread对象,调用函数中会包含Service的Binder,然后再ApplicationThread中回调bindService 把Binder回传.. 我们再onServiceConected中 接受保存为全局变量即可, 在客户端中代码如下所示:

ServiceConnection conn = new ServiceConnection() {
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
    }

    @Override
    public void onServiceDisconnected(ComponentName name) {
    }
};


public void bindService(View v) {
    bindService(new Intent(MainActivity.this, MyServices.class), conn, Context.BIND_AUTO_CREATE);
}
其中ServiceConnection 是绑定后的回调,  一般onServiceDisconnected不会调用,除非异常终止。 最后一个参数是一个int类型的 flag, 这里我们写 Context.BIND_AUTO_CREATE 含义不说了,看名字就知道。

可以看到在onServiceConnected里面 返回了一个service. 这就是我们要用到的Binder(mRemote引用).



2/我们还可以使用SDK提供的aidl工具

aidl工具  会把 以.aidl为结尾的文件 转换成java类. 同时重载了 transact()和onTransact()方法.统一了包裹的读取顺序,让开发者把注意力放在服务代码本身上.

aidl使用:编写一个以.aidl为结尾的文件,内容如下所示


aidl语法与java类似,   也可以使用import关键字.  但是包裹内只能写入 Java原子类型,Binder引用,实现了Parcelable对象

interface为关键字,  我们也可以再 interface前面加上oneway,代表该service提供的方法都是没有返回值的.生成的代码这里就不贴了, 大家可以自行去尝试。



  在生成的代码中主要完成了以下三个事情

 1.定义个Java Interface类, 该类基于IInterface接口,即需提供一个asBinder()函数

 2.定义一个Proxy类,该类将作为客户端访问服务端的代理. (统一参数顺序)

 3.定义一个Stub类(abstract类),基于Binder,主要服务端来使用,并实现Java Interface类接口, 之所以定义为abstract 是因为函数,是因为具体服务函数必须有程序员自己来实现, Stub类中 重载了onTransact()方法,由于写入规则是aidl自己定义的, 那么读取顺序它自己子让也知道

Stub类中还定义了一些int常量, 这些常量与服务端函数所对应, code值即来源于此


Binder内部有一个queryLocalInterface(String ..)用于判断该binder对象是否是本地的引用



系统服务中的Binder对象

我们再应用中一般都是 用getSystemService来获取一个服务的. 那么这些系统服务的Binder如果传递给客户端的,首选我们要知道 系统服务并不是通过startService来开启的, getSystemService的具体实现是在ContextImpl中, 由于服务较多, 统一由ServiceManager来管理.



ServiceManager(独立线程)所管理的服务


ServiceManager 本身也是一个service,Framework 提供了一个函数 用于获取该 Service对应的binder引用. BinderInternal.getContextObject(). 该静态函数返回ServiceManager后,就可以通过ServiceManager提供的方法获取系统其它Service的binder引用.


下面我们来看看ContextImpl.getSystemService(),以INPUT_METHOD_SERVICE为例

@Override
public Object getSystemService(String name) {
    return SystemServiceRegistry.getSystemService(this, name);
}

这里调用了SystemServiceRegistry.geySystemService。由名可知, 该类是 系统服务注册表。

在它的静态代码块中 注册了很多系统服务,其中就有INPUT_METHOD_SERVICE

static {
    registerService(Context.ACCESSIBILITY_SERVICE, AccessibilityManager.class,
            new CachedServiceFetcher<AccessibilityManager>() {
        @Override
        public AccessibilityManager createService(ContextImpl ctx) {
            return AccessibilityManager.getInstance(ctx);
        }});

    registerService(Context.CAPTIONING_SERVICE, CaptioningManager.class,
            new CachedServiceFetcher<CaptioningManager>() {
        @Override
        public CaptioningManager createService(ContextImpl ctx) {
            return new CaptioningManager(ctx);
        }});

    registerService(Context.ACCOUNT_SERVICE, AccountManager.class,
            new CachedServiceFetcher<AccountManager>() {
        @Override
        public AccountManager createService(ContextImpl ctx) {
            IBinder b = ServiceManager.getService(Context.ACCOUNT_SERVICE);
            IAccountManager service = IAccountManager.Stub.asInterface(b);
            return new AccountManager(ctx, service);
        }});
registerService(Context.INPUT_SERVICE, InputManager.class,
        new StaticServiceFetcher<InputManager>() {
    @Override
    public InputManager createService() {
        return InputManager.getInstance();
    }});


 InputManager.getInstance(this) 关键代码如下

public static InputManager getInstance() {
    synchronized (InputManager.class) {
        if (sInstance == null) {
            IBinder b = ServiceManager.getService(Context.INPUT_SERVICE);
            sInstance = new InputManager(IInputManager.Stub.asInterface(b));
        }
        return sInstance;
    }
}

 通过ServiceManager 获取服务的 Binder对象. 在将该对象作为asInterface的参数,返回统一接口.

 下面来看getService.

public static IBinder getService(String name) {
    try {
        IBinder service = sCache.get(name);
        if (service != null) {
            return service;
        } else {
            return getIServiceManager().getService(name);
        }
    } catch (RemoteException e) {
        Log.e(TAG, "error in getService", e);
    }
    return null;
}

先从sCache缓存中查看是否有对应的Binder对象,有则返回, 没有调用getIServiceManager.getService(name).

getIServiceManager用于返回系统中唯一的ServiceManager对应的Binder:

 在接下来调用 Binder对象的getService 返回对应的Binder对象.


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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值