Binder、AIDL学习笔记
来源:互联网 发布:手机分享电脑网络 编辑:程序博客网 时间:2024/06/05 05:17
本文是任玉刚《Android开发艺术探索》的学习笔记,介绍Binder的使用以及上层原理
什么是Binder
- 直观上,Binder是Android的一个类,实现IBider借口
- 从IPC角度,Binder是Android的一种跨进程通信方式
- 从硬件角度,Binder是一种虚拟的物理设备,设备驱动是/dev/binder
- 从Framework角度,Binder是ServiceManager连接各种Manager和ManagerService的桥梁
- 从应用层角度,Binder是客户端和服务端进行通信的媒介。当bindService的时候,服务端会返回一个包含了服务端业务调用的Binder对象,通过这个Binder对象,客户端就可以获取服务端提供的服务或者数据。这里的服务包括普通服务和基于AIDL的服务
通过AIDL分析Binder的工作机制
Android开发中,Binder主要用在Service中,包括AIDL,其中普通Service中的Binder不涉及进程间的通信,所以较为简单,无法触及Binder的核心。
AIDL (Android Interface Definition Language) 是一种IDL 语言,编译器可以通过aidl文件生成一段代码,通过预先定义的接口达到两个进程内部通信进程(IPC)的目的。如果需要在一个Activity中, 访问另一个Service中的某个对象, 需要先将对象转化成AIDL可识别的参数(可能是多个参数), 然后使用AIDL来传递这些参数, 在消息的接收端, 使用这些参数组装成自己需要的对象。
实例
涉及三个文件:
Book.java 表示图书信息的类
package com.example.zzh.binderdemo;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; } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(this.bookId); dest.writeString(this.bookName); } protected Book(Parcel in) { this.bookId = in.readInt(); this.bookName = in.readString(); } public static final Creator<Book> CREATOR = new Creator<Book>() { @Override public Book createFromParcel(Parcel source) { return new Book(source); } @Override public Book[] newArray(int size) { return new Book[size]; } }; @Override public String toString() { return String.format("[bookId:%s, bookName:%s]", bookId, bookName); }}
Book.aidl Book类在AIDL中的声明
package com.example.zzh.binderdemo;parcelable Book;
IBookManager.aidl 定义的接口
package com.example.zzh.binderdemo;import com.example.zzh.binderdemo.Book;interface IBookManager { List<Book> getBookList(); void addBook(in Book book);}
编译后,在model工程的build/generated目录下,系统为IBookManager.aidl
生成了Binder类IBookManager.java
其中这个类核心内容是内部类Stub
和Stub的内部代理类Proxy
。
Stub
是一个Binder类,当客户端和服务端都位于同一个进程时,方法调用不会走跨进程的transact过程;
当二者在不同进程中时,方法调用需要走transact过程,由Proxy
来完成。
介绍针对这两个类的买个方法的含义:
DESCRIPTOR
Binder的唯一标识,一般用当前Binder的类名表示,如***.***.IBookManager
asInterface(android.os.IBinder obj)
用于将服务端的Binder对象转换成客户端所需的AIDL接口类型的对象,这种转换过程是区分进程,
如果客户端和服务端位于同一进程,那么这个方法返回就是服务端的Stub对象本身,反则返回的是
系统封装后的Stub.proxy对象。asBinder
返回当前Binder对象。
onTransact
这个方法运行在服务端中的Binder线程池中,当客户端发起跨进程请求时,远程请求会通过系统封装后
交由此方法来处理。当这个方法返回false时,那么客户端的请求会失败,因此可以利用这个特性来做
权限验证(避免随便一个进程都能远程调用我们的服务)。Proxy#getBookList
Proxy#addBook
这两个方法运行在客户端中。内部实现是:首先创建该方法所需要的输入型Parcel对象_data、输出型
Parcel对象_reply和返回值对象List(addBook没有返回值);然后把该方法的参数信息写入_data中
(如果有参数的话);接着调用transact方法来发起RPC
(远程过程调用)请求,同时当前线程挂起;
然后服务的onTransaction方法会被调用,直到RPC过程返回后,当前线程继续执行,并从_reply中取
出RPC过程的返回结果;最后返回_reply中的数据。
- 当客户端发起远程请求时,由于当前线程会挂起直至服务端进程返回数据,所以如果一个远程方法是耗时的,那么不能在UI线程中发起此远程请求。
- 由于服务端的Binder方法运行在Binder的线程池中,所以Binder方法不管是否耗时都应采用同步的方式去实现,因为它已经运行一个线程中。
Binder的工作机制图
Binder两个重要的方法
Binder运行在服务端进程,如果服务端进程由于某种原因异常终止,这个时候与服务端的Binder连接断裂,导致远程调用失败。
linkToDeath
unlinkToDeath
给Binder设置一个死亡代理,当Binder死亡时,收到通知,可以重新发起连接请求从而恢复连接:
首先,声明一个DeathRecipient对象。当Binder死亡时候,系统就回调DeathRecipient接口方法binderDied
,
然后就可以移除之前绑定的Binder代理并重新绑定远程服务。
IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() { @Override public void binderDied() { if (mRemoteBookManager == null) { return; } mRemoteBookManager.asBinder().unlinkToDeath(mDeathRecipient, 0); mRemoteBookManager = null; //TODO 这里重新绑定远程Service }};
其次,在客户端绑定远程Service成功后,给binder设置死亡代理
IBookManager bookManager = IBookManager.Stub.asInterface(binder);binder.linkToDeath(mDeathRecipient, 0);
另外,通过Binder的方法isBinderAlive
也可以判断Binder是否死亡。
基于AIDL的IPC
使用AIDL进行进程间通信的流程
服务端
服务端首先要创建一个Service用来监听客户端的连接请求,然后创建一个AIDL文件,将暴露给客户端的
接口在这个AIDL文件中声明,最后在Service中实现这个AIDL接口即可。客户端
首先绑定服务端的Service,绑定成功后,将服务端返回的Binder对象转成AIDL接口所属的类型,最后
在Service中实现这个AIDL接口即可。
AIDL文件支持的数据类型
- 基本数据类型(int、long、char、boolean等)
- String和CharSequence
- List: 只支持ArrayList, 里面每个元素都必须能够被AIDL支持
- Map: 只支持HashMap, 里面的每个元素都必须被AIDL支持,包括key和value
- Parcelabe: 所有实现了Parcelable接口的对象
AIDL:所有的AIDL接口本身也可以在AIDL文件中使用
其中自定义的Parcelable对象和AIDL对象必须要显式import进来,无论是否位于同一个包内。
如果AIDL文件用到了Parcelabl对象,那么必须新建一个和它同名的AIDL文件,并在其中声明它为
Parcelable类型。如上个实例中Book.aidl
RemoteCallbackList
如果服务端提供一些监听给客户端,那么我们需要采用RemoteCallbackList
,才能实现在客户端取消
注册功能。RemoteCallbackList
是系统专门提供的用于删除跨进程listener的接口。
其中原理是:虽然跨进程传输客户端的同一个对象会在服务端生成不同的对象,但是这些新生成的对象有一
共同点,那就是它们底层的Binder是同一个,利用这个特性(Array
实例
远程服务端Service的实现
public class BookManagerService extends Service { private static final String TAG = "BMS"; private AtomicBoolean mIsServiceDestoryed = new AtomicBoolean(false); private CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<Book>(); private RemoteCallbackList<IOnNewBookArrivedListener> mListenerList = new RemoteCallbackList<>(); 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 void onCreate() { super.onCreate(); mBookList.add(new Book(1, "Android")); mBookList.add(new Book(2, "IOS")); Log.d(TAG, "BMS add"); new Thread(new ServiceWorker()).start(); } @Override public IBinder onBind(Intent intent) { return mBinder; } private void onNewBookArrived(Book book) throws RemoteException { mBookList.add(book); final int N = mListenerList.beginBroadcast(); for (int i = 0; i < N; i++) { IOnNewBookArrivedListener l = mListenerList.getBroadcastItem(i); if (l != null) { l.onNewBookArrived(book); } } mListenerList.finishBroadcast();} private class ServiceWorker implements Runnable { @Override public void run() { while (!mIsServiceDestoryed.get()) { try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } int bookId = mBookList.size() + 1; Book newBook = new Book(bookId, "new book#" + bookId); try { onNewBookArrived(newBook); } catch (RemoteException e) { e.printStackTrace(); } } } }}
客户端的实现
public class BookManagerActivity extends Activity { private static final String TAG = "BookManagerActivity"; private static final int MESSAGE_NEW_BOOK_ARRIVED = 1; private IBookManager mRemoteBookManager; private Handler mHandler = new Handler(){ @Override public void handleMessage(Message msg) { switch (msg.what) { case MESSAGE_NEW_BOOK_ARRIVED: Log.d(TAG, "receive new book: " + msg.obj); break; default: break; } } }; private ServiceConnection mConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { IBookManager bookManager = IBookManager.Stub.asInterface(service); try { mRemoteBookManager = bookManager; List<Book> list = bookManager.getBookList(); Log.i(TAG, "onServiceConnected: query book list, list type:" + list.getClass().getCanonicalName()); Log.i(TAG, "onServiceConnected: query book list:" + list.toString()); Book newBook = new Book(3, "Android开发艺术探索"); bookManager.addBook(newBook); Log.i(TAG, "add Book:" + newBook); List<Book> newList = bookManager.getBookList(); Log.i(TAG, "onServiceConnected: query book list:" + newList.toString()); bookManager.registerListener(mOnNewBookArrivedListener); } catch (RemoteException e) { e.printStackTrace(); } } @Override public void onServiceDisconnected(ComponentName name) { mRemoteBookManager = null; Log.e(TAG, "binder died."); } }; private IOnNewBookArrivedListener mOnNewBookArrivedListener = new IOnNewBookArrivedListener.Stub() { @Override public void onNewBookArrived(Book newBook) throws RemoteException { mHandler.obtainMessage(MESSAGE_NEW_BOOK_ARRIVED, newBook).sendToTarget(); } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_book_manager); Intent intent = new Intent(this, BookManagerService.class); bindService(intent, mConnection, Context.BIND_AUTO_CREATE); } @Override protected void onDestroy() { if (mRemoteBookManager != null && mRemoteBookManager.asBinder().isBinderAlive()) { try { Log.i(TAG, "unregister listener:" + mOnNewBookArrivedListener); mRemoteBookManager.unregisterListener(mOnNewBookArrivedListener); } catch (RemoteException e) { e.printStackTrace(); } } unbindService(mConnection); super.onDestroy(); }}
在AIDL中使用权限验证功能的方式(保证权限验证成功的一方才能连接远程服务)
- 在onBind中验证,验证不通过返回null,这样验证失败的客户端直接无法绑定服务。
- 在服务端的onTransact方法中进行权限验证,如果验证失败就返回false,这样服务端就不会终止执行AIDL中的方法从而达到保护服务端的效果。
- Binder、AIDL学习笔记
- Android aidl Binder 机制学习
- Binder AIDL
- Binder&AIDL
- AIDL学习(三)---Binder连接池
- android学习笔记--binder
- Android学习笔记--Binder
- Android学习笔记--Binder
- Android学习笔记--Binder
- Android 学习笔记 binder
- binder学习笔记
- Binder 驱动学习笔记
- Android学习笔记--Binder
- Binder学习笔记
- Android学习笔记--Binder
- Android学习笔记--Binder
- Android学习笔记--Binder
- Binder学习笔记
- Node.js入门(一)——调用函数方式小结
- 安装docker
- HTML jquery 添加+删除+批量删除+二级联动
- 数字图像---之调色板
- 如何直接执行js代码
- Binder、AIDL学习笔记
- openVswitch(OVS)源代码分析之工作流程(数据包处理)
- nvm简介
- 数论的一些证明
- 使用windows10下搭建flask开发环境(python 3)
- 读书随笔1
- EOJ月赛总结
- linux两个线程交替打印数字
- unity 按钮绑定事件(NGUI UGUI)