IPC 之 AIDL 的使用
一、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 的使用的更多相关文章
- android service 的各种用法(IPC、AIDL)
http://my.oschina.net/mopidick/blog/132325 最近在学android service,感觉终于把service的各种使用场景和用到的技术整理得比较明白了,受益颇 ...
- AIDL/IPC Android AIDL/IPC 进程通信机制——超具体解说及使用方法案例剖析(播放器)
首先引申下AIDL.什么是AIDL呢?IPC? ------ Designing a Remote Interface Using AIDL 通常情况下,我们在同一进程内会使用Binder.Broad ...
- Android开发之IPC进程间通信-AIDL介绍及实例解析
一.IPC进程间通信 IPC是进程间通信方法的统称,Linux IPC包括以下方法,Android的进程间通信主要采用是哪些方法呢? 1. 管道(Pipe)及有名管道(named pipe):管道可用 ...
- IPC进程间通信 - AIDL+Binder
原理 http://www.linuxidc.com/Linux/2012-07/66195.htm 服务端,客户端处在用户空间,而binder驱动处在内核空间. 服务器端.一个Binder服 ...
- Android之IPC(aidl)
IPC(Inter-Process Conmunication) 进程间通讯 在同一进程中,各个组件进行通信是十分方便的,普通的函数调用就可以解决:但是,对于处于不同进程中的组件来说,要进行通信,就需 ...
- IPC之AIDL&binder关系
binder是一个远程对象的基础类,核心部分是远程调用机制,这部分是由IBinder定义的.它是对IBinder类的实现,其中IBinder类提供这样一个类的标准的本地化实现方式. 大多数开发者不会去 ...
- android跨进程通信(IPC)——AIDL
转载请标明出处: http://blog.csdn.net/sinat_15877283/article/details/51026711: 本文出自: [温利东的博客] 近期在看 @任玉刚 大神编写 ...
- Android AIDL 进行进程间通讯(IPC)
编写AIDL文件时,需要注意: 1.接口名和aidl文件名相同. 2.接口和方法前不用加访问权限修饰符 (public.private.protected等,也不能用final.static). 3. ...
- Android IPC通信和AIDL技术应用
首先我们了解一下 IPC和AIDL IPC:进程间通信 AIDL:Android Interface Definition Language,即Android接口定义语言. 为什么使用: Androi ...
随机推荐
- Kattis之旅——Prime Path
The ministers of the cabinet were quite upset by the message from the Chief of Security stating that ...
- CentOS7下部署Django项目详细操作步骤
严格按下面步骤 一.更新系统软件包 yum update -y 二.安装软件管理包和可能使用的依赖 yum -y groupinstall "Development tools" ...
- navicat mysql导出数据 批量插入的形式
这里介绍的是mysql 相同服务器类型数据传输的高级设置 选中数据库后右键“ 转储SQL文件”默认导出的记录格式是一条条的,采用的是”完整插入语句”,格式如下 '); '); '); 这种格式保证了兼 ...
- 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 ...
- sudo env
$ sudo echo "Defaults env_keep=\"PATH PYTHONPATH LD_LIBRARY_PATH CAFFE_ROOT\"& ...
- oracle12c的日志查看
查看GI日志:切换到grid用户 查看DB日志:切换到oracle的目录下 执行[oracle@swnode1 ~]$ adrci [oracle@swnode1 ~]$ adrci ADRCI: R ...
- 使用TortoiseGit从GitHub下拉上传代码配置
转载:http://baijiahao.baidu.com/s?id=1579466751803515477&wfr=spider&for=pc 转载:https://blog.csd ...
- 使用SimpleDateFormat时的日期和时间模式
日期和时间模式 日期和时间格式由日期和时间模式 字符串指定.在日期和时间模式字符串中,未加引号的字母 ‘A’ 到’Z’ 和’a’ 到’z’ 被解释为模式字母,用来表示日期或时间字符串元素.文本可以使用 ...
- Linux使用——Linux命令——Linux文件压缩和解压使用记录
一:tar(可压缩可解压) tar命令是Unix/Linux系统中备份文件的可靠方法,几乎可以工作于任何环境中,它的使用权限是所有用户.但是tar本身只是一个文件打包工具,只有和其他工具组合时才具有压 ...
- 远程调试Spring项目
目录 服务端启动: 启动jar包: 使用环境变量参数调试jar包: 使用mvnDebug启动SpringMVC项目: 使用mvn启动: 使用Tomcat,非嵌入式启动: 客户端设置: IDEA设置: ...