AIDL是一种接口定义语言,用于生成可在Android设备上两个进程之间进行进程间通信(IPC)的代码。

AIDL的使用

  • 新建一个aidl文件,定义进程间通信的接口
// IStudentManager.aidl 
package com.tellh.androidart;
// Declare any non-default types here with import statements
import com.tellh.androidart.Student;
  interface IStudentManager {
  
List<Student> getStudent();
   void addStudent(in Student student);
}

注意点:

  • aidl中支持的参数类型为:基本类型(int,long,char,boolean等),String,CharSequence,List,Map,其他类型必须使用import导入,即使它们可能在同一个包里。
  • 接口中的参数除了aidl支持的类型,其他类型必须标识其方向:到底是输入还是输出抑或两者兼之,用in,out或者inout来表示。
  • 如果有自定义的数据类型,需要把自定义类实现Parcelable接口并新建aidl声明文件。
// Student.aidl
package tellh.com.androidart;
parcelable Student;
public class Student implements Parcelable {...}

点击sycn project,对应的接口java类就被自动生成在gen目录中。

  • 创建服务端的Service,运行在服务端进程
public class AidlTestService extends Service { 
   private final static String TAG = "AidlTestService";
  private static final String PACKAGE_CLIENT = "com.example.test.aidlclient&&com.tellh.androidart";
private final List<Student> mStudents = new ArrayList<>();
private IBinder mBinder = new IStudentManager.Stub() {
    @Override
   public List<Student> getStudent() throws RemoteException {
synchronized (mStudents) { return mStudents; }
}
    @Override
  public void addStudent(Student student) throws RemoteException {
  synchronized (mStudents) {
    if (!mStudents.contains(student)) mStudents.add(student);
    }   
   }
 @Override
public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {     //权限控制
    String packageName = null;
    String[] packages = AidlTestService.this.getPackageManager(). getPackagesForUid(getCallingUid());
    if (packages != null && packages.length > 0) {
      packageName = packages[0];
      }  
    Log.d(TAG, "onTransact: " + packageName);
    String[] clients = PACKAGE_CLIENT.split("&&");
   for (String client : clients) {
     if (client.equals(packageName)) {
       return super.onTransact(code, data, reply, flags); }
   }
  return false;
}
};
@Override
public void onCreate() {
  super.onCreate();
  synchronized (mStudents) {
    for (int i = 1; i < 6; i++) {
      Student student = new Student();
      student.sno = i;
      student.name = "student#" + i;
      student.age = i * 5;
     mStudents.add(student);
} } }
public AidlTestService() { }
@Override
public IBinder onBind(Intent intent) { return mBinder; }
}

注册Service

<service android:name=".AidlTestService" android:enabled="true" android:exported="true"> 
  <intent-filter>
     <category android:name="android.intent.category.DEFAULT" />
     <action android:name="com.tellh.androidart.AidlTestService" />
  </intent-filter>
</service>

新建一个进程作为客户端,隐式启动服务端的Service,在绑定的回调函数中通过IStudentManager.Stub.asInterface(binder)获得通信接口的代理对象。

public class AidlClientActivity extends ActionBarActivity { 
  private String tag = "AidlClient";
  private static final String ACTION_BIND_SERVICE = "com.tellh.androidart.AidlTestService";
  private IStudentManager mStudentManager;
    // Binder连接断裂(死亡)代理
  private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
    @Override
    public void binderDied() {
       if (mStudentManager == null) return;
        mStudentManager.asBinder().unlinkToDeath(mDeathRecipient, 0);
        mStudentManager = null;
        bindService(); }
   };
  private ServiceConnection mServiceConn = new ServiceConnection() {
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
      mStudentManager = IStudentManager.Stub.asInterface(service);
      try { service.linkToDeath(mDeathRecipient, 0); }
      catch (RemoteException e) { e.printStackTrace(); }
    }
    @Override
    public void onServiceDisconnected(ComponentName name) { mStudentManager = null; }
};
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_aidl_client);
    bindService(); }
private void bindService() {
  Intent intent = new Intent(ACTION_BIND_SERVICE);
  intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
  intent.setPackage("com.tellh.androidart");
   bindService(intent, mServiceConn, BIND_AUTO_CREATE);
}
@Override
protected void onDestroy() {
  super.onDestroy();
  if (mStudentManager != null) unbindService(mServiceConn);
}
public void onClickGetStudent(View view) {
  if (mStudentManager == null) return;
  try { Log.d(tag, mStudentManager.getStudent().toString()); }
  catch (RemoteException e) { e.printStackTrace(); } }
public void onClickAddStudent(View view) {
  if (mStudentManager == null) return;
  try { Student student = new Student();
  student.sno = 7;
  student.age = 10;
  student.name = "TellH";
  student.sex = Student.SEX_MALE;
  mStudentManager.addStudent(student);
  Log.d(tag, mStudentManager.getStudent().toString());
}
catch (RemoteException e) { e.printStackTrace(); } } }

注册Activity,在新的进程中运行

 <activity
android:name=".AidlClientActivity"
android:process=":client" />

如果在应用之间通信,客户端应用需要把服务端的aidl文件全部copy过来,并且保证包名相同,

AIDL的工作原理

分析自动生成的java接口代码:

pubblic interface IStudentManager extends android.os.IInterface { 

    public static abstract class Stub extends android.os.Binder implements tellh.com.androidart.IStudentManager {...} 
public java.util.List<tellh.com.androidart.Student> getStudent() throws android.os.RemoteException;
public void addStudent(tellh.com.androidart.Student student) throws android.os.RemoteException;

这个接口类继承IInterface,所有在Binder中传输的接口都必须继承IInterface。它除了保留了我们定义在aidl文件中的两个方法外,还自动生成一个名为Stub的静态类。这个Stub是抽象类,实现了这个接口类并继承自Binder,将会在服务端的Service中被实例化,作为onBind的返回指返回给客户端,客户端通过IStudentManager.Stub.asInterface(binder)转化为这个接口类的代理对象。
抽象类Stub的成员和属性:
    DESRIPTOR
Binder的唯一标识,一般用当前Binder的全类名表示。
    public static IStudentManager asInterface(IBinder obj)

如果客户端和服务端位于同一个进程,那么直接返回服务端的Stub对象本身,否则返回Stub.Proxy代理对象。

public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags)

该方法运行在服务端中的Binder线程池中,服务端通过code确定客户端所请求的目标方法是什么,从data中取出目标方法所需的参数,然后执行目标方法,并将目标方法的返回值写入reply。如果该方法的返回false,那么客户端的请求回失败。因此,我们可以利用这个特性来做权限验证。

Proxy#addStudent

Proxy是一个代理类,或者说是装饰者类。

@Override public java.util.List<tellh.com.androidart.Student> getStudent() throws android.os.RemoteException { 
   android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.util.List<tellh.com.androidart.Student> _result; try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getStudent, _data, _reply, 0);
_reply.readException();
_result = _reply.createTypedArrayList(tellh.com.androidart.Student.CREATOR); }
finally {
    _reply.recycle();
    _data.recycle();
}
return _result; }

每一个代理方法都是相同的套路,将方法的标识,参数和返回值对象传入方法mRemote.transact中,发起远程调用请求,同时挂起当前线程,然后服务端的Stub#onTransact被调用。当onTransact返回true,调用成功,从reply对象中取出返回值,返回给客户端调用方。

总结

高度概括AIDL的用法,就是服务端里有一个Service,给与之绑定(bindService)的特定客户端进程提供Binder对象。客户端通过AIDL接口的静态方法asInterface 将Binder对象转化成AIDL接口的代理对象,通过这个代理对象就可以发起远程调用请求了。

至于更深入地,远程调用的过程可以概括如下图

————————————————
版权声明:本文为CSDN博主「TellH」的原创文章,遵循undefined版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/tellh/article/details/55100167

浅析AIDL的使用和工作原理的更多相关文章

  1. “Ceph浅析”系列之五——Ceph的工作原理及流程

    本文将对Ceph的工作原理和若干关键工作流程进行扼要介绍.如前所述,由于Ceph的功能实现本质上依托于RADOS,因而,此处的介绍事实上也是针对RADOS进行.对于上层的部分,特别是RADOS GW和 ...

  2. 初涉IPC,了解AIDL的工作原理及使用方法

    初涉IPC,了解AIDL的工作原理及使用方法 今天来讲讲AIDL,这个神秘的AIDL,也是最近在学习的,看了某课大神的讲解写下的blog,希望结合自己的看法给各位同价通俗易懂的讲解 官方文档:http ...

  3. 初涉IPC,了解AIDL的工作原理及用法

    初涉IPC,了解AIDL的工作原理及用法 今天来讲讲AIDL.这个神奇的AIDL,也是近期在学习的,看了某课大神的解说写下的blog,希望结合自己的看法给各位同价通俗易懂的解说 官方文档:http:/ ...

  4. CPU处理器架构和工作原理浅析

    CPU处理器架构和工作原理浅析 http://c.biancheng.net/view/3456.html 汇编语言是学习计算机如何工作的很好的工具,它需要我们具备计算机硬件的工作知识. 基本微机设计 ...

  5. Sftp和ftp 差别、工作原理等(汇总ing)

    Sftp和ftp over ssh2的差别 近期使用SecureFx,涉及了两个不同的安全文件传输协议: -sftp -ftp over SSH2 这两种协议是不同的.sftp是ssh内含的协议,仅仅 ...

  6. 面试:Handler 的工作原理是怎样的?

    面试场景 平时开发用到其他线程吗?都是如何处理的? 基本都用 RxJava 的线程调度切换,嗯对,就是那个 observeOn 和 subscribeOn 可以直接处理,比如网络操作,RxJava 提 ...

  7. [转]ionic工作原理

    本文转自:https://segmentfault.com/a/1190000011495654 1.Ionic的工作原理 Ionic通过cordova把一个Web应用嵌入原生应用 用户打开一个ion ...

  8. FTP(文件传输协议)工作原理

    目前在网络上,如果你想把文件和其他人共享.最方便的办法莫过于将文件放FTP服务器上,然后其他人通过FTP客户端程序来下载所需要的文件. 1.FTP架构 如同其他的很多通讯协议,FTP通讯协议也采用客户 ...

  9. Ceph的工作原理及流程

    本文将对Ceph的工作原理和若干关键工作流程进行扼要介绍.如前所述,由于Ceph的功能实现本质上依托于RADOS,因而,此处的介绍事实上也是针对RADOS进行.对于上层的部分,特别是RADOS GW和 ...

随机推荐

  1. BZOJ 5120: [2017国家集训队测试]无限之环(费用流)

    传送门 解题思路 神仙题.调了一个晚上+半个上午..这道咋看咋都不像图论的题竟然用费用流做,将行+列为奇数的点和偶数的点分开,也就是匹配问题,然后把一个点复制四份,分别代表这个点的上下左右接头,如果有 ...

  2. NX二次开发-使用MFC对话框不能用UF_UI_select等函数解决方法

    VC/MFC调用UG Dialog要进入加锁状态 加锁 UF_UI_lock_ug_access ( UF_UI_FROM_CUSTOM ); 此处为UF_UI_select的函数 解锁 UF_UI_ ...

  3. python 参数定义库argparse

    python 参数定义库argparse 这一块的官方文档在这里 注意到这个库是因为argparse在IDE中和在ipython notebook中使用是有差异的,习惯了再IDE里面用,转到ipyth ...

  4. 通过adb命令查看SN、CID码等信息

      用ADB命令来查看自己手机的相关硬件以及其他的参数信息,相信许多机友已经早已查看过,而新入门感兴趣的机友可以尝试一下. 运用这些ADB命令可以很直观的查看到你手机上的硬件与软件方面的详细信息. 下 ...

  5. 1、postman介绍与安装

    postman介绍 官方介绍:Developers use Postman to build modern software for the API-first world. 个人理解postman是 ...

  6. 自旋锁spinlock

    1 在单处理器上的实现 单核系统上,不存在严格的并发,因此对资源的共享主要是多个任务分时运行造成的. 只要在某一时段,停止任务切换,并且关中断(对于用户态应用程序,不大可能与中断处理程序抢临界区资源) ...

  7. ALSA 更改默认声卡

    用 aplay -l 看一下各声卡的配置信息 创建 /etc/asound.conf,根据 aplay -l  的输入,选择需要的声卡信息,按如下格式添入,即可更改默认声卡 defaults.pcm. ...

  8. Java文件系统

    Java7 引入了新的输入/输出2(NIO.2)API并提供了一个新的I/O API. 它向Java类库添加了三个包:java.nio.file,java.nio.file.attribute和jav ...

  9. JFinal教程

    自学JFinal总结 前言:每次搭建ssm框架时,就像搬家一样,非常繁杂,并且还容易出错.正好了解到JFinal极简,无需配置即可使用,在这里记录下学习的过程. 感谢:非常感谢此网站发布的教程,非常详 ...

  10. jq-demo-轮播图

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...