AIDL

AIDL

相关文章:

关于Binder

注:
一、如果不需要跨进程,则应继承Binder并返回给绑定服务调用处;参考:https://developer.android.com/guide/components/bound-services#Binder
二、如果需要跨进程但不需要处理多线程,则建议使用Messenger来实现接口;参考:https://developer.android.com/guide/components/bound-services#Messenger

第一

注:最好把所有的aidl文件放在同一个包中,因为另一个进程调用时需要一样的包名才能生效

新建一个以aidl为后缀的文件,其它类型参数必须标上方向:in out 或者inout
in表示输入型参数,即数据只能从客户端传递向服务端,由服务端传递向客户端的参数会变成默认值
out表示输出型参数,即数据只能从服务端传递向客户端,由客户端传递向服务的参数会变成默认值
inout表示输入输出型,可以双向传递数据

IBookManager.aidl

1
2
3
4
5
6
7
package com.test.aidl;
import com.test.aidl.Book;

interface IBookManager {
Book getBookList();
void setBook(in Book book);
}
第二

由于用到了自定义的Parcelable对象,必须新建一个和它同名的AIDL文件,并在其中声明它为Parcelable类型
例:上面我们用到了Book这个类,所以要创建
Book.aidl

1
2
package com.test.aidl;
parcelable Book;

跨进程数据传递需要赋值封装,这样才能跨进程传递数据
Book.class

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
public class Book implements Parcelable{
public int mId;
public String mName;

public Book(){
}

public Book(Parcel in) {
//一定要按照字段顺序写
mId = in.readInt();
mName = in.readString();
}

@Override
public int describeContents() {
return 0;
}

@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(mId);
dest.writeString(mName);
}

public static final Parcelable.Creator<Book> CREATOR = new Parcelable
.Creator<Book>() {
public Book createFromParcel(Parcel in) {
return new Book(in);
}

public Book[] newArray(int size) {
return new Book[size];
}
};
}
第三

远程服务端Service的实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package com.test.service;
public class BookManagerService extends Service {

private Book mBook = new Book();

private Binder mBinder = new IBookManager.Stub(){
@Override
public Book getBookList() throws RemoteException{
return mBook;
}
@Override
public void setBook(Book book) throws RemoteException {
mBook = book;
}
}

@Override
public void onCreate(){
super.onCreate();
}

@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
}
第四

客户端的实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class BookManagerActivity extends Activity{

private ServiceConnection mConnection = new ServiceConnection(){
public void onServiceConnected(ComponentName className, IBinder service) {
IBookManager bookManager = IBookManager.Stub.asInterface(service);
bookManager.getBookList();//做你要做的操作
}

public void onServiceDisconnected(ComponentName className) {

}
}

@Override
public void onCreate(Bundle savedInstanceState) {
Intent intent = new Intent(this,BookManagerService.class);
bindService(intent,mConnection,Context.BIND_AUTO_CREATE);
}

@Override
public void onDestroy(){
super.onDestroy();
unbindService(mConnection);
}
}
需要注意的地方
  1. 要往子进程传递一个监听类如:IOnNewBookArrivedListener ,那么该类必须是Binder的子类;由于跨进程通讯该监听类会序列化传递到子进程再反序列化重新生成一个对象,所以用普通的List去存储监听类是无法移除的;这里要使用RemoteCallBackListregister方法和unregister来存取;
    • 由于跨进程传输对象的底层Binder对象是同一个,RemoteCallBackList便是使用这一点来进行添加移除
    • RemoteCallBackList的遍历需要通过beginBroadcastfinishBroadcast配对使用
跨进程内存泄漏

在跨进程调用IPC方法,即当前进程传递回调监听Listener到目标进程,在取消监听后该Listener不会立马被回收,会存入一个静态链表ReferenceQueue中,有一个专门的守护线程去维护这个链表,当该线程执行的时候会弹出里面的对象,执行他们的finalize方法,下次执行GC时才会被回收。

内存泄漏:
直接原因时该守护线程优先级比较低,运行的时间比较少。当ReferenceQueue持有的对象过多时,守护线程来不及弹出对象,造成无论如何GC都不会回收。
如果监听类持有Context等需要回收的对象,会造成内存泄漏

其中Android9.0之前对象会被FinalizerFeference持有并存入ReferenceQueue

Android9.0起(含9.0)对象会被Cleaner持有并存入ReferenceQueue