在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);    }}

说明:

  1. 在Android的AIDL中,仅支持的数据类型有:
    1. 基本数据类型(int、long、char、boolean、double等);
    2. String和CharSequence;
    3. List:只支持ArrayList,里面的每个元素都必须能够被AIDL支持;
    4. Map:只支持HashMap,里面的每个元素都必须能够被AIDL支持;
    5. Parcelable:所有实现了Parcelable接口的对象;        
    6. AIDL:所有的AIDL接口本身也可以在AIDL文件中使用。
  2. Parcelable的作用是序列化对象,因为跨进程通信传输对象必须是以序列化和反序列化的方式进行。Parcelable的实现有些繁琐,但性能效率很高。
  3. 序列化功能由writeToParcel方法来完成,最终是通过Parcel中的一系列write方法来完成的。
  4. 反序列化功能由CREATEOR来完成,其内部标明了如何创建序列化对象和数组,并通过Parcel的一系列read方法来完成反序列化过程。
  5. 内容描述功能由describeContents方法来完成,几乎在所有情况下这方法都应该返回0,仅当当前对象中存在文件描述符时才返回1。

这里就不进行Parcelable详细的介绍了,大家在使用方面按照上例来一般都不会有问题。

 

第二步,创建AIDL接口文件People.aidl

package com.zyx.aidldemo;parcelable People;

说明:

  1. 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中定义了一个接口,它的功能很简单就是获取列表对象和添加对象

注意:

  1. 注意import com.zyx.aidldemo.People;这句代码,因为自定义的Parcelable对象和AIDL对象不管是否和当前的AIDL文件位于同一个包内,也必须要显式import进来。
  2. addPeople方法的参数中有in关键字,因为aidl除了基本数据类型,其它类型的参数必须标上方向:in、out或者inout,我们需根据实现需要去指定参数类型,因为这在底层实现是有开销的。
  3. 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;    }}

说明

  1. CopyOnWriteArrayListArrayList 的一个线程安全的变体,它支持并发读/写,可以自动的线程同步,并且最终返回一个新的ArrayList传递给客户端。
  2. 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();}

说明

  1. RemoteCallbackList是系统专门提供的用于删除跨进程listener接口,它是一个泛型,支持管理任意AIDL接口。另外它内部自动实现了线程同步的功能。
  2. beginBroadcast和finishBroadcast必须要配对使用,哪怕仅仅获取RemoteCallbackList中的元素个数。

 

点击下载示例源码

 

——本文部分内容参考自《Android开发艺术探索》

 

 

更多相关文章

  1. 如何确定Android中刚修改后的c/c++是否编译成功
  2. 关于Android(安卓)EditText图文混排的总结
  3. Android的事件处理机制之基于监听的事件处理
  4. Gradle Kotlin DSL , 你知道它吗?
  5. 监听Android设备网络变化 - 亲测在华为Emui8.0 以及 Android原生
  6. Android选择弹窗(自定义可扩展)
  7. 第一行代码笔记3
  8. Android(安卓)CheckBox 修改选择框
  9. [置顶] [Android(安卓)Studio 权威教程]配置出“NB”的Android(安

随机推荐

  1. android侧滑效果,SlidingMenu配置
  2. Android(安卓)自定义 View - 颜色选取器(
  3. android ListView中自定义SimpleAdapter
  4. (jenkins)hudson平台搭建android项目持续化
  5. Android突破三:Intent类
  6. 快速提高Android开发调试的使用技巧
  7. Android(安卓)adb opendir failed ,permi
  8. 编程回忆之Android回忆(AnimationDrawable
  9. Android(安卓)Widget事件
  10. 高通Android(安卓)display架构分析