一、AIDL 知识储备

 1. AIDL 文件支持的数据类型:

  • 基本数据类型 (int , long , char , boolean ,double 等);
  • String 和 CharSequence;
  • List :只支持 ArrayList,里面每个元素都必须能够被 AIDL 支持;
  • Map :只支持 HashMap,里面每个元素都必须能够被 AIDL 支持,包括 key 和 value;
  • Parcelable : 所有实现了 Parcelable 接口的对象;
  • AIDL : 所有的 AIDL 接口本身也可以在 AIDL 文件中使用。

 2. 自定义的 Parcelable 对象 和 AIDL  对象必须要显示 import 进来,不管他们是否和当前的 AIDL 文件位于同一个包内。

 3. 如果 AIDL 文件中用到了自定义的 Parcelable 对象,那么必须新建一个和它同名的 AIDL 文件,并在其中声明它为 Parcelable 类型。

 4. AIDL 中除了基本数据类型,其他类型的参数必须标上方向:in 、out 或者 inout,in 表示输入型参数 , out 表示输出型参数,inout 表示输入输出型参数。

二、AIDL 的使用:

  AIDL 是用于多进程通信场景的,我们创建两个应用,一个 Client 和一个 Server 来说明 AIDL 的使用。在示例项目中实现了基本的交互操作,还有 权限验证 、注册 listener 到服务端监听服务端事件 和重连机制。

  1. 客户端

    客户端工程目录:

  看图我们可以知道,工程中创建了和 AIDL 相关的四个文件,Book类、Book.aidl 、IBookManager.aidl、IOnNewBookArrivedListener.aidl 四个文件。

  (1) Book类是示例中用于传递的数据类,实现了 Parcelable接口,代码如下:

  1. package com.sl.analyzebinder;
  2.  
  3. import android.os.Parcel;
  4. import android.os.Parcelable;
  5.  
  6. public class Book implements Parcelable {
  7. public int bookId;
  8.  
  9. public String bookName;
  10.  
  11. public Book(int bookId, String bookName) {
  12. this.bookId = bookId;
  13. this.bookName = bookName;
  14. }
  15.  
  16. protected Book(Parcel in) {
  17. bookId = in.readInt();
  18. bookName = in.readString();
  19. }
  20.  
  21. @Override
  22. public void writeToParcel(Parcel dest, int flags) {
  23. dest.writeInt(bookId);
  24. dest.writeString(bookName);
  25. }
  26.  
  27. @Override
  28. public int describeContents() {
  29. return 0;
  30. }
  31.  
  32. public static final Creator<Book> CREATOR = new Creator<Book>() {
  33. @Override
  34. public Book createFromParcel(Parcel in) {
  35. return new Book(in);
  36. }
  37.  
  38. @Override
  39. public Book[] newArray(int size) {
  40. return new Book[size];
  41. }
  42. };
  43. }

  (2)Book.aidl 的作用是要符合上面第一章节第 3 点的规定,代码如下:

  1. package com.sl.analyzebinder;
  2. parcelable Book;

  (3)IBookManager.aidl 定义交互操作的接口,代码如下:

  1. // IBookManager.aidl
  2. package com.sl.analyzebinder;
  3. import com.sl.analyzebinder.Book;
  4. import com.sl.analyzebinder.IOnNewBookArrivedListener;
  5.  
  6. // Declare any non-default types here with import statements
  7.  
  8. interface IBookManager {
  9. List<Book> getBookList();
  10. void addBook(in Book book);
  11. void registerListener(IOnNewBookArrivedListener listener);
  12. void unRegisterListener(IOnNewBookArrivedListener listener);
  13. }

  (4)IOnNewBookArrivedListener.aidl 定义接听服务事件的接口,代码如下:

  1. // IOnNewBookArrivedListener.aidl
  2. package com.sl.analyzebinder;
  3. import com.sl.analyzebinder.Book;
  4. // Declare any non-default types here with import statements
  5.  
  6. interface IOnNewBookArrivedListener {
  7. void onNewBookArrived(in Book book);
  8. }

  所有关于AIDL的文件写好之后,我们先 Make Project 一下,然后来看一下客户端如何连接服务端,MainActivity 中进行连接:

  1. package com.sl.analyzebinder;
  2.  
  3. import android.content.ComponentName;
  4. import android.content.Context;
  5. import android.content.Intent;
  6. import android.content.ServiceConnection;
  7. import android.os.Bundle;
  8. import android.os.Handler;
  9. import android.os.IBinder;
  10. import android.os.Message;
  11. import android.os.RemoteException;
  12. import android.support.v7.app.AppCompatActivity;
  13. import android.view.View;
  14. import android.widget.TextView;
  15.  
  16. import java.lang.ref.WeakReference;
  17. import java.util.List;
  18.  
  19. public class MainActivity extends AppCompatActivity {
  20.  
  21. private static final int MESSAGE_NEW_BOOK_ARRIVED = 110;
  22. private BookHandler mHandler = new BookHandler(this);
  23. private IBookManager bookManager;
  24.  
  25. private static class BookHandler extends Handler {
  26. private WeakReference<MainActivity> activity;
  27.  
  28. BookHandler(MainActivity activity) {
  29. this.activity = new WeakReference<>(activity);
  30. }
  31.  
  32. @Override
  33. public void handleMessage(Message msg) {
  34. switch (msg.what) {
  35. case MESSAGE_NEW_BOOK_ARRIVED:
  36. if (activity != null) {
  37. activity.get().changeContent((Book) msg.obj);
  38. }
  39. break;
  40. default:
  41. super.handleMessage(msg);
  42. break;
  43. }
  44. }
  45. }
  46.  
  47. private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
  48. @Override
  49. public void binderDied() {
  50. if (bookManager == null)
  51. return;
  52. bookManager.asBinder().unlinkToDeath(mDeathRecipient, 0);
  53. bookManager = null;
  54. bindService();
  55.  
  56. }
  57. };
  58.  
  59. private void changeContent(Book book) {
  60. String text = content.getText().toString();
  61. content.setText(text.concat("-----------").concat(book.bookName));
  62. }
  63.  
  64. private IOnNewBookArrivedListener mListener = new IOnNewBookArrivedListener.Stub() {
  65. @Override
  66. public void onNewBookArrived(Book book) throws RemoteException {
  67. mHandler.obtainMessage(MESSAGE_NEW_BOOK_ARRIVED, book).sendToTarget();
  68. }
  69. };
  70. private ServiceConnection mConnection = new ServiceConnection() {
  71. @Override
  72. public void onServiceConnected(ComponentName name, IBinder service) {
  73. // onServiceConnected 和 onServiceDisconnected 都在ui线程中执行,不要执行服务端耗时操作,否则 ANR
  74. bookManager = IBookManager.Stub.asInterface(service);
  75. try {
  76. List<Book> bookList = bookManager.getBookList();
  77. StringBuilder result = new StringBuilder();
  78. if (bookList != null && bookList.size() > 0) {
  79. for (int i = 0; i < bookList.size(); i++) {
  80. result.append("----").append(bookList.get(i).bookName);
  81. }
  82. }
  83. content.setText(bookList != null ? result : "失败了");
  84. bookManager.addBook(new Book(3, "摆渡人"));
  85. bookManager.registerListener(mListener);
  86. service.linkToDeath(mDeathRecipient,0); //重连机制
  87. } catch (RemoteException e) {
  88. e.printStackTrace();
  89. }
  90. }
  91.  
  92. @Override
  93. public void onServiceDisconnected(ComponentName name) { //重连机制
  94. bookManager = null;
  95. }
  96. };
  97. private TextView content;
  98.  
  99. @Override
  100. protected void onCreate(Bundle savedInstanceState) {
  101. super.onCreate(savedInstanceState);
  102. setContentView(R.layout.activity_main);
  103. bindService();
  104. content = findViewById(R.id.book_list);
  105. }
  106.  
  107. private void bindService() {
  108. Intent service = new Intent();
  109. service.setAction("com.sl.aidl");
  110. service.setPackage("com.sl.binderservice");
  111. bindService(service, mConnection, Context.BIND_AUTO_CREATE);
  112. }
  113.  
  114. @Override
  115. protected void onDestroy() {
  116. super.onDestroy();
  117. unbindService(mConnection);
  118. unRegisterListener();
  119. }
  120.  
  121. private void unRegisterListener() {
  122. if (bookManager != null && bookManager.asBinder().isBinderAlive()) { // 通过Binder 的 isBinderAlive 方法也可以判断 Binder 是否死亡
  123. try {
  124. bookManager.unRegisterListener(mListener);
  125. } catch (RemoteException e) {
  126. e.printStackTrace();
  127. }
  128. }
  129. }
  130.  
  131. public void stopInform(View view) {
  132. unRegisterListener();
  133. }
  134. }

  为什么要先 Make Project 一下呢,目的是为了让系统为我们自动生成 AIDL 中的 Binder 实现,你可以到下面这个目录中去查看一下具体生成的代码:

     

    对了,要想 aidl 目录下的 java 文件被 编译,还需要再 app 下的build.xml 文件中添加配置:

  1. android {
  2. sourceSets{
    main{
    java.srcDirs = ['src/main/java', 'src/main/aidl']
    aidl.srcDirs = ['src/main/aidl']
    }
    }

  服务端要求权限验证,所以客户端得添加权限,这个权限在服务端中定义:

  1. <uses-permission android:name="com.sl.permission.aidl" />

  2. 服务端

     将 客户端 aidl 目录全部拷贝到 服务端,拷贝后来看服务端目录:

        

    拷贝过来之后,仍然需要 Make Project ,道理你懂得。BookManagerService 就是我们的服务了,来看具体实现:

  1. package com.sl.binderservice;
  2.  
  3. import android.app.Service;
  4. import android.content.Intent;
  5. import android.content.pm.PackageManager;
  6. import android.os.Binder;
  7. import android.os.IBinder;
  8. import android.os.Parcel;
  9. import android.os.RemoteCallbackList;
  10. import android.os.RemoteException;
  11. import android.util.Log;
  12.  
  13. import com.sl.analyzebinder.Book;
  14. import com.sl.analyzebinder.IBookManager;
  15. import com.sl.analyzebinder.IOnNewBookArrivedListener;
  16.  
  17. import java.util.List;
  18. import java.util.concurrent.CopyOnWriteArrayList;
  19. import java.util.concurrent.atomic.AtomicBoolean;
  20.  
  21. public class BookManagerService extends Service {
  22. private static final String TAG = "BMS";
  23.  
  24. private CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<>(); //CopyOnWriteArrayList 支持并发读写
  25. private RemoteCallbackList<IOnNewBookArrivedListener> mListenerList = new RemoteCallbackList<>(); //CopyOnWriteArrayList 支持并发读写
  26. private AtomicBoolean mIsServiceDestory = new AtomicBoolean();
  27.  
  28. private Binder mBinder = new IBookManager.Stub() {
  29. @Override
  30. public List<Book> getBookList() throws RemoteException {
  31. return mBookList;
  32. }
  33.  
  34. @Override
  35. public void addBook(Book book) throws RemoteException {
  36. mBookList.add(book);
  37. }
  38.  
  39. @Override
  40. public void registerListener(IOnNewBookArrivedListener listener) throws RemoteException {
  41. mListenerList.register(listener);
  42. }
  43.  
  44. @Override
  45. public void unRegisterListener(IOnNewBookArrivedListener listener) throws RemoteException {
  46. mListenerList.unregister(listener);
  47. }
  48.  
  49. @Override
  50. public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException { //onTransact处进行权限验证
  51. int check = checkCallingOrSelfPermission("com.sl.permission.aidl");
  52. if (check == PackageManager.PERMISSION_DENIED) //权限验证
  53. return false;
  54. String packageName = null;
  55. String[] packages = getPackageManager().getPackagesForUid(getCallingUid()); // uid 验证
  56. if (packages != null && packages.length > 0) {
  57. packageName = packages[0];
  58. }
  59. if (packageName == null || !packageName.startsWith("com.sl"))
  60. return false;
  61. return super.onTransact(code, data, reply, flags);
  62. }
  63. };
  64.  
  65. @Override
  66. public void onCreate() {
  67. super.onCreate();
  68. mIsServiceDestory.set(false);
  69. mBookList.add(new Book(1, "Android"));
  70. mBookList.add(new Book(2, "IOS"));
  71.  
  72. new Thread(new ServiceWorker()).start();
  73. }
  74.  
  75. @Override
  76. public void onDestroy() {
  77. super.onDestroy();
  78. mIsServiceDestory.set(true);
  79. }
  80.  
  81. @Override
  82. public IBinder onBind(Intent intent) { //onBind 处进行权限验证,验证权限并没有通过。不知道是为什么?
    /**int check = checkCallingOrSelfPermission("com.sl.permission.aidl");
  1. * if (check == PackageManager.PERMISSION_DENIED)
  2. * return null;
    */
  3. return mBinder;
  4. }
  5.  
  6. private class ServiceWorker implements Runnable {
  7. @Override
  8. public void run() {
  9. while (!mIsServiceDestory.get()) {
  10. try {
  11. Thread.sleep(5000);
  12. } catch (InterruptedException e) {
  13. e.printStackTrace();
  14. }
  15. int bookId = mBookList.size() + 1;
  16. Book newBook = new Book(bookId, "NewBook#" + bookId);
  17. try {
  18. onNewBookArrived(newBook);
  19. } catch (RemoteException e) {
  20. e.printStackTrace();
  21. }
  22. }
  23. }
  24. }
  25.  
  26. private void onNewBookArrived(Book book) throws RemoteException {
  27. mBookList.add(book);
  28. int count = mListenerList.beginBroadcast();
  29. for (int i = 0; i < count; i++) {
  30. IOnNewBookArrivedListener listener = mListenerList.getBroadcastItem(i);
  31. if (listener != null)
  32. listener.onNewBookArrived(book);
  33. }
  34. mListenerList.finishBroadcast();
  35. }
  36. }

  记得在 AndroidManifest.xml 中注册服务:

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <manifest xmlns:android="http://schemas.android.com/apk/res/android"
  3. package="com.sl.binderservice">
  4.  
  5. <permission
  6. android:name="com.sl.permission.aidl"
  7. android:description="@string/service_permission"
  8. android:permissionGroup="com.sl.permissions"
  9. android:protectionLevel="normal" />
  10.  
  11. <application
  12. android:allowBackup="true"
  13. android:icon="@mipmap/ic_launcher"
  14. android:label="@string/app_name"
  15. android:roundIcon="@mipmap/ic_launcher_round"
  16. android:supportsRtl="true"
  17. android:theme="@style/AppTheme">
  18. <activity android:name=".MainActivity">
  19. <intent-filter>
  20. <action android:name="android.intent.action.MAIN" />
  21.  
  22. <category android:name="android.intent.category.LAUNCHER" />
  23. </intent-filter>
  24. </activity>
  25.  
  26. <service
  27. android:name=".BookManagerService"
  28. android:permission="com.sl.permission.aidl"
  29. android:exported="true">
  30. <intent-filter>
  31. <action android:name="com.sl.aidl" />
  32. <category android:name="android.intent.category.DEFAULT" />
  33. </intent-filter>
  34. </service>
  35. </application>
  36.  
  37. </manifest>

  BookManagerService 中进行了权限验证,涉及到 自定义权限 查看这篇。除了自定义的Permission 验证外,还有通过 UID 验证,这是 Binder 机制提供的,这就是体现 Binder 通信机制相对其他机制安全的点。

  注意服务端中用到了几个类,这里提一下:

  AtomicBoolean :  支持并发读写

  RemoteCallbackList : 支持并发读写 ,是系统专门提供的用于删除跨进程 listener 的接口,它是一个泛型,支持管理任意的 AIDL 接口 。它还能当客户端进程终止后,它能够自动移除客户端所注册的 listener 。

  CopyOnWriteArrayList : 支持并发读写 。

  这三个这里就不详细介绍了,到这里AIDL 的具体使用也基本完事了,如果有漏的话,后续补上或者告诉我。

  示例工程代码,下载地址  密码: smf7

IPC 之 AIDL 的使用的更多相关文章

  1. android service 的各种用法(IPC、AIDL)

    http://my.oschina.net/mopidick/blog/132325 最近在学android service,感觉终于把service的各种使用场景和用到的技术整理得比较明白了,受益颇 ...

  2. AIDL/IPC Android AIDL/IPC 进程通信机制——超具体解说及使用方法案例剖析(播放器)

    首先引申下AIDL.什么是AIDL呢?IPC? ------ Designing a Remote Interface Using AIDL 通常情况下,我们在同一进程内会使用Binder.Broad ...

  3. Android开发之IPC进程间通信-AIDL介绍及实例解析

    一.IPC进程间通信 IPC是进程间通信方法的统称,Linux IPC包括以下方法,Android的进程间通信主要采用是哪些方法呢? 1. 管道(Pipe)及有名管道(named pipe):管道可用 ...

  4. IPC进程间通信 - AIDL+Binder

      原理 http://www.linuxidc.com/Linux/2012-07/66195.htm   服务端,客户端处在用户空间,而binder驱动处在内核空间. 服务器端.一个Binder服 ...

  5. Android之IPC(aidl)

    IPC(Inter-Process Conmunication) 进程间通讯 在同一进程中,各个组件进行通信是十分方便的,普通的函数调用就可以解决:但是,对于处于不同进程中的组件来说,要进行通信,就需 ...

  6. IPC之AIDL&binder关系

    binder是一个远程对象的基础类,核心部分是远程调用机制,这部分是由IBinder定义的.它是对IBinder类的实现,其中IBinder类提供这样一个类的标准的本地化实现方式. 大多数开发者不会去 ...

  7. android跨进程通信(IPC)——AIDL

    转载请标明出处: http://blog.csdn.net/sinat_15877283/article/details/51026711: 本文出自: [温利东的博客] 近期在看 @任玉刚 大神编写 ...

  8. Android AIDL 进行进程间通讯(IPC)

    编写AIDL文件时,需要注意: 1.接口名和aidl文件名相同. 2.接口和方法前不用加访问权限修饰符 (public.private.protected等,也不能用final.static). 3. ...

  9. Android IPC通信和AIDL技术应用

    首先我们了解一下 IPC和AIDL IPC:进程间通信 AIDL:Android Interface Definition Language,即Android接口定义语言. 为什么使用: Androi ...

随机推荐

  1. Kattis之旅——Prime Path

    The ministers of the cabinet were quite upset by the message from the Chief of Security stating that ...

  2. CentOS7下部署Django项目详细操作步骤

    严格按下面步骤 一.更新系统软件包 yum update -y 二.安装软件管理包和可能使用的依赖 yum -y groupinstall "Development tools" ...

  3. navicat mysql导出数据 批量插入的形式

    这里介绍的是mysql 相同服务器类型数据传输的高级设置 选中数据库后右键“ 转储SQL文件”默认导出的记录格式是一条条的,采用的是”完整插入语句”,格式如下 '); '); '); 这种格式保证了兼 ...

  4. Golang通过git clone beego框架报错 error: while accessing https://github.com/astaxie/beego/info/refs fatal: HTTP request failed package github.com/astaxie/beego: exit status 128

    在Centos6.4尝试搭建beego框架,使用git命令clone时报错 # cd .; git clone https://github.com/astaxie/beego /www/projec ...

  5. sudo env

    $ sudo echo "Defaults        env_keep=\"PATH PYTHONPATH LD_LIBRARY_PATH CAFFE_ROOT\"& ...

  6. oracle12c的日志查看

    查看GI日志:切换到grid用户 查看DB日志:切换到oracle的目录下 执行[oracle@swnode1 ~]$ adrci [oracle@swnode1 ~]$ adrci ADRCI: R ...

  7. 使用TortoiseGit从GitHub下拉上传代码配置

    转载:http://baijiahao.baidu.com/s?id=1579466751803515477&wfr=spider&for=pc 转载:https://blog.csd ...

  8. 使用SimpleDateFormat时的日期和时间模式

    日期和时间模式 日期和时间格式由日期和时间模式 字符串指定.在日期和时间模式字符串中,未加引号的字母 ‘A’ 到’Z’ 和’a’ 到’z’ 被解释为模式字母,用来表示日期或时间字符串元素.文本可以使用 ...

  9. Linux使用——Linux命令——Linux文件压缩和解压使用记录

    一:tar(可压缩可解压) tar命令是Unix/Linux系统中备份文件的可靠方法,几乎可以工作于任何环境中,它的使用权限是所有用户.但是tar本身只是一个文件打包工具,只有和其他工具组合时才具有压 ...

  10. 远程调试Spring项目

    目录 服务端启动: 启动jar包: 使用环境变量参数调试jar包: 使用mvnDebug启动SpringMVC项目: 使用mvn启动: 使用Tomcat,非嵌入式启动: 客户端设置: IDEA设置: ...