Android点将台:金科玉律[-AIDL-]

栏目: Android · 发布时间: 5年前

内容简介:在本文通过AIDL实现了跨进程间调用Service(即App2调用App1的Service)本篇将来探索一下AIDL自动生成的类,再重新审视一下

Android点将台:绝命暗杀官[-Service-] 的最后一小节,本文图例见

本文通过AIDL实现了跨进程间调用Service(即App2调用App1的Service)

本篇将来探索一下AIDL自动生成的类,再重新审视一下 ActivityManagerService

一、上一篇中AIDL自动生成的IMusicPlayerService分析

1.IMusicPlayerService的类结构

太多了,有点晃眼,打开结构图来分析一下

Android点将台:金科玉律[-AIDL-]
Android点将台:金科玉律[-AIDL-]

2.从IInterface开始

IMusicPlayerService是一个接口,并且继承于IInterface接口,那IInterface是何许人也?

就这两行代码,只有一个asBinder方法,返回一个IBinder对象,注意它在的包是 android.os

package android.os;
public interface IInterface{
    public IBinder asBinder();
}
复制代码

3.IMusicPlayerService的分析

可见除去Stub,IMusicPlayerService里只是我们在 IMusicPlayerService.aidl 定义的接口

也没有什么太多要说明的地方,我们的终极目标是看一下它的实现类是什么

/*
 * This file is auto-generated.  DO NOT MODIFY.
    自动生成的,不要修改
 * Original file: J:\\Java\\Android\\LeverUp\\TolyService\\app\\src\\main\\aidl\\com\\toly1994\\tolyservice\\IMusicPlayerService.aidl
 原始文件在: J:\\Java\\Android\\LeverUp\\TolyService\\app\\src\\main\\aidl\\com\\toly1994\\tolyservice\\IMusicPlayerService.aidl
 */
package com.toly1994.tolyservice;
// Declare any non-default types here with import statements
//在这里使用import语句声明任何非默认类型

public interface IMusicPlayerService extends android.os.IInterface {
    //Stub类,暂略...
    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
      说明一些在AIDL中可以用作参数和返回值的基本类型。
     */
    public void stop() throws android.os.RemoteException;
    public void pause() throws android.os.RemoteException;
    public void start() throws android.os.RemoteException;
    public void prev() throws android.os.RemoteException;
    public void next() throws android.os.RemoteException;
    public void release() throws android.os.RemoteException;
    public boolean isPlaying() throws android.os.RemoteException;
    public void seek(int pre_100) throws android.os.RemoteException;
    public void create(java.util.List<java.lang.String> filePaths) throws android.os.RemoteException;
}
复制代码

4.然后当然是深入一下,到 Stub

英语Stub有 存根 的意思,看完本篇之后自己意会一下它的含义

它不仅继承Binder,而且实现了IMusicPlayerService,看起来挺牛的,可惜是个抽象类

它实现了Binder的一些东西,但并没有IMusicPlayerService的任何方法,就是挂个头而已

实现从成员变量来看,每个方法都对应了一个int常量,从switch来看应该是方法的标识

DESCRIPTOR 对应了一个 包名.IMusicPlayerService 字符串,用于 attachInterface

Android点将台:金科玉律[-AIDL-]
public static abstract class Stub extends android.os.Binder implements com.toly1994.tolyservice.IMusicPlayerService {
     private static final java.lang.String DESCRIPTOR = "com.toly1994.tolyservice.IMusicPlayerService";

     /**
      * Construct the stub at attach it to the interface.
        在将关联到接口时构造stub。----注意使用了DESCRIPTOR字符串
      */
     public Stub() {
         this.attachInterface(this, DESCRIPTOR);
     }

     /**
      * Cast an IBinder object into an com.toly1994.tolyservice.IMusicPlayerService interface,
      * generating a proxy if needed.
      */
     public static com.toly1994.tolyservice.IMusicPlayerService asInterface(android.os.IBinder obj) {
         if ((obj == null)) {
             return null;
         }
         android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
         if (((iin != null) && (iin instanceof com.toly1994.tolyservice.IMusicPlayerService))) {
             return ((com.toly1994.tolyservice.IMusicPlayerService) iin);
         }
         return new com.toly1994.tolyservice.IMusicPlayerService.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 {
         java.lang.String descriptor = DESCRIPTOR;
         switch (code) {
             case INTERFACE_TRANSACTION: {
                 reply.writeString(descriptor);
                 return true;
             }
             case TRANSACTION_stop: {//暂略...
                 return true;
             }
             case TRANSACTION_pause: {//暂略...
                 return true;
             }
             case TRANSACTION_start: {//暂略...
                 return true;
             }
             case TRANSACTION_prev: {//暂略...
                 return true;
             }
             case TRANSACTION_next: {//暂略...
                 return true;
             }
             case TRANSACTION_release: {//暂略...
                 return true;
             }
             case TRANSACTION_isPlaying: {//暂略...
                 return true;
             }
             case TRANSACTION_seek: {//暂略...
                 return true;
             }
             case TRANSACTION_create: {//暂略...
                 return true;
             }
             default: {
                 return super.onTransact(code, data, reply, flags);
             }
         }
     }
     
     //Proxy类,暂略...
     static final int TRANSACTION_stop = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
     static final int TRANSACTION_pause = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
     static final int TRANSACTION_start = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
     static final int TRANSACTION_prev = (android.os.IBinder.FIRST_CALL_TRANSACTION + 3);
     static final int TRANSACTION_next = (android.os.IBinder.FIRST_CALL_TRANSACTION + 4);
     static final int TRANSACTION_release = (android.os.IBinder.FIRST_CALL_TRANSACTION + 5);
     static final int TRANSACTION_isPlaying = (android.os.IBinder.FIRST_CALL_TRANSACTION + 6);
     static final int TRANSACTION_seek = (android.os.IBinder.FIRST_CALL_TRANSACTION + 7);
     static final int TRANSACTION_create = (android.os.IBinder.FIRST_CALL_TRANSACTION + 8);
 }
复制代码

5.现在看最后一层奶油夹心: Proxy

现在把聚光灯打到Stub的内部类Proxy上, Proxy 是个敲代码的都知道是 代理 的意思

既然是代理,它代理什么?成员变量有个 IBinder mRemote ,构造方法要传一个IBinder,so...

它实现了 IMusicPlayerService ,并且有所动作,而且动作基本一致

Android点将台:金科玉律[-AIDL-]
private static class Proxy implements com.toly1994.tolyservice.IMusicPlayerService {
    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;
    }

    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    @Override
    public void stop() throws android.os.RemoteException {
        android.os.Parcel _data = android.os.Parcel.obtain();
        android.os.Parcel _reply = android.os.Parcel.obtain();
        try {
            _data.writeInterfaceToken(DESCRIPTOR);
            mRemote.transact(Stub.TRANSACTION_stop, _data, _reply, 0);
            _reply.readException();
        } finally {
            _reply.recycle();
            _data.recycle();
        }
    }

    @Override
    public void pause() throws android.os.RemoteException {//暂略...
    }

    @Override
    public void start() throws android.os.RemoteException {//暂略...
    }

    @Override
    public void prev() throws android.os.RemoteException {//暂略...
    }

    @Override
    public void next() throws android.os.RemoteException {//暂略...
    }

    @Override
    public void release() throws android.os.RemoteException {//暂略...
    }

    @Override
    public boolean isPlaying() throws android.os.RemoteException {//暂略...
    }

    @Override
    public void seek(int pre_100) throws android.os.RemoteException {//暂略...
    }

    @Override
    public void create(java.util.List<java.lang.String> filePaths) throws android.os.RemoteException {//暂略...
    }
}
复制代码

到现在为止,它们几个的关系理清了,下面就来详细看看哪些 //暂略... 的方法

二、Binder和IBinder的个别方法

这里先列举出上面使用到的方法

Android点将台:金科玉律[-AIDL-]

1.目前IBinder台面上出现的方法

public interface IBinder {
    /**
     * Attempt to retrieve a local implementation of an interface for this Binder object.  
     If null is returned, you will need to instantiate a proxy class to marshall calls through the transact() method.
     尝试检索此绑定器对象的本地接口实现。
     如果返回null,则需要通过transact()方法实例化一个代理类来调用。
     */
    public @Nullable IInterface queryLocalInterface(@NonNull String descriptor);

    /**
     * Perform a generic operation with the object.
     * 使用对象执行一般操作。
     * @param code The action to perform.  This should be a number between {@link #FIRST_CALL_TRANSACTION} and {@link #LAST_CALL_TRANSACTION}.
    code 操作的行为码  0x00000001 ~ 0x00ffffff 之间 1~16777215
     * @param data Marshalled data to send to the target.  Must not be null.If you are not sending any data, you must create an empty Parcel that is given here.
    data 发送到目标的编组数据。不能为空。如果您没有发送任何数据,则必须创建这里给出的空包。
     * @param reply Marshalled data to be received from the target.  May be null if you are not interested in the return value.
     从目标接收到的编组数据。如果您对返回值不感兴趣,则可能为null。
     * @param flags Additional operation flags.  Either 0 for a normal RPC, or {@link #FLAG_ONEWAY} for a one-way RPC.
   flags  额外的操作标志,对于普通RPC为0,对于单向RPC为1{@link #FLAG_ONEWAY}。
    
     * @return Returns the result from {@link Binder#onTransact}.  A successful call generally returns true; false generally means the transaction code was not understood.
   return  从Binder.onTransact()返回结果。成功调用通常返回true;false通常表示事务代码未被理解。
     */
    public boolean transact(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags)
        throws RemoteException;
    //其他...略
}
复制代码

2.目前Binder台面上出现的方法

---->[Binder]-------------
private IInterface mOwner;
private String mDescriptor;

---->[Binder#attachInterface]-------------
 Convenience method for associating a specific interface with the Binder. 
 After calling, queryLocalInterface() will be implemented for you to return
 the given owner IInterface when the corresponding descriptor is requested.
|--译:一个将特定接口与Binder关联的方便方法。调用后,将为你实现queryLocalInterface(),
|--以便在请求相应的描述符时返回owner (IInterface对象)。
public void attachInterface(@Nullable IInterface owner, @Nullable Stringdescriptor) {
    mOwner = owner;
    mDescriptor = descriptor;
}

---->[Binder#queryLocalInterface]-------------
 Use information supplied to attachInterface() to return the associated 
 IInterface if it matches the requested descriptor.
 如果提供的描述符,与通过attachInterface()方法进行关联的IInterface一致
 返回该IInterface,否则返回null
public @Nullable IInterface queryLocalInterface(@NonNull String descriptor) {
    if (mDescriptor.equals(descriptor)) {
        return mOwner;
    }
    return null;
}

---->[Binder#onTransact]-------------
 * Default implementation is a stub that returns false.  You will want
 * to override this to do the appropriate unmarshalling of transactions.
 * 默认实现是stub返回false。您会想要覆谢它,以便对事务进行适当的反编组。
 * <p>If you want to call this, call transact().
protected boolean onTransact(int code, @NonNull Parcel data, @Nullable Parcel
reply,int flags) throws RemoteException {
复制代码

3.再来看刚才暂略的方法

3.1:attachInterface

此方法在Stub的构造函数中调用,为成员变量 mOwnermDescriptor 赋值

Android点将台:金科玉律[-AIDL-]
---->[IMusicPlayerService.Stub#Stub]-------------
public Stub() {
    this.attachInterface(this, DESCRIPTOR);
}
|---这里调用了attachInterface方法,参数是自己和描述符

---->[Binder]-------------
private IInterface mOwner;
private String mDescriptor;

---->[Binder#attachInterface]-------------
public void attachInterface(@Nullable IInterface owner, @Nullable String descriptor) {
    mOwner = owner;
    mDescriptor = descriptor;
}
复制代码

3.2:asInterface

传入一个IBinder对象obj,调用obj的queryLocalInterface方法生成IInterface对象

再进行强转,如果强转不成功,才会创建Proxy代理对象

Android点将台:金科玉律[-AIDL-]
---->[IMusicPlayerService.Stub#asInterface]-------------
public static com.toly1994.tolyservice.IMusicPlayerService asInterface(android.os.IBinder obj) {
    if ((obj == null)) {
        return null;
    }
    android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
    if (((iin != null) && (iin instanceof com.toly1994.tolyservice.IMusicPlayerService))) {
        return ((com.toly1994.tolyservice.IMusicPlayerService) iin);
    }
    return new com.toly1994.tolyservice.IMusicPlayerService.Stub.Proxy(obj);
}
复制代码

3.3: asBinder

asBinder是 IInterface 接口的方法,把自己(Stub继承自Binder)返回去,没什么好说的

---->[IMusicPlayerService.Stub#asInterface]-------------
@Override
public android.os.IBinder asBinder() {
    return this;
}
复制代码

3.4: onTransact

此方法运行在服务端Binder线程池中,客户端发起跨进程请求时,远程请求通过系统底层封装后交由此方法处理。

处理的逻辑基本上都一样,通过判断code来调用方法,这里看两个 startseek ,

Android点将台:金科玉律[-AIDL-]
@Override
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel
reply, int flags) throws android.os.RemoteException {
    java.lang.String descriptor = DESCRIPTOR;
    switch (code) {
    //略...
        case TRANSACTION_start: {
            data.enforceInterface(descriptor);//执行接口
            this.start();//调用start方法
            reply.writeNoException();//将计算结果写入reply中
            return true;
        }
        case TRANSACTION_seek: {
            data.enforceInterface(descriptor);//执行接口
            int _arg0;
            _arg0 = data.readInt();//获取数据
            this.seek(_arg0);//调用seek方法
            reply.writeNoException();//将计算结果写入reply中
            return true;
        }
  //略...
复制代码

3.5:Proxy中的实现方法

Proxy是代理类,运行在客户端,也就是Stub#asInterface中iin

无法强转成IMusicPlayerService是返回Proxy类对象

---->[IMusicPlayerService.Stub.Proxy#start]--------
@Override
public void start() throws android.os.RemoteException {
    android.os.Parcel _data = android.os.Parcel.obtain();
    android.os.Parcel _reply = android.os.Parcel.obtain();
    try {
         //向_data中写入参数
        _data.writeInterfaceToken(DESCRIPTOR);
        //通过transact方法向服务端传递参数,并写入_reply
        mRemote.transact(Stub.TRANSACTION_start, _data, _reply, 0);
        _reply.readException();
    } finally {
        _reply.recycle();
        _data.recycle();
    }
}

@Override
public void seek(int pre_100) 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(pre_100);
        mRemote.transact(Stub.TRANSACTION_seek, _data, _reply, 0);
        _reply.readException();
    } finally {
        _reply.recycle();
        _data.recycle();
    }
}
复制代码

4.现在再来看我们在Service中画的最后一张图

ServiceConnection的onServiceConnected方法会回调一个IBinder对象 service

并通过 IMusicPlayerService.Stub.asInterface 方法将service变成IMusicPlayerService对象

还记得asInterface里做了什么吗?下面再看一遍,aidl在使用层面的逻辑是不是清晰了一些?

这里要强调一下:
图中:蓝色区域可在任意app中使用,相当于客户端:只要连接就行了   
其他区域是服务端的逻辑,为客户端提供业务逻辑处理 
复制代码
Android点将台:金科玉律[-AIDL-]
Android点将台:金科玉律[-AIDL-]

至于Binder在底层怎么跑的,那是后话,那根钻石级的硬骨头还是最后啃吧

5.小结一下:现在回头再看

1.在一个aidl的服务中,业务逻辑的处理是在哪的?
|--- 这还用问吗,当然在MusicPlayerStub里面,不然还能在哪?

2.IMusicPlayerService和Stub有什么用
|--- IMusicPlayerService是可调用的接口
|--- Stub在台面上有一个功劳,用asInterface将IBinder对象转化为IMusicPlayerService对象

3.Proxy有什么用?
|---Proxy唯一出现的是在asInterface方法中,在inn无法强转成IMusicPlayerService时使用Proxy  

4.什么时候inn无法转成IMusicPlayerService?(见下图)
|--- 我做了一个实验,就是分别看一下客户端和服务端绑定时回调的IBinder对象类型
再调用queryLocalInterface方法得到inn,看一下它的类型
服务端是:MusicPlayerStub ,它何许人也? Stub之子,Stub实现了IMusicPlayerService,强转ok  
客户端是:BinderProxy对象queryLocalInterface后为null,使用Proxy  
复制代码
Android点将台:金科玉律[-AIDL-]
Android点将台:金科玉律[-AIDL-]

三、ActivityManagerService初探

1.对于系统级跨进程通信的Service如何分析

当我们研究源码时遇到这种代码应该怎么分析?(如ActivityManagerNative)
可以看出无论Stub和Proxy或Binder对代码的逻辑性的分析并没有太大的用处,
他们只是实现类跨进程通信的机制,这种机制和逻辑相关性不是非常大

IXXXService只是规定接口方法,需要了解一下,着重的业务逻辑全在XXXXStub的实现类当中,
一般XXXXStub是作为XXXXService的内部类,也就是说看到有关aidl的系统服务源码时,
直接分析XXXXService就行了,逻辑全在那里
复制代码

2.AIDL为何而生

|--- 一个字:懒...   
自动生成的那个类,自己敲出来也能跑,既然能自动生成,那就生成呗,AIDL就出来了
这样就简化了跨进程间通信,但是凡事都有两面性,一旦简化了,就很难玩花样
Binder可以很复杂,IBinder还有很多控制的方法,所以AIDL只是最简单的跨进程间通信机制  
就像快速生产的板凳和精心雕刻的板凳,虽然都能坐,但价值是天差地别的
复制代码

3.ActivityManagerNative分析

还记得第一篇中Activity启动时出场的ActivityManagerNative吗?

这就是传说中的 ActivityManagerNative 你可以和最上面的 IMusicPlayerService 对比一下

Android点将台:金科玉律[-AIDL-]

4.ActivityManagerNative的幕后大佬

一般来说都是调用 ActivityManagerNative.getDefault() 生成一个IActivityManager对象

然后调用IActivityManager的相应抽象方法,那具体的实现类是谁?

ActivityManagerNative的作用相当于上面的Stub,需要找到一个继承他的类

这便是 ActivityManagerService ,注意它并不是Service类对象,而是服务的核心逻辑

它的价值也就相当于我们上面的 MusicPlayerStub ,不是Service,却更似服务

Android点将台:金科玉律[-AIDL-]

5. ActivityManagerNative.getDefault() 方法说了什么?

返回一个IActivityManager对象,实际类型为 ActivityManagerService

Android点将台:金科玉律[-AIDL-]
---->[ActivityManagerNative#getDefault]--------------------
static public IActivityManager对象 getDefault() {
    return gDefault.get();
}

//|--这里通过gDefault成员变量的get()方法获取了一个IActivityManager对象
|----现在焦点在gDefault身上,来看一下他是什么

---->[ActivityManagerNativegetDefault.gDefault]--------------------
private static final Singleton<IActivityManager> gDefault = new Singleton<IActivityManager>() {
    protected IActivityManager create() {
        //通过ServiceManager获取activity的IBinder对象
        IBinder b = ServiceManager获取.getService("activity");
        if (false) {
            Log.v("ActivityManager", "default service binder = " + b);
        }
        //asInterface方法使用IBinder对象,获取IActivityManager对象
        IActivityManager am = asInterface(b);
        if (false) {
            Log.v("ActivityManager", "default service = " + am);
        }
        return am;
    }
};

//|--gDefault的数据类型是Singleton<IActivityManager>,有点意思,看一下Singleton类
----单例辅助类啊!create()方法用于穿件对象,get方法获取单例对象,但它是@hide的,我们不能用
public abstract class Singleton<T> {
    private T mInstance;
    protected abstract T create();
    public final T get() {
        synchronized (this) {
            if (mInstance == null) {
                mInstance = create();
            }
            return mInstance;
        }
    }
}

---->[ActivityManagerNativegetDefault.asInterface]--------------------
static public IActivityManager asInterface(IBinder obj) {
    if (obj == null) {
        return null;
    }
    //如果obj(即Bundle) queryLocalInterface 不为空,就使用in
    IActivityManager in =
        (IActivityManager)obj.queryLocalInterface(descriptor);
    if (in != null) {
        return in;
    }
    //否则返回ActivityManagerProxy对象
    return new ActivityManagerProxy(obj);
}

---->[ActivityManagerNativegetDefault$ActivityManagerProxy]--------------------
class ActivityManagerProxy implements IActivityManager{
    public ActivityManagerProxy(IBinder remote){
        mRemote = remote;
    }
    public IBinder asBinder(){
        return mRemote;
    }
    //略...
        public int broadcastIntent(IApplicationThread caller,
            Intent intent, String resolvedType, IIntentReceiver resultTo,
            int resultCode, String resultData, Bundle map,
            String[] requiredPermissions, int appOp, Bundle options, boolean serialized,
            boolean sticky, int userId) throws RemoteException
    //略...
    }
复制代码

这样使用 ActivityManagerNative.getDefault() 便可以获得一个IActivityManager对象,

该对象的实现类型为ActivityManagerService,也就是传说中的AMS,

所以在看源码时 ActivityManagerNative.getDefault() ,就相当于看到了AMS

后记:捷文规范

1.本文成长记录及勘误表

项目源码 日期 附录
V0.1--无 2018-2-25

发布名: Android点将台:金科玉律[-AIDL-]
捷文链接: juejin.im/post/5c4a7e…

2.更多关于我

笔名 QQ 微信
张风捷特烈 1981462002 zdl1994328

我的github: github.com/toly1994328

我的简书: www.jianshu.com/u/e4e52c116…

我的简书: www.jianshu.com/u/e4e52c116…

个人网站:www.toly1994.com

3.声明

1----本文由张风捷特烈原创,转载请注明

2----欢迎广大编程爱好者共同交流

3----个人能力有限,如有不正之处欢迎大家批评指证,必定虚心改正

4----看到这里,我在此感谢你的喜欢与支持

Android点将台:金科玉律[-AIDL-]

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

入侵的艺术

入侵的艺术

Kevin D.Mitnick、William L.Simon / 袁月杨、谢衡 / 清华大学 / 2007-1 / 39.00元

《入侵的艺术》中四个志同道合的伙伴使用口袋大小的计算机在拉斯维加斯大把挣钱。一个无聊的加拿大小伙子居然能够非法访问南部的一家银行。几个年轻人被拉登的恐怖分子征召去攻击Lockheed Martin公司和防御信息系统网络。   [精彩试读一]   [精彩试读二]一起来看看 《入侵的艺术》 这本书的介绍吧!

SHA 加密
SHA 加密

SHA 加密工具

html转js在线工具
html转js在线工具

html转js在线工具

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具