关闭

AIDL源码分析

239人阅读 评论(0) 收藏 举报
分类:

前言

本文是本人研究AIDL时候的笔记,包含很多UML图和截图,内容仓促且不包含驱动层分析,如下文有错漏还请指出(容我精通Linux和C++后杀入,很可惜现在太菜)

服务端

  • 首先写一个AIDL文件 如下:
// IMyAidlInterface.aidl
package com.fmy.changevoice.aidl_resource;



interface IMyAidlInterface {

    void test1(int anInt, long aLong, boolean aBoolean, float aFloat,
            double aDouble, String aString);

                String hello( String aString);
}
  • 在编译后生成一个java文件(as位于build/generated/source/aidl/debug),文件内容如下:

package com.fmy.changevoice.aidl_resource;

public interface IMyAidlInterface extends android.os.IInterface {

    public static abstract class Stub extends android.os.Binder implements com.fmy.changevoice.aidl_resource.IMyAidlInterface {
        private static final java.lang.String DESCRIPTOR = "com.fmy.changevoice.aidl_resource.IMyAidlInterface";


        public Stub() {
            this.attachInterface(this, DESCRIPTOR);
        }


        public static com.fmy.changevoice.aidl_resource.IMyAidlInterface asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.fmy.changevoice.aidl_resource.IMyAidlInterface))) {
                return ((com.fmy.changevoice.aidl_resource.IMyAidlInterface) iin);
            }
            return new com.fmy.changevoice.aidl_resource.IMyAidlInterface.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_test1: {
                    data.enforceInterface(DESCRIPTOR);
                    int _arg0;
                    _arg0 = data.readInt();
                    long _arg1;
                    _arg1 = data.readLong();
                    boolean _arg2;
                    _arg2 = (0 != data.readInt());
                    float _arg3;
                    _arg3 = data.readFloat();
                    double _arg4;
                    _arg4 = data.readDouble();
                    java.lang.String _arg5;
                    _arg5 = data.readString();
                    this.test1(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5);
                    reply.writeNoException();
                    return true;
                }
                case TRANSACTION_hello: {
                    data.enforceInterface(DESCRIPTOR);
                    java.lang.String _arg0;
                    _arg0 = data.readString();
                    java.lang.String _result = this.hello(_arg0);
                    reply.writeNoException();
                    reply.writeString(_result);
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }

        private static class Proxy implements com.fmy.changevoice.aidl_resource.IMyAidlInterface {
            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 test1(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) 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(anInt);
                    _data.writeLong(aLong);
                    _data.writeInt(((aBoolean) ? (1) : (0)));
                    _data.writeFloat(aFloat);
                    _data.writeDouble(aDouble);
                    _data.writeString(aString);
                    mRemote.transact(Stub.TRANSACTION_test1, _data, _reply, 0);
                    _reply.readException();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }

            @Override
            public java.lang.String hello(java.lang.String aString) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                java.lang.String _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    _data.writeString(aString);
                    mRemote.transact(Stub.TRANSACTION_hello, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.readString();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }
        }

        static final int TRANSACTION_test1 = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
        static final int TRANSACTION_hello = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
    }

    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    public void test1(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException;

    public java.lang.String hello(java.lang.String aString) throws android.os.RemoteException;
}
  • 先看一下类图结构示例图:

这里写图片描述

  • 首先分析IMyAidlInterface这个类(内部类先不分析):
public interface IMyAidlInterface extends android.os.IInterface {
//内部类先不做分析
 public static abstract class Stub extends android.os.Binder implements com.fmy.changevoice.aidl_resource.IMyAidlInterface{...}
//声明接口方法
public void test1(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException;
//声明接口方法
    public java.lang.String hello(java.lang.String aString) throws android.os.RemoteException;
}
  • 我们看到IMyAidlInterface 还继承类一个接口IInterface ,我们再看看这个接口:
package android.os;

public interface IInterface
{


//检索与此接口相关联的Binder对象。您必须使用此代替普通转换,以便代理对象可以返回正确的结果。
    public IBinder asBinder();
}
  • 从描述可得治会在代理对象用使用使用此方法。然而IMyAidlInterface 没有实现此方法。我们继续看内部类Stub。
    这里写图片描述

  • 分析1:Stub是一个抽象类。继承Binder和实现IMyAidlInterface接口。
    但是从类图可以看到并没有实现我们IMyAidlInterface的方法(test1和hello方法)。

综上分析,我们服务实现接口的时候我们会继承IMyAidlInterface.Stub类。而不是直接IMyAidlInterface因为此接口还继承了IInterface接口 还必须实现其IBinder asBinder();方法。但是这个方法怎么实现返回我们却不知道。好在Stub实现除了我们自己定义接口方法之外IMyAidlInterface类的所有继承方法。

也就是说IMyAidlInterface我们定义了两个方法test1()和hello()没有被Stub实现,但是IMyAidlInterface其继承IInterface方法我们实现了。

所以我们写一个服务如下:

////MyService.java
package com.fmy.changevoice.aidl_resource;

public class MyService extends Service {



    @Override
    public IBinder onBind(Intent intent) {

        return new MyAidlInterface();
    }

    @Override
    public void onRebind(Intent intent) {
        super.onRebind(intent);
    }

    class MyAidlInterface extends IMyAidlInterface.Stub {



        @Override
        public void test1(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {

        }

        @Override
        public String hello(String aString) throws RemoteException {
            return null;
        }
    }
}
  • 分析2: 我们再看一下我们Stub类实例化的过程.在上面的Myservice中我们在实例化内部类MyAidlInterface 的时候会调用父类无参构造方法。所以我们从Stub的构造方法开始看。
 private static final String DESCRIPTOR = "com.fmy.changevoice.aidl_resource.IMyAidlInterface";

        public Stub() {
            this.attachInterface(this, DESCRIPTOR);
        }

构造方法调用attachInterface传入this和一个描述。而attachInterface属于Binder方法
继续跟进

 //将特定接口与Binder关联的便捷方法。
 //调用后,将为您实现queryLocalInterface()
 //返回给定的所有者IInterface时相应的
 //描述符被请求。
    public void attachInterface(IInterface owner, String descriptor) {
        mOwner = owner;
        mDescriptor = descriptor;
    }

DESCRIPTOR 字符串就是一个id一样 用于确定一个接口对应的Binder对象。而Binder对象是IBinder子类用于内存共享实现进程通信.当我们使用IInterface接口的asBinder()方法时候会根据descriptor返回对应IBinder对象

既然上文提到了queryLocalInterface ()那么我们顺便看看这个方法。
这个方法在Binder实现继承自IBinder

//使用提供给attachInterface()的信息来返回
关联的IInterface。
 public IInterface queryLocalInterface(String descriptor) {
        if (mDescriptor.equals(descriptor)) {
            return mOwner;
        }
        return null;
    }

mOwner对象是Binder属性对象,就是我们attachInterface()传入的。回过头来再看看我们怎么调用attachInterface()的传入实参

 public Stub() {
            this.attachInterface(this, DESCRIPTOR);
        }

可见调用queryLocalInterface()方法后直接返回如果字符串匹配那么直接返回Stub对象

Stub父类的构造方法Binder如下

public Binder() {
        init();

        if (FIND_POTENTIAL_LEAKS) {
            final Class<? extends Binder> klass = getClass();
            if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
                    (klass.getModifiers() & Modifier.STATIC) == 0) {
                Log.w(TAG, "The following Binder class should be static or leaks might occur: " +
                    klass.getCanonicalName());
            }
        }
    }
  private native final void init();

和可惜主要的init()方法是native方法,所以暂时到这


客户端

我们连接Myservice这个服务类代码如下

//MainActivity.java
package com.fmy.changevoice.aidl_resource;


public class MainActivity extends AppCompatActivity {

    private static final String TAG = "FMY";

    @Override
    protected void onStart() {
        super.onStart();
        Log.e(TAG, "onStart: ");
    }


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //连接核心代码
        Intent intent = new Intent(this, MyService.class);
        bindService(intent, new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                Log.e(TAG, "onServiceConnected: ");
               //32行 
                IMyAidlInterface.Stub.asInterface(service);

               try {
                    //调用接口中的一个方法. //36行
                    String test = iMyAidlInterface.hello("test");
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }

            @Override
            public void onServiceDisconnected(ComponentName name) {

            }
        }, BIND_AUTO_CREATE);
    }
}

我们来先分析一下32行代码:
我们看看调用Stub的方法asInterface

     /**
        将IBinder对象转换为com .fmy. changevoice.aidl_resource.IMyAidlInterface接口,在需要时生成代理。
         */
        public static com.fmy.changevoice.aidl_resource.IMyAidlInterface asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            //30行
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.fmy.changevoice.aidl_resource.IMyAidlInterface))) {
                return ((com.fmy.changevoice.aidl_resource.IMyAidlInterface) iin);
            }
            //34行
            return new com.fmy.changevoice.aidl_resource.IMyAidlInterface.Stub.Proxy(obj);
        }

我们先分析30行

//30行
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);

queryLocalInterface方法在前面我们大致介绍了下,用你传入的DESCRIPTOR检索接口和binder相关的接口实现。如果你的ServiceClient在一个同一个进程那么直接返回本地对象,不需要进程间通信。否则返回空

如果返回本地对象那么后面也没有什么介绍了,我们假设这里ServiceClient不在同一进程。

34行

  return new com.fmy.changevoice.aidl_resource.IMyAidlInterface.Stub.Proxy(obj);

可以看到假设不在同进程的时候用Proxy类创建返回IMyAidlInterface接口的实现

我们来大致看看Proxy类

这里写图片描述

ProxyStub静态私有内部类代码如下

   private static class Proxy implements com.fmy.changevoice.aidl_resource.IMyAidlInterface {
            private android.os.IBinder mRemote;

            Proxy(android.os.IBinder remote) {
                mRemote = remote;
            }

            @Override
            public android.os.IBinder asBinder() {
                return mRemote;
            }

            public String getInterfaceDescriptor() {
                return DESCRIPTOR;
            }

            /**
             * Demonstrates some basic types that you can use as parameters
             * and return values in AIDL.
             */
            @Override
            public void test1(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) 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(anInt);
                    _data.writeLong(aLong);
                    _data.writeInt(((aBoolean) ? (1) : (0)));
                    _data.writeFloat(aFloat);
                    _data.writeDouble(aDouble);
                    _data.writeString(aString);
                    mRemote.transact(Stub.TRANSACTION_test1, _data, _reply, 0);
                    _reply.readException();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }

            @Override
            public String hello(String aString) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                String _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    _data.writeString(aString);
                    mRemote.transact(Stub.TRANSACTION_hello, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.readString();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }
        }

我们先把精力放在Proxy构造方法上

  private static class Proxy implements com.fmy.changevoice.aidl_resource.IMyAidlInterface {
            private android.os.IBinder mRemote;
            //当Client和Service不在同进程的时候 传入Stub
            Proxy(android.os.IBinder remote) {
                mRemote = remote;
            }
}

可见当ClientSerivice不在同进程的时候 ,Stub把自身传给Proxy构造对象 然后返回接口实例。
Stub因为继承自Binder可以把一些数据写入到共享内存中,所以保存此对象的一个引用。而proxy是我们接口的实现。

自此我们分析完成了
IMyAidlInterface iMyAidlInterface = IMyAidlInterface.Stub.asInterface(service);这句代码

接下来我们看下用返回实例对象调用接口方法:

  Log.e(TAG, "onServiceConnected: ");
                //32
                IMyAidlInterface iMyAidlInterface = IMyAidlInterface.Stub.asInterface(service);

                try {
                    //调用接口中的一个方法. //36行
                    String test = iMyAidlInterface.hello("test");
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
  • 36行分析开始:
  String test = iMyAidlInterface.hello("test");

我们知道aidl连接对象当客户端服务端不在同一进程的时候 ,在客户端返回的接口实例是Stub内部类Proxy的实例。

所以上面的调用 hell(“test”);的源码我们直接从Proxyhell()开始看

hell() 是我们最开始定义的两个方法的其中一个,另外一个是test1()

Proxy hell方法

这里写图片描述

上图已给出行号

先解释一个类android.os.Parcel 这个类运行在内存中传递序列化后的数据。Parcelable这类相比大家都用过。Parcelable内部就是封装了Parcel 对象而已。

Serializable是java提供的序列化,Parcel是google提供。更轻量并且在内存传输胜过Serializable

122-123获取parcel池获取对象实例,

   android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();

_data 用于写入数据,reply用于接受数据。

124行接收结果。因为我们hello()返回值是String所以他自然也是String。

写入token。DESCRIPTOR就是我们前面说过确定Binder和哪个接口关联的一个ID。这里很明显。你要告诉我你要写个哪个Binder我才能确定 传个哪个接口。

 _data.writeInterfaceToken(DESCRIPTOR);

127行

//写入参数
 _data.writeString(aString);

128行 放入Stub对象transact方法中 然后写入共享内存

mRemote.transact(Stub.TRANSACTION_hello, _data, _reply, 0);

其中第一个参数 final的int值。是用于确定你调用哪个接口方法。每个定义接口方法都有一个对应的final值。其声明在Stub类中
本案例中
这里写图片描述

继续分析。。。。。。。
mRemote.transact()客户端的实现类是final class BinderProxy implements IBinder {。。。}
BinderProxyBinder 两个都是IBinder的实现类

public boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
        Binder.checkParcel(this, code, data, "Unreasonably large binder buffer");
        if (Binder.isTracingEnabled()) { Binder.getTransactionTracker().addTrace(); }
        return transactNative(code, data, reply, flags);
    }

后面是JNI代码有兴趣的同学可以继续往下学习。
在这之后我们会回调到服务端的StubonTransact()方法

@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_test1: {
                    data.enforceInterface(DESCRIPTOR);
                    int _arg0;
                    _arg0 = data.readInt();
                    long _arg1;
                    _arg1 = data.readLong();
                    boolean _arg2;
                    _arg2 = (0 != data.readInt());
                    float _arg3;
                    _arg3 = data.readFloat();
                    double _arg4;
                    _arg4 = data.readDouble();
                    java.lang.String _arg5;
                    _arg5 = data.readString();
                    this.test1(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5);
                    reply.writeNoException();
                    return true;
                }
                case TRANSACTION_hello: {
                    data.enforceInterface(DESCRIPTOR);
                    java.lang.String _arg0;
                    _arg0 = data.readString();
                    java.lang.String _result = this.hello(_arg0);
                    reply.writeNoException();
                    reply.writeString(_result);
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }

上面的代码不需要介绍太多了相信代价都看得懂。

这里写图片描述
看完这些我们再来看看bindService的执行过程。这里我直接贴出时序图给大家看,不做过多介绍这里写图片描述

Activity的实现类为ContextImpl。你也许在继承类图看不到这个类的,但是你可以看到Activity的父类ContextWrapper的构造方法传入的一个对象实例是什么。
这里写图片描述

0
0
查看评论

AIDL 的理解&源码分析

aidl通讯试所有的对象都得实现Parcelable 接口 onTransact() Binder 驱动执行完毕时需要回调的方法 Proxy 移动端IPC通讯的时候,消息都是通过Binder 进行传递的。 这个代理可以理解为:多个进程之间的代理。 但是个人理解为:Android代码 ...
  • lxcay
  • lxcay
  • 2016-01-26 16:59
  • 615

Android深入源码分析理解Aidl整体调用流程(雷惊风)

2017年开始上班的第一天,老不想工作了,假期感觉还没开始就已经结束了,唉,时间就是这样,新的一年开始了,虽然很不想干正事,没办法,必须干起来,因为后边的路还很长,距离六十岁还很远。刚上班也没什么事,复习一下之前的东西,看了一下Aidl相关的知识,仔细瞅了瞅Aidl的调用流程,这里写篇文章整理一下,...
  • liuyonglei1314
  • liuyonglei1314
  • 2017-02-03 20:37
  • 1366

aidl使用以及bindService

转自:http://blog.csdn.net/yangzhaomuma/article/details/50576017 AIDL简义 Android中的数据传输、方法调用等,常见的是集中在应用程序内的Activity之间,如Activity-A传递到Activity-B。 ...
  • lzpdz
  • lzpdz
  • 2016-11-24 15:41
  • 737

Android源码分析二:Android AIDL使用详解

1.什么是aidl:aidl是 Android Interface definition language的缩写,一看就明白,它是一种android内部进程通信接口的描述语言,通过它我们可以定义进程间的通信接口 icp:interprocess communication :内部进程通信 ...
  • koumen3
  • koumen3
  • 2016-02-17 16:31
  • 632

浅析绑定远程服务bindService基本原理与AIDL分析

我们一般绑定远程服务,分为客户端和服务端,并且需要使用AIDL来实现,这里主要从java层面来讲解它的基本过程,没有深入到Binder的C/C++底层的实现。 我们的通常做法是这样的: 1、创建一个客户端项目和一个服务端项目 2、在客户端项目和服务端项目相同的包下创建AIDL文件 3、实现客...
  • hp910315
  • hp910315
  • 2015-08-02 16:59
  • 2037

关于AIDL一些需要注意的地方

1、从远程客户端进程来的调用是由不同的线程发起的,运行在不同的进程。所以,服务端必须处理好在同一时刻有多个从不同线程过来的请求的情况。也就是说,一个AIDL的实现必须是完成线程安全的,必须手动处理多线程的情况。 2、AIDL接口当中的oneway关键字代表远程调用的行为。当使用的时候,远程调用不会阻...
  • liuyi1207164339
  • liuyi1207164339
  • 2016-06-19 13:53
  • 4743

Android AIDL源码分析

前言 上一篇博文介绍了关于AIDL是什么,为什么我们需要AIDL,AIDL的语法以及如何使用AIDL等方面的知识,这一篇博文将顺着上一篇的思路往下走,接着介绍关于AIDL的一些更加深入的知识。强烈建议大家在看这篇博文之前先看一下上一篇博文:Android:学习AIDL,这一篇文章就够了(上) 注...
  • kururunga
  • kururunga
  • 2017-03-11 15:43
  • 169

Android AIDL使用步骤

最近项目中用到蓝牙打电话,需要调用AIDL实现,说实话,AIDL之前没有用过,其实不太会,于是网上搜索哈,算是知道怎么用了,具体实现原理还是不太懂,反正知道怎么用了。原理:就是A程序定义一个AIDL文件,eclipse会自动编译生成class文件,然后在A程序的service里面实现AIDL接口,并...
  • u011747761
  • u011747761
  • 2015-08-13 11:08
  • 2487

eclipse 创建aidl

1,在eclipse包名中添加xxx.aidl;(注意是.aidl而不是adil)File->new->Gennel->File->xxx.aidl; 2,xxx.aidl中的package必须是xxx.adil所在包名(不然创建不了); 3,xxx.adil中不能出现不...
  • didiao11300
  • didiao11300
  • 2013-12-05 13:20
  • 4183

关于Android中的四大组件(AIDL Service的使用)

跨进程调用Service(AIDL Service) Android系统中的进程之间不能共享内存,因此,需要提供一些机制在不同进程之间进行数据通信。 在前一篇文章(关于Android中的四大组件(Service的开启与关闭))中介绍了开发人员如何定制自己的服务,但这些 服务并不能被其它的...
  • GULINHAI12
  • GULINHAI12
  • 2015-08-18 17:59
  • 3148
    个人资料
    • 访问:491601次
    • 积分:6793
    • 等级:
    • 排名:第4126名
    • 原创:274篇
    • 转载:63篇
    • 译文:0篇
    • 评论:51条