AIDL实现原理

Posted 一代小强

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了AIDL实现原理相关的知识,希望对你有一定的参考价值。


“在之前有写过一篇《大话android 进程通信之AIDL》,但是一直没有补充对应的实现原理“

引言

说实在的,AIDL跨进程方式用得比较少,也一直比较神秘,这篇文章将剖析AIDL的通信过程,开车!

一、AIDL的用法

列一下本文用到的AIDL通信方式,代码详情见 大话android 进程通信之AIDL

首先定义Server端

public class BookManagerService extends Service 
    private List<Book> bookList = new ArrayList<>();
    private Binder binder = new IBookManager.Stub() 
        @Override
        public List<Book> getBookList() 
            return bookList;
        

        @Override
        public void addBook(Book book) 
            if (!bookList.contains(book)) 
                bookList.add(book);
            
        
    ;
    
    @Override
    public IBinder onBind(Intent intent) 
        return binder;
    

Client端的调用如下:

private IBookManager iBookManager = null;
private ServiceConnection serviceConnection = new ServiceConnection() 
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) 
            Log.i(TAG, "onServiceConnected: ");
            iBookManager = IBookManager.Stub.asInterface(service);
            try 
                Book book = new Book(1, "web App");
                iBookManager.addBook(book);
             catch (Exception e) 
                e.printStackTrace();
            
        

        @Override
        public void onServiceDisconnected(ComponentName name) 
            Log.i(TAG, "onServiceDisconnected: ");
        
    ;

在绑定service后,可以拿到server端提供的IBinder,通过asInterface方法转为对应的AIDL接口,即可实现与Server端的通信。

那asInterface 方法做了啥?我们知道,在定义AIDL接口后,build整个项目,就会生成对应的java接口。我们来看看对应的实现

// AIDL接口对应的Java接口
public interface IBookManager extends android.os.IInterface 
  
    // 提供给client 继承的抽象类,client端可以实现 AIDL中定义的方法。
    public static abstract class Stub extends android.os.Binder implements com.example.aidl.IBookManager 
        ...
        public static com.example.aidl.IBookManager asInterface(android.os.IBinder obj) 
            if ((obj == null)) 
                return null;
            
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.example.aidl.IBookManager))) 
                // 同一个进程,不需要binder通信,直接获取对接的接口
                return ((com.example.aidl.IBookManager) iin);
            
            // 涉及到跨进程,需要创建一个代理对象
            return new com.example.aidl.IBookManager.Stub.Proxy(obj);
        

    @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_getBookList: 
                    data.enforceInterface(descriptor);
                    java.util.List<com.example.aidl.Book> _result = this.getBookList();
                    reply.writeNoException();
                    reply.writeTypedList(_result);
                    return true;
                
                case TRANSACTION_addBook: 
                    data.enforceInterface(descriptor);
                    com.example.aidl.Book _arg0;
                    if ((0 != data.readInt())) 
                        _arg0 = com.example.aidl.Book.CREATOR.createFromParcel(data);
                     else 
                        _arg0 = null;
                    
                    this.addBook(_arg0);
                    reply.writeNoException();
                    return true;
                
                default: 
                    return super.onTransact(code, data, reply, flags);
                
            
        
        
        // 代理对象,用于处理binder通信相关的操作
        private static class Proxy implements com.example.aidl.IBookManager 
            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 java.util.List<com.example.aidl.Book> getBookList() throws android.os.RemoteException 
              android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                java.util.List<com.example.aidl.Book> _result;
                try 
                    _data.writeInterfaceToken(DESCRIPTOR);
                    mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.createTypedArrayList(com.example.aidl.Book.CREATOR);
                 finally 
                    _reply.recycle();
                    _data.recycle();
                
                return _result;
            
           
            @Override
            public void addBook(com.example.aidl.Book book) throws android.os.RemoteException 
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                try 
                    _data.writeInterfaceToken(DESCRIPTOR);
                    if ((book != null)) 
                        _data.writeInt(1);
                        book.writeToParcel(_data, 0);
                     else 
                        _data.writeInt(0);
                    
                    mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply, 0);
                    _reply.readException();
                 finally 
                    _reply.recycle();
                    _data.recycle();
                
            
        
        // 定义方法的调用序号
        static final int TRANSACTION_getBookList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
        static final int TRANSACTION_addBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
    
    // 对应的Java接口方法
    public java.util.List<com.example.aidl.Book> getBookList() throws android.os.RemoteException;
    public void addBook(com.example.aidl.Book book, com.example.aidl.Book book1) throws android.os.RemoteException;


可以看到,内部的抽象类Stub是实现了IBookManager接口,并且内部有一个私有的proxy类,用于处理binder通信。client端只需要基础Stub,并实现对应的自定的java接口即可。

所以 IBookManager.Stub.asInterface(service) 这段代码实际是返回了Stub的代理类——proxy,并传入obj作为mRemote。

二、Stub.Proxy类

这个proxy类主要的工作是 处理传参、返回值、资源回收的一些工作。拿 addBook 这个方法来说,具体如下:

           @Override
            public void addBook(com.example.aidl.Book book) throws android.os.RemoteException 
                // 获取入参的 Parcel
                android.os.Parcel _data = android.os.Parcel.obtain();
                // 获取返回值的 Parcel
                android.os.Parcel _reply = android.os.Parcel.obtain();
                try 
                    // 标注远程服务名称
                    _data.writeInterfaceToken(DESCRIPTOR);
                    if ((book != null)) 
                        _data.writeInt(1);
                        book.writeToParcel(_data, 0);
                     else 
                        _data.writeInt(0);
                    
                    // 调用远端的binder,即Stub类实现
                    mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply, 0);
                    // 读取异常
                    _reply.readException();
                 finally 
                    _reply.recycle();
                    _data.recycle();
                
            

该方法通过Parcel 这个对象实现入参和返回值的传递(Parcel 主要作用是将我们的数据进行打包,用于在binder中传输)。

我们知道如果需要通过binder传递对象,那么这个对象必须要实现Parcelable接口,那是因为需要把对象打包到Parcel对象中。

if ((book != null)) 
    // Parcel 内部采用了mNativePtr指针来记录写入的值。这里写入1,表示入参是一个有效值
     _data.writeInt(1);
    // 把 book的一些属性值写入_data,方便Server端反序列化
     book.writeToParcel(_data, 0);
  else 
    // 入参为无效值,写入0
    _data.writeInt(0);
 

在封装好Parcel后,proxy就直接调用了remote的transact方法,之后就是异常的校验动作。

proxy是怎么获取到返回值的呢?我们来看getBookList方法实现

            @Override
            public java.util.List<com.example.aidl.Book> getBookList() throws android.os.RemoteException 
                android.os.Parcel _reply = android.os.Parcel.obtain();
                java.util.List<com.example.aidl.Book> _result;
                try 
                    ....
                    _result = _reply.createTypedArrayList(com.example.aidl.Book.CREATOR);
                 finally 
                    ....
                
                return _result;
            

这里是拿到_reply对象,然后做了一次反序列化的动作。createTypedArrayList实现如下:

public final class Parcel 
    public final <T> ArrayList<T> createTypedArrayList(Parcelable.Creator<T> c) 
            int N = readInt();
            if (N < 0) 	
                return null;
            
            ArrayList<T> l = new ArrayList<T>(N);
            while (N > 0) 
                if (readInt() != 0) 
                    l.add(c.createFromParcel(this));
                 else 
                    l.add(null);
                
                N--;
            
            return l;
    

Book 的实现如下:

public class Book implements Parcelable 
    public int bookId;
    public String bookName;
    private Book(Parcel parcel) 
        bookId = parcel.readInt();
        bookName = parcel.readString();
    
    
    public static final Creator<Book> CREATOR = new Creator<Book>() 

        @Override
        public Book createFromParcel(Parcel parcel) 
            return new Book(parcel);
        

        @Override
        public Book[] newArray(int i) 
            return new Book[i];
        
    ;

三、Stub类

刚才有提到,Proxy addBook方法中会调用mRemote的transact方法,实现传参数和获取返回值。我们来看看mRemote是怎么来的。

回到 IBookManager.Stub.asInterface 方法,上面有说到,在构建Proxy对象的时候,需要传入IBinder对象,这个IBinder对象是onServiceConnected 传入的service。而service正是Server端的onBinder返回的binder对象。由于client端与Server端的AIDL接口是一样的,并且Server端的binder实现也是继承于Stub。所以Client端的proxy实际调用的是Server端的Stub对象。

我们先看看Stub的onTransact实现,这个方法比较长,传入的参数有4个

  • code:用于标识是调用哪个方法,即AIDL是通过序号判断client端调用的是哪个方法
  • data:存放方法调用传入的参数
  • reply:存放方法调用返回的参数
  • flags:用于判断Client端transact()方法是否立即返回
        @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_getBookList: 
                    data.enforceInterface(descriptor);
                    // 调用服务端的方法,获取返回值
                    java.util.List<com.example.aidl.Book> _result = this.getBookList();
                    // 在reply头部写入无异常的标识
                    reply.writeNoException();
                    // 将数据写入reply中
                    reply.writeTypedList(_result);
                    return true;
                
                case TRANSACTION_addBook: 
                    // 执行对应的接口
                    data.enforceInterface(descriptor);
                    com.example.aidl.Book _arg0;
                    // 判断参数是否有效,这里与client端写参数前,也会写一个int值是对应的。
                    if ((0 != data.readInt())) 
                        // 反序列化,从data中获取参数对象
                        _arg0 = com.example.aidl.Book.CREATOR.createFromParcel(data);
                     else 
                        _arg0 = null;
                    
                    // 调用Server端的addBook方法,实现方法的逻辑
                    this.addBook(_arg0);
                   // 在reply头部写入无异常的标识
                    reply.writeNoException();
                    return true;
                
                default: 
                    return super.onTransact(code, data, reply, flags);
                
            
        

我们可以看到,Stub类其实比较简单,主要处理client端的方法调用请求,获取传入的参数,调用Server端的接口,封装返回值。

问题来了Stub的onTransact方法是怎么调用的?上面不是说了,client端拿到了Server端的binder,然后直接调用的嘛~~

是这样吗?

接着看,Stub的父类是一个Binder,我们直接在Server端的addBook方法打个断点看看,调用栈如下,即是被Binder的execTransact方法回调的。


Binder的execTransact方法如下,该方法是私有的,并且内部没有其他地方调用。我们注意到该方法的备注,说明是在C++层调用的该方法

 // Entry point from android_util_Binder.cpp's onTransact
    private boolean execTransact(int code, long dataObj, long replyObj,
            int flags) 
        BinderCallsStats binderCallsStats = BinderCallsStats.getInstance();
        BinderCallsStats.CallSession callSession = binderCallsStats.callStarted(this, code);
        Parcel data = Parcel.obtain(dataObj);
        Parcel reply = Parcel.obtain(replyObj);
        boolean res;
        final boolean tracingEnabled = Binder.isTracingEnabled();
        try 
            if (tracingEnabled) 
                Trac

以上是关于AIDL实现原理的主要内容,如果未能解决你的问题,请参考以下文章

Android——AIDL基础实现demo以及原理探究

IPC——浅谈AIDL的架构原理

Android进程间通信-AIDL实现原理

Android进阶笔记:AIDL内部实现详解

android开发aidl何时使用

AndroidStudio通过AIDL开启 绑定远程Service