Android 探索AIDL

参考书籍《Android开发艺术探索》,《Android内核剖析》
案例源码

AIDL中支持的数据类型
  1. 基本数据类型:int,long,char,boolean,double等
  2. String和CharSequence
  3. ArrayList,但是ArrayList中存入的元素必须被AIDL支持
  4. HashMap,但是HashMap中存入的元素都必须被AIDL支持
  5. 所有实现了Parcelable接口的对象
使用AIDL注意事项
  1. 编译时可能找不到自定义实现Parcelable接口的java类,需要在App Module的build.gradle中添加以下代码。参考
android {
    sourceSets {
        main {
            manifest.srcFile 'src/main/AndroidManifest.xml'
            java.srcDirs = ['src/main/java', 'src/main/aidl']
            resources.srcDirs = ['src/main/java', 'src/main/aidl']
            aidl.srcDirs = ['src/main/aidl']
            res.srcDirs = ['src/main/res']
            assets.srcDirs = ['src/main/assets']
        }
    }
}
  1. 每一个实现了Parcelable接口的Java类,如果AIDL文件中用到了该类,必须新建一个同名的.aidl文件.
// Book.java
// 自定义Java类,实现了Parcelable接口.
package com.yey.ok;
import android.os.Parcel;
import android.os.Parcelable;
public class Book implements Parcelable {

}
// IBookManager.aidl
// 假如它被另外一个AIDL文件用到了.
package com.yey.ok;
import com.yey.ok.Book;
interface IBookManager{
    void addBook(in Book book);
}

// 必须新建一个同名的.aidl文件.
package com.yey.ok;
parcelable Book;
  1. AIDL对象被另一个AIDL文件使用, 就算他们位于同一包下,使用aidl文件也需要显示的import进来.
// Book.aidl
// Book.aidl如果被另外一个AIDL文件使用
package com.yey.ok;
parcelable Book;

// IBookManager.aidl
// 使用Book.aidl文件,需要显示的import.
package com.yey.ok;
// 显示的import
import com.yey.ok.Book;
interface IBookManager{
    void addBook(in Book book);
}
  1. AIDL中只支持方法,不支持声明静态常量.
// IBookManager.aidl
package com.yey.ok;
import com.yey.ok.Book;
interface IBookManager{
    // String name; //声明静态常量是不允许的.
    List<Book> getBookList();//方法是可以的
}
  1. 方法参数必须标记好方向
// IBookManager.aidl
package com.yey.ok;
import com.yey.ok.Book;
interface IBookManager{
    // in:输入型参数
    // out:输出型参数
    // inout:输入输出型参数
    void addBook(in Book book);
}
  1. 客户端发起远程请求时,当前线程会被挂起直到服务端进程返回数据,这个过程是一个耗时操作,所以不要在UI线程发起请求.
  2. 被请求的服务端方法是运行在Binder线程池中(另一个线程中),onTransact()应该采用同步方式实现.
分析通过AIDL自动生成的Java类
  1. IBookManager.javaIBookManager.aidl文件自动生成,首先来分析IBookManager.Stub
// IBookManager.Stub.java
public interface IBookManager extends android.os.IInterface {
    // Stub:Binder本地对象,也就是服务端进程持有它.
    public static abstract class Stub extends android.os.Binder implements IBookManager {
        // 该Binder本地对象的唯一标识符,一般用当前Binder类名称表示.
        private static final String DESCRIPTOR = "com.yey.ok.笔记.IBookManager";
        public Stub() {
          // mOwner = this
          // mDescriptor = DESCRIPTOR
          // 传入之后,就可以使用queryLocalInterface()返回相应的Binder对象了.
          // attachInterface()是使用queryLocalInterface()的前提.
          this.attachInterface(this, DESCRIPTOR);
        }
        // 用于返回Binder对象, 是返回Binder本地对象还是Binder代理对象视进程而定。
        public static IBookManager asInterface(android.os.IBinder obj) {
          if ((obj==null)) {
            return null;
          }
          // queryLocalInterface() 该方法上面讲过, 通过描述符来查找Binder本地对象。
          android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
          // 如果查找的Binder对象不为空,且是在同一个进程(iin instanceof 笔记.IBookManager)
          if (((iin!=null)&&(iin instanceof IBookManager))) {
            // 那么就将查到当前进程中的Binder本地对象直接返回就行了。
            return ((IBookManager)iin);
          }
          // 如果不是同一个进程中的(iin instanceof 笔记.IBookManager)
          // 那么就返回这个Binder本地对象对应的Binder代理对象。
          // obj 是BpBinder类型的Binder代理对象,内部包含有Binder引用对象的句柄值.通过该句柄值就可以找到Binder引用对象再找到Binder实体对象,最后找到Binder本地对象.
          return new Stub.Proxy(obj);
        }
        // 返回当前Binder本地对象。
        @Override
        public android.os.IBinder asBinder() {
          return this;
        }
        /**
         * 这个方法是运行在服务端的Binder线程池中,客户端发起跨进程请求,最终会交由此方法处理。
         * @param code 用来确认客户端请求的目标方法是什么。
         * @param data 用来存放目标方法中的参数
         * @param reply 用来存入目标方法执行完成的返回值
         * @param flags
         * @return false 代表客户端请求失败,true代表客户端请求成功。
         * @throws android.os.RemoteException
         */
        @Override
        public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException{
          String descriptor = DESCRIPTOR;
          switch (code) {
            case INTERFACE_TRANSACTION: {
              reply.writeString(descriptor);
              return true;
            }
            case TRANSACTION_getBookList: {
              data.enforceInterface(descriptor);
              // getBookList()由服务端实现.
              java.util.List<com.yey.ok.Book> _result = this.getBookList();
              reply.writeNoException();
              // 写入返回值  
              reply.writeTypedList(_result);
              return true;
            }
            case TRANSACTION_addBook: {
              data.enforceInterface(descriptor);
              com.yey.ok.Book _arg0;
              if ((0!=data.readInt())) {
                _arg0 = com.yey.ok.Book.CREATOR.createFromParcel(data);
              }
              else {
                _arg0 = null;
              }
              // 该方法由服务端实现  
              this.addBook(_arg0);
              reply.writeNoException();
              // 没有返回值就无需写入  
              return true;
            }
            default: {
              return super.onTransact(code, data, reply, flags);
            }
          }
        }
    }
}
  1. 分析IBookManager.Stub.Proxy
// IBookManager.Stub.Proxy.java
public interface IBookManager extends android.os.IInterface {
    public static abstract class Stub extends android.os.Binder implements IBookManager {
       //Binder代理类 
       private static class Proxy implements IBookManager {
            // mRemote 是BpBinder类型的Binder代理对象,内部包含有Binder引用对象的句柄值.通过该句柄值就可以找到Binder引用对象再找到Binder实体对象,最后找到Binder本地对象.
            private android.os.IBinder mRemote;
            Proxy(android.os.IBinder remote) {
              mRemote = remote;
            }
            
            // 返回Binder代理对象
            @Override
            public android.os.IBinder asBinder() {
              return mRemote;
            }
            
            public String getInterfaceDescriptor() {
              return DESCRIPTOR;
            }
            
            /**
             * 该方法运行在客户端,当客户端调用该方法时,最终会调用服务端的onTransact().
             * @return
             * @throws android.os.RemoteException
             */
            @Override
            public java.util.List<com.yey.ok.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.yey.ok.Book> _result;
              try {
                _data.writeInterfaceToken(DESCRIPTOR);
                // 开始RPC(远程过程调用),最终调用的就是服务端的onTransact()。
                // 执行该方法后,当前线程会被挂起,等待服务端进程onTransact()调用完成并返回返回值,然后唤醒客户端线程继续执行。
                boolean _status = mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);
                // 假如RPC失败,则通过IBookManager类型对象调用当前进程中getBookList(),
                // getBookList()它是接口方法,需要当前进程自己该方法。
                if (!_status && getDefaultImpl() != null) {
                  return getDefaultImpl().getBookList();
                }
                _reply.readException();
                // RPC成功,从_reply中取出返回值并返回。
                _result = _reply.createTypedArrayList(com.yey.ok.Book.CREATOR);
              }
              finally {
                _reply.recycle();
                _data.recycle();
              }
              return _result;
            }
            
            /**
             * 该方法运行在客户端,当客户端调用该方法时,最终会调用服务端的onTransact().
             * @param book
             * @throws android.os.RemoteException
             */
            @Override
            public void addBook(com.yey.ok.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);
                }
                // RPC开始,当前线程会被挂起。
                // RPC完成唤醒线程,继续下面代码执行。
                boolean _status = mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply, 0);
                if (!_status && getDefaultImpl() != null) {
                  // 如果RPC失败,尝试调用当前进程中的方法,但是当前进程需要实现该方法。
                  getDefaultImpl().addBook(book);
                  // addBook() 不需要返回值, 所以return就行了。
                  return;
                }
                _reply.readException();
              }
              finally {
                _reply.recycle();
                _data.recycle();
              }
            }
            public static IBookManager sDefaultImpl;
        }
    }
}
AIDL使用
  • 客户端
public class MainActivity extends AppCompatActivity {
    private ActivityMainBinding mainBinding;
    private final String TAG = this.getClass().getName();
    private boolean isBind;
    // 定义一个死亡接收者, 当服务进程挂了这里会收到通知
    private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
        @Override
        public void binderDied() {
            if (iBookManager == null) return;
            // 取消设置的代理
            // iBookManager如果是Stub.Proxy类型, 就返回Binder代理对象.
            // iBookManager如果是Stub类型, 将返回Binder本地对象.
            iBookManager.asBinder().unlinkToDeath(mDeathRecipient, 0);
            iBookManager = null;
            Log.e(TAG, "收到死亡通知,已经接触绑定");
        }
    };

    // Stub.Proxy类型对象或者Stub类型对象.
    private IBookManager iBookManager;
    // 服务链接
    private ServiceConnection connection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            // service Binder代理对象.
            // asInterface()返回Stub.Proxy类型对象或者Stub类型对象.
            iBookManager = IBookManager.Stub.asInterface(service);
            try {
                // 设置死亡代理
                service.linkToDeath(mDeathRecipient, 0);
                printBookList();
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
        @Override
        public void onServiceDisconnected(ComponentName name) {
        }
    };
    // 绑定远程服务
    mainBinding.button2.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            Intent service = new Intent("com.yey.app_server.bookmanagerserver");
            service.setPackage("com.yey.app_server");
            bindService(service, connection, BIND_AUTO_CREATE);
            isBind = true;
        }
    });
    // 解绑远程服务
    mainBinding.button3.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            if (isBind) {
                unbindService(connection);
                isBind = false;
            }
        }
    });
}
  • 服务端
public class BookManagerServer extends Service {
    // CopyOnWriteArrayList写是同步的, 支持并发读取.
    public CopyOnWriteArrayList mBookList = new CopyOnWriteArrayList();

    @Override
    public void onCreate() {
        super.onCreate();
        mBookList.add(new Book(0,"Android开发艺术探索"));
        mBookList.add(new Book(1,"Android内核剖析"));
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }
    // Binder本地对象
    private Binder mBinder = new IBookManager.Stub() {
        @Override
        public List<Book> getBookList() throws RemoteException {
            return mBookList;
        }

        @Override
        public void addBook(Book book) throws RemoteException {
            mBookList.add(book);
        }
    };
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值