Android中AIDL的使用(一) 之 AIDL经典示例
在Android开发中IPC(Inter-Process Communication的缩写)多进程通信的使用场景很多。一般地由于某些功能模块需要特殊原因运行在单独的进程中,又或者为了加大一个应用可使用的内存,因为Android对单个应用使用的最大内存是有限制的,又或者需要做一些非常耗内存而又不好回收的事情,在事情完成后直接结束掉进程,等。开启多进程模式很简单,就是给四大组件指定android:process属性即可。IPC的方式有多种,在日常开发中涉及进程间通信时的首选应该就是AIDL了。我们就来以示例的方式学习AIDL的使用。
示例
第一步,新建一个People的类,使它继承Parcelable,代码如下:
package com.zyx.aidldemo;import android.os.Parcel;import android.os.Parcelable;public class People implements Parcelable { public int pId; public String pName; public People() { } public People(int pId, String pName) { this.pId = pId; this.pName = pName; } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(pId); dest.writeString(pName); } public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { public People createFromParcel(Parcel in) { return new People(in); } public People[] newArray(int size) { return new People[size]; } }; private People(Parcel in) { pId = in.readInt(); pName = in.readString(); } @Override public String toString() { return String.format("pId:%s, pName:%s", pId, pName); }}
说明:
- 在Android的AIDL中,仅支持的数据类型有:
- 基本数据类型(int、long、char、boolean、double等);
- String和CharSequence;
- List:只支持ArrayList,里面的每个元素都必须能够被AIDL支持;
- Map:只支持HashMap,里面的每个元素都必须能够被AIDL支持;
- Parcelable:所有实现了Parcelable接口的对象;
- AIDL:所有的AIDL接口本身也可以在AIDL文件中使用。
- Parcelable的作用是序列化对象,因为跨进程通信传输对象必须是以序列化和反序列化的方式进行。Parcelable的实现有些繁琐,但性能效率很高。
- 序列化功能由writeToParcel方法来完成,最终是通过Parcel中的一系列write方法来完成的。
- 反序列化功能由CREATEOR来完成,其内部标明了如何创建序列化对象和数组,并通过Parcel的一系列read方法来完成反序列化过程。
- 内容描述功能由describeContents方法来完成,几乎在所有情况下这方法都应该返回0,仅当当前对象中存在文件描述符时才返回1。
这里就不进行Parcelable详细的介绍了,大家在使用方面按照上例来一般都不会有问题。
第二步,创建AIDL接口文件People.aidl
package com.zyx.aidldemo;parcelable People;
说明:
- AIDL文件中用到的自定义的Parcelable对象,必须新建一个和它同名的AIDL文件,而且要在其中声明它为parcelable类型
第三步,创建AIDL接口文件IPeopleManager.aidl
package com.zyx.aidldemo;import com.zyx.aidldemo.People;interface IPeopleManager { List getPeopleList(); void addPeople(in People people);}
IPeopleManager.adil中定义了一个接口,它的功能很简单就是获取列表对象和添加对象
注意:
- 注意import com.zyx.aidldemo.People;这句代码,因为自定义的Parcelable对象和AIDL对象不管是否和当前的AIDL文件位于同一个包内,也必须要显式import进来。
- addPeople方法的参数中有in关键字,因为aidl中除了基本数据类型,其它类型的参数必须标上方向:in、out或者inout,我们需根据实现需要去指定参数类型,因为这在底层实现是有开销的。
- AIDL接口中只支持方法,不支持声明静态常量
第四步,此时,准备功能已经完成了,先对工程进行初始的编译,因为只要在编译后才可以将aidl文件转化成IPeopleManager.java文件:build\generated\source\aidl\debug\com\zyx\aidldemo\IPeopleManager.java
第五步,新建一个服务PeopleManagerService.java,并在AndroidManifest.xml中特意指定它在不同的进程android:process=":remote"代码如:
package com.zyx.aidldemo;import android.app.Service;import android.content.Intent;import android.os.Binder;import android.os.IBinder;import android.os.RemoteException;import java.util.List;import java.util.concurrent.CopyOnWriteArrayList;public class PeopleManagerService extends Service { // 是ArrayList 的一个线程安全的变体 private CopyOnWriteArrayList mPeopleList = new CopyOnWriteArrayList(); private Binder mBinder = new IPeopleManager.Stub() { @Override public List getPeopleList() throws RemoteException { return mPeopleList; } @Override public void addPeople(People people) throws RemoteException { mPeopleList.add(people); } }; @Override public void onCreate() { super.onCreate(); } @Override public IBinder onBind(Intent intent) { return mBinder; }}
说明:
- CopyOnWriteArrayList是ArrayList 的一个线程安全的变体,它支持并发读/写,可以自动的线程同步,并且最终返回一个新的ArrayList传递给客户端。
- IPeopleManager.Stub返回一个Binder对象,它的实现就是上面第四步中的IPeopleManager.java里面自动生成
第六步,在MainActivity中绑定PeopleManagerService服务,并在绑定回调中进行跨进程添加对象和获得列表对象:
package com.zyx.aidldemo;import android.content.ComponentName;import android.content.Context;import android.content.Intent;import android.content.ServiceConnection;import android.os.IBinder;import android.os.RemoteException;import android.support.v7.app.AppCompatActivity;import android.os.Bundle;import android.util.Log;import java.util.List;public class MainActivity extends AppCompatActivity { private ServiceConnection mConnection = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder service) { IPeopleManager peopleManager = IPeopleManager.Stub.asInterface(service); try { People people = new People(1, "子云心"); // 添加对象 peopleManager.addPeople(people); // 获取对象列表 List list = peopleManager.getPeopleList(); } catch (RemoteException e) { e.printStackTrace(); } } public void onServiceDisconnected(ComponentName className) { } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Intent intent = new Intent(this, PeopleManagerService.class); bindService(intent, mConnection, Context.BIND_AUTO_CREATE); } @Override protected void onDestroy() { unbindService(mConnection); super.onDestroy(); }}
就这样,一次完整的使用AIDL进行IPC实例就完成。
示例扩展1
在实际开发中远不止上例如此简单,假如现在需要在客户端增加一个监听,好让每次列表更新后就会进行一次通知,也就是一个典型的观察者模式。所以就有了以下的扩展。
第一步,因为AIDL中无法使用普通接口,所以得创建IOnNewPeopleListener.aidl文件:
package com.zyx.aidldemo;import com.zyx.aidldemo.People;interface IOnNewPeopleListener { void onNewPeople(in People newPeople);}
第二步,修改IPeopleManager.adil,加两个接口:
package com.zyx.aidldemo;import com.zyx.aidldemo.People;import com.zyx.aidldemo.IOnNewPeopleListener;interface IPeopleManager { List getPeopleList(); void addPeople(in People people); // 扩展新增代码:新增接口 void registerListener(IOnNewPeopleListener listener); void unregisterListener(IOnNewPeopleListener listener);}
第三步,在执行编译后生成新的IPeopleManager.java后,进行PeopleManagerService的修改:
package com.zyx.aidldemo;import android.app.Service;import android.content.Intent;import android.os.Binder;import android.os.IBinder;import android.os.RemoteException;import java.util.List;import java.util.concurrent.CopyOnWriteArrayList;public class PeopleManagerService extends Service { // 是ArrayList 的一个线程安全的变体 private CopyOnWriteArrayList mPeopleList = new CopyOnWriteArrayList(); // 扩展新增代码:用于存储回调监听的接口列表 private CopyOnWriteArrayList mListenerList = new CopyOnWriteArrayList(); private Binder mBinder = new IPeopleManager.Stub() { @Override public List getPeopleList() throws RemoteException { return mPeopleList; } @Override public void addPeople(People people) throws RemoteException { mPeopleList.add(people); // 扩展新增代码:新加对象后执行监听回调 for (int i = 0; i < mListenerList.size(); i++) { IOnNewPeopleListener listener = mListenerList.get(i); listener.onNewPeople(people); } } // 扩展新增代码:添加监听接口到列表 @Override public void registerListener(IOnNewPeopleListener listener) throws RemoteException { if (!mListenerList.contains(listener)) { mListenerList.add(listener); } } // 扩展新增代码:移除监听列表中的监听接口 @Override public void unregisterListener(IOnNewPeopleListener listener) throws RemoteException { if (mListenerList.contains(listener)) { mListenerList.remove(listener); } } }; @Override public void onCreate() { super.onCreate(); } @Override public IBinder onBind(Intent intent) { return mBinder; }}
最后一步,就是修改客户端MainActivity.java文件,客户端要注册IOnNewPeopleListener:
package com.zyx.aidldemo;import android.content.ComponentName;import android.content.Context;import android.content.Intent;import android.content.ServiceConnection;import android.os.IBinder;import android.os.RemoteException;import android.support.v7.app.AppCompatActivity;import android.os.Bundle;import android.util.Log;import java.util.List;public class MainActivity extends AppCompatActivity { IPeopleManager mPeopleManager; private ServiceConnection mConnection = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder service) { mPeopleManager = IPeopleManager.Stub.asInterface(service); try { // 扩展新增代码:注册监听 mPeopleManager.registerListener(mIOnNewPeopleListener); People people = new People(1, "子云心"); mPeopleManager.addPeople(people); List list = mPeopleManager.getPeopleList(); } catch (RemoteException e) { e.printStackTrace(); } } public void onServiceDisconnected(ComponentName className) { mPeopleManager = null; } }; // 扩展新增代码:定义监听接口 private IOnNewPeopleListener mIOnNewPeopleListener = new IOnNewPeopleListener.Stub() { @Override public void onNewPeople(People newPeople) throws RemoteException { // TODO 这里子线程,如若需要修改UI请使用 Handler } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Intent intent = new Intent(this, PeopleManagerService.class); bindService(intent, mConnection, Context.BIND_AUTO_CREATE); } @Override protected void onDestroy() { // 扩展新增代码:移除监听 if (mPeopleManager != null && mPeopleManager.asBinder().isBinderAlive()) { try { mPeopleManager.unregisterListener(mIOnNewPeopleListener); } catch (RemoteException e) { e.printStackTrace(); } } unbindService(mConnection); super.onDestroy(); }}
示例扩展2
从扩展1中,其实存在一个bug,因为客户端MainActivity的移除监听根本就没有成功。why?因为客户端注册和移除注册过程中使用的虽是同一个客户端对象,但通过Binder传递到服务端后,产生了两个不同的对象,因为对象是不能跨进程直接传输的,对象的跨进程传输本质上都是反序列化的过程。所以就有了扩展2的修改:
第一步,修改服务端PeopleManagerService.java的几个方法代码:
// 扩展新增代码:用于存储回调监听的接口列表// private CopyOnWriteArrayList mListenerList = new CopyOnWriteArrayList();// 新扩展修改代码:将 CopyOnWriteArrayList 替换掉变成 RemoteCallbackListprivate RemoteCallbackList mListenerList = new RemoteCallbackList();
@Overridepublic void addPeople(People people) throws RemoteException { mPeopleList.add(people); // 扩展新增代码:新加对象后执行监听回调 // for (int i = 0; i < mListenerList.size(); i++) { // IOnNewPeopleListener listener = mListenerList.get(i); // listener.onNewPeople(people); // } // 新扩展修改代码 final int N = mListenerList.beginBroadcast(); for (int i = 0; i < N; i++) { IOnNewPeopleListener listener = mListenerList.getBroadcastItem(i); if (listener != null) { try { listener.onNewPeople(people); } catch (RemoteException e) { e.printStackTrace(); } } } mListenerList.finishBroadcast();}// 扩展新增代码:添加监听接口到列表@Overridepublic void registerListener(IOnNewPeopleListener listener) throws RemoteException { // if (!mListenerList.contains(listener)) { // mListenerList.add(listener); // } // 新扩展修改代码 boolean success = mListenerList.register(listener); final int N = mListenerList.beginBroadcast(); mListenerList.finishBroadcast();}// 扩展新增代码:移除监听列表中的监听接口@Overridepublic void unregisterListener(IOnNewPeopleListener listener) throws RemoteException { // if (mListenerList.contains(listener)) { // mListenerList.remove(listener); // } // 新扩展修改代码 boolean success = mListenerList.unregister(listener); final int N = mListenerList.beginBroadcast(); mListenerList.finishBroadcast();}
说明:
- RemoteCallbackList是系统专门提供的用于删除跨进程listener接口,它是一个泛型,支持管理任意AIDL接口。另外它内部自动实现了线程同步的功能。
- beginBroadcast和finishBroadcast必须要配对使用,哪怕仅仅获取RemoteCallbackList中的元素个数。
点击下载示例源码
——本文部分内容参考自《Android开发艺术探索》
更多相关文章
- 如何确定Android中刚修改后的c/c++是否编译成功
- 关于Android(安卓)EditText图文混排的总结
- Android的事件处理机制之基于监听的事件处理
- Gradle Kotlin DSL , 你知道它吗?
- 监听Android设备网络变化 - 亲测在华为Emui8.0 以及 Android原生
- Android选择弹窗(自定义可扩展)
- 第一行代码笔记3
- Android(安卓)CheckBox 修改选择框
- [置顶] [Android(安卓)Studio 权威教程]配置出“NB”的Android(安