一、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接口,代码如下:

package com.sl.analyzebinder;

import android.os.Parcel;
import android.os.Parcelable; public class Book implements Parcelable {
public int bookId; public String bookName; public Book(int bookId, String bookName) {
this.bookId = bookId;
this.bookName = bookName;
} protected Book(Parcel in) {
bookId = in.readInt();
bookName = in.readString();
} @Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(bookId);
dest.writeString(bookName);
} @Override
public int describeContents() {
return 0;
} public static final Creator<Book> CREATOR = new Creator<Book>() {
@Override
public Book createFromParcel(Parcel in) {
return new Book(in);
} @Override
public Book[] newArray(int size) {
return new Book[size];
}
};
}

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

package com.sl.analyzebinder;
parcelable Book;

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

// IBookManager.aidl
package com.sl.analyzebinder;
import com.sl.analyzebinder.Book;
import com.sl.analyzebinder.IOnNewBookArrivedListener; // Declare any non-default types here with import statements interface IBookManager {
List<Book> getBookList();
void addBook(in Book book);
void registerListener(IOnNewBookArrivedListener listener);
void unRegisterListener(IOnNewBookArrivedListener listener);
}

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

// IOnNewBookArrivedListener.aidl
package com.sl.analyzebinder;
import com.sl.analyzebinder.Book;
// Declare any non-default types here with import statements interface IOnNewBookArrivedListener {
void onNewBookArrived(in Book book);
}

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

package com.sl.analyzebinder;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.RemoteException;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.TextView; import java.lang.ref.WeakReference;
import java.util.List; public class MainActivity extends AppCompatActivity { private static final int MESSAGE_NEW_BOOK_ARRIVED = 110;
private BookHandler mHandler = new BookHandler(this);
private IBookManager bookManager; private static class BookHandler extends Handler {
private WeakReference<MainActivity> activity; BookHandler(MainActivity activity) {
this.activity = new WeakReference<>(activity);
} @Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MESSAGE_NEW_BOOK_ARRIVED:
if (activity != null) {
activity.get().changeContent((Book) msg.obj);
}
break;
default:
super.handleMessage(msg);
break;
}
}
} private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
@Override
public void binderDied() {
if (bookManager == null)
return;
bookManager.asBinder().unlinkToDeath(mDeathRecipient, 0);
bookManager = null;
bindService(); }
}; private void changeContent(Book book) {
String text = content.getText().toString();
content.setText(text.concat("-----------").concat(book.bookName));
} private IOnNewBookArrivedListener mListener = new IOnNewBookArrivedListener.Stub() {
@Override
public void onNewBookArrived(Book book) throws RemoteException {
mHandler.obtainMessage(MESSAGE_NEW_BOOK_ARRIVED, book).sendToTarget();
}
};
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
// onServiceConnected 和 onServiceDisconnected 都在ui线程中执行,不要执行服务端耗时操作,否则 ANR
bookManager = IBookManager.Stub.asInterface(service);
try {
List<Book> bookList = bookManager.getBookList();
StringBuilder result = new StringBuilder();
if (bookList != null && bookList.size() > 0) {
for (int i = 0; i < bookList.size(); i++) {
result.append("----").append(bookList.get(i).bookName);
}
}
content.setText(bookList != null ? result : "失败了");
bookManager.addBook(new Book(3, "摆渡人"));
bookManager.registerListener(mListener);
service.linkToDeath(mDeathRecipient,0); //重连机制
} catch (RemoteException e) {
e.printStackTrace();
}
} @Override
public void onServiceDisconnected(ComponentName name) { //重连机制
bookManager = null;
}
};
private TextView content; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
bindService();
content = findViewById(R.id.book_list);
} private void bindService() {
Intent service = new Intent();
service.setAction("com.sl.aidl");
service.setPackage("com.sl.binderservice");
bindService(service, mConnection, Context.BIND_AUTO_CREATE);
} @Override
protected void onDestroy() {
super.onDestroy();
unbindService(mConnection);
unRegisterListener();
} private void unRegisterListener() {
if (bookManager != null && bookManager.asBinder().isBinderAlive()) { // 通过Binder 的 isBinderAlive 方法也可以判断 Binder 是否死亡
try {
bookManager.unRegisterListener(mListener);
} catch (RemoteException e) {
e.printStackTrace();
}
}
} public void stopInform(View view) {
unRegisterListener();
}
}

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

     

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

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

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

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

  2. 服务端

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

        

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

package com.sl.binderservice;

import android.app.Service;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Binder;
import android.os.IBinder;
import android.os.Parcel;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.util.Log; import com.sl.analyzebinder.Book;
import com.sl.analyzebinder.IBookManager;
import com.sl.analyzebinder.IOnNewBookArrivedListener; import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicBoolean; public class BookManagerService extends Service {
private static final String TAG = "BMS"; private CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<>(); //CopyOnWriteArrayList 支持并发读写
private RemoteCallbackList<IOnNewBookArrivedListener> mListenerList = new RemoteCallbackList<>(); //CopyOnWriteArrayList 支持并发读写
private AtomicBoolean mIsServiceDestory = new AtomicBoolean(); 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);
} @Override
public void registerListener(IOnNewBookArrivedListener listener) throws RemoteException {
mListenerList.register(listener);
} @Override
public void unRegisterListener(IOnNewBookArrivedListener listener) throws RemoteException {
mListenerList.unregister(listener);
} @Override
public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException { //onTransact处进行权限验证
int check = checkCallingOrSelfPermission("com.sl.permission.aidl");
if (check == PackageManager.PERMISSION_DENIED) //权限验证
return false;
String packageName = null;
String[] packages = getPackageManager().getPackagesForUid(getCallingUid()); // uid 验证
if (packages != null && packages.length > 0) {
packageName = packages[0];
}
if (packageName == null || !packageName.startsWith("com.sl"))
return false;
return super.onTransact(code, data, reply, flags);
}
}; @Override
public void onCreate() {
super.onCreate();
mIsServiceDestory.set(false);
mBookList.add(new Book(1, "Android"));
mBookList.add(new Book(2, "IOS")); new Thread(new ServiceWorker()).start();
} @Override
public void onDestroy() {
super.onDestroy();
mIsServiceDestory.set(true);
} @Override
public IBinder onBind(Intent intent) { //onBind 处进行权限验证,验证权限并没有通过。不知道是为什么?
/**int check = checkCallingOrSelfPermission("com.sl.permission.aidl");
      * if (check == PackageManager.PERMISSION_DENIED)
* return null;
*/
return mBinder;
} private class ServiceWorker implements Runnable {
@Override
public void run() {
while (!mIsServiceDestory.get()) {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
int bookId = mBookList.size() + 1;
Book newBook = new Book(bookId, "NewBook#" + bookId);
try {
onNewBookArrived(newBook);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
} private void onNewBookArrived(Book book) throws RemoteException {
mBookList.add(book);
int count = mListenerList.beginBroadcast();
for (int i = 0; i < count; i++) {
IOnNewBookArrivedListener listener = mListenerList.getBroadcastItem(i);
if (listener != null)
listener.onNewBookArrived(book);
}
mListenerList.finishBroadcast();
}
}

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

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.sl.binderservice"> <permission
android:name="com.sl.permission.aidl"
android:description="@string/service_permission"
android:permissionGroup="com.sl.permissions"
android:protectionLevel="normal" /> <application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity> <service
android:name=".BookManagerService"
android:permission="com.sl.permission.aidl"
android:exported="true">
<intent-filter>
<action android:name="com.sl.aidl" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</service>
</application> </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. Kivy之常用的小知识

    1.设置标题 app.title = '测试' 2.设置屏幕长度 Window.size=1000,600 3.设置屏幕右上角icon app.title = r'C:\Users\Administr ...

  2. 如何使用 lsyncd 实时同步并执行 shell 命令

    修改 lsyncd 的默认配置,不直接执行rsync 进行同步,而是改用自己的脚本. binary 指定我们的脚本 vim /usr/local/lsyncd/etc/lsyncd.conf sett ...

  3. Linux学习笔记之查看Linux版本信息

    0x00 概述 这里所谓的Linux版本信息,包括Linux内核版本信息和Linux系统版本信息. 0x01 查看Linux内核版本信息 方法1:登陆Linux,在终端输入 cat /proc/ver ...

  4. django模板-通过a标签生成链接并跳转

    views.py from django.shortcuts import render from django.http import HttpResponse def index(request) ...

  5. P3813 [FJOI2017]矩阵填数(组合数学)

    P3813 [FJOI2017]矩阵填数 shadowice1984说:看到计数想容斥........ 这题中,我们把图分成若干块,每块的最大值域不同 蓝后根据乘法原理把每块的方案数(互不相干)相乘. ...

  6. ubuntu文件名乱码convmv和iconv

    sudo apt install convmv sudo convmv -f gbk -t utf- -r --notest /home/pm/Desktop/p Linux下两个工具convmv和i ...

  7. shell实现自动部署两台tomcat项目+备份

    就做个记录吧, 其实也没啥好说的. 主机 #!/bin/bash TODAY=$(date -d 'today' +%Y-%m-%d-%S) MIP="192.168.180.24" ...

  8. UI自动化(二)css选择器

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

  9. NT1_keras下搭建一个3层模型并且修改。

    In [1]: import keraskeras.__version__ C:\ProgramData\Anaconda3\lib\site-packages\h5py\__init__.py:36 ...

  10. alloc_skb申请函数分析

    alloc_skb()用于分配缓冲区的函数.由于"数据缓冲区"和"缓冲区的描述结构"(sk_buff结构)是两种不同的实体,这就意味着,在分配一个缓冲区时,需要 ...