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); } }
|
需要注意的地方
- 要往子进程传递一个监听类如:
IOnNewBookArrivedListener
,那么该类必须是Binder的子类;由于跨进程通讯该监听类会序列化传递到子进程再反序列化重新生成一个对象,所以用普通的List去存储监听类是无法移除的;这里要使用RemoteCallBackList
的register
方法和unregister
来存取;
- 由于跨进程传输对象的底层Binder对象是同一个,
RemoteCallBackList
便是使用这一点来进行添加移除
RemoteCallBackList
的遍历需要通过beginBroadcast
和finishBroadcast
配对使用
跨进程内存泄漏
在跨进程调用IPC方法,即当前进程传递回调监听Listener
到目标进程,在取消监听后该Listener
不会立马被回收,会存入一个静态链表ReferenceQueue
中,有一个专门的守护线程去维护这个链表,当该线程执行的时候会弹出里面的对象,执行他们的finalize
方法,下次执行GC时才会被回收。
内存泄漏:
直接原因时该守护线程优先级比较低,运行的时间比较少。当ReferenceQueue
持有的对象过多时,守护线程来不及弹出对象,造成无论如何GC都不会回收。
如果监听类持有Context
等需要回收的对象,会造成内存泄漏
其中Android9.0之前对象会被FinalizerFeference
持有并存入ReferenceQueue
:
Android9.0起(含9.0)对象会被Cleaner
持有并存入ReferenceQueue
: