学习记录,其实是反复看了好几次Binder了,做个笔记记录一下,学习的内容是在《Android 开发艺术探索》中的
Binder是安卓的一种跨进程通信(IPC)的方式。不管是在安卓系统中,还是在日常开发中都可以存在着Binder。试想一下,两个人在不同的地方要进行通信,那么他们就可以用打电话的方式。那两个进程需要通信,那么它们就可以使用Binder来进行。
aidl是什么?aidl与binder又有什么关系。aidl是一种文件,通过编写aidl文件,IDE会自动为我们生成Binder的代码。这就类似于我们创建menu,我们只是在menu文件夹下敲一敲xml格式的文件,然后IDE就会给我们生成响应的Java代码。
package com.example.aidltest;
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 out, int flags) {
out.writeInt(bookId);
out.writeString(bookName);
}
public static final Parcelable.Creator<Book> CREATOR = new Parcelable.Creator<Book>(){
@Override
public Book createFromParcel(Parcel in) {
return new Book(in);
}
@Override
public Book[] newArray(int size) {
return new Book[size];
}
};
private Book(Parcel in){
bookId = in.readInt();
bookName = in.readString();
}
}
package com.example.aidltest;
parcelable Book;
package com.example.aidltest;
import com.example.aidltest.Book;
interface IBookManager {
List<Book> getBookList();
void addBook(in Book book);
}
我使用的是Android Studio的开发环境
生成的Binder类位置在根目录下,位置如下所示
直接从最外层来看有3个部分:Default类、Stub类、方法声明。关键代码其实都在Stub类中
整个IBookManager是一个接口,同时也继承了IInterface这个接口,这里声明了两个我们在aidl文件中定义的方法getBookList和addBook,如下所示
自动生成的注释也有说明,这个类是接口IBookManger的默认实现,里面默认实现了我们在aidl中声明的两个方法getBookList和addBook,这里默认实现基本是空的,如下。我们就跳过
最重要的类,这里分为两块,一块是Stub自己的逻辑,一块是Stub类中的内部类Proxy类。
static final int TRANSACTION_getBookList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_addBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
public static com.example.aidltest.IBookManager asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.example.aidltest.IBookManager))) {
return ((com.example.aidltest.IBookManager)iin);
}
return new com.example.aidltest.IBookManager.Stub.Proxy(obj);
}
从代码逻辑来看,伪代码如下
@Override public android.os.IBinder asBinder()
{
return this;
}
@Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
{
java.lang.String descriptor = DESCRIPTOR;
switch (code)
{
case INTERFACE_TRANSACTION:
{
reply.writeString(descriptor);
return true;
}
case TRANSACTION_getBookList:
{
data.enforceInterface(descriptor);
java.util.List<com.example.aidltest.Book> _result = this.getBookList();
reply.writeNoException();
reply.writeTypedList(_result);
return true;
}
case TRANSACTION_addBook:
{
data.enforceInterface(descriptor);
com.example.aidltest.Book _arg0;
if ((0!=data.readInt())) {
_arg0 = com.example.aidltest.Book.CREATOR.createFromParcel(data);
}
else {
_arg0 = null;
}
this.addBook(_arg0);
reply.writeNoException();
return true;
}
default:
{
return super.onTransact(code, data, reply, flags);
}
}
}
根据code执行不同的逻辑,这里的第2、3个代码框的code就是之前定义的整形ID标识
getBookList时会获取到图书列表_result然后写入reply中,再返回true代表获取了请求。
addBook时会创建一个Book实例,将参数的data转换后再调用addBook()方法添加图书,最后返回true代表获取了请求
上面我们看到,如果客户端与服务端不在一个进程,那么就会调用到Proxy,这里主要看两个方法一个是getBookList(),另一个是addBook(),就是我们声明的并且一直提到那两个方法。
@Override public java.util.List<com.example.aidltest.Book> getBookList() throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.util.List<com.example.aidltest.Book> _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
boolean _status = mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);
if (!_status && getDefaultImpl() != null) {
return getDefaultImpl().getBookList();
}
_reply.readException();
_result = _reply.createTypedArrayList(com.example.aidltest.Book.CREATOR);
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
先创建Parcel类型的_data和_reply,再List类型的结果_result,如果有参数,则将参数写入_data,再调用mRemote的transact方法,传入请求code,_data信息,_reply。这样就发起了RPC(远程过程调用),然后当前线程挂起,服务端的onTransact就会被调用,直到放回了结果,这样当前线程就会继续执行,如果返回值为true,表示服务端接收了我们的请求,就从_reply中取出结果并构造到_result中,最后对Parcel对象进行回收并返回_result。
@Override public void addBook(com.example.aidltest.Book book) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
if ((book!=null)) {
_data.writeInt(1);
book.writeToParcel(_data, 0);
}
else {
_data.writeInt(0);
}
boolean _status = mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply, 0);
if (!_status && getDefaultImpl() != null) {
getDefaultImpl().addBook(book);
return;
}
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
这个方法过程与getBookList()方法的过程类似,这里就不再展开
小时候你妈妈要炒菜,发现家里没有菜了,拿了个菜篮让你去隔壁邻居家借点菜回来炒。自己的家跟邻居的家就是两个进程,妈妈要炒菜,先在家里自己找,如果有,那就直接用,如果没有就有你这个小朋友做代理去邻居家要,并且拿个菜篮子让你把菜放里面包装好带回家里来。在作为小代理的你还没回来时,妈妈没有菜炒,只能一直在家等你回来,直到你回来了,妈妈才继续干接下来的事。
我把相关的代码放在github上了,如果有需要的话,大家自取
下一篇是Binder在两个进程的使用
本文地址:https://blog.csdn.net/weixin_42530254/article/details/107409520
如对本文有疑问, 点击进行留言回复!!
LongClick原理、上下文菜单原理、EditText长按弹窗原理、WebView长按弹窗自定义、修复WebView全选重复bug ———————————————— 版权声明:本文为CSDN博主「
JobScheduler 实现 特定时间,特定条件(系统空闲,电池电量,磁盘空间 ……)下执行任务
网友评论