Android进程间通信(二)

在上一节中,主要阐述了Parcelable的序列化和AIDL文件的书写规范,以及相应的示例。这一节,将实现一个完整的通过AIDL实现进程通信的示例。首先要说明的是,这个例子并不符合Android的开发规范, AIDL是在涉及跨应用并需要处理多线程的情况下才有必要使用的,而这个例子并不涉及跨应用的情况。为了方便起见,此例将客户端和服务端放在了同一应用下的不同进程。(AIDL代码和Parcelable代码沿用上文示例)

Android进程间通信(一)

准备工作

在示例中,创建一个简单的Activity和一个简单的Service。以Activity作为客户端, 以Service作为服务端, 在AndroidManifest.xml文件中将service的进程设为 :remote

接下来实现Service中的相关内容, 首先要实现Binder

//定义一个Binder, IMyAidlInterface.Stub类继承自Binder类, 并实现了IMyAidlInterface接口的抽象类, 经AIDL文件编译后生成
private final IMyAidlInterface.Stub mBinder = new IMyAidlInterface.Stub() {
        @Override
        public NewsEntity getData(int id, String title) throws RemoteException {
            System.out.println("Server: Thread.currentThread().getName() = " + Thread.currentThread().getName());
            try {
                Thread.sleep(2000);
                callbackClient();
            }catch (Exception e){
                e.printStackTrace();
            }
            return new NewsEntity(id, title);
        }
    };
@Nullable
@Override
public IBinder onBind(Intent intent) {
    return mBinder;
}

Activity 的相关实现

//在Activity中要实现ServiceConnection接口的相关方法, 例如.
private ServiceConnection mConnection = new ServiceConnection() {
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        iService = IMyAidlInterface.Stub.asInterface(service);

        System.out.println("mConnection: Thread.currentThread().getName() = " + Thread.currentThread().getName());
        try {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        NewsEntity entity = iService.getData(1, "Hello Activity");
                        System.out.println(entity.toString());
                    }catch (Exception e){
                        e.printStackTrace();
                    }
                }
            }).start();


        }catch (Exception e){
            e.printStackTrace();
        }
    }

    @Override
    public void onServiceDisconnected(ComponentName name) {

    }
};

客户端调用服务端

首先要在Activity中绑定Service

Intent intent = new Intent(this, RemoteService.class);
intent.setAction(RemoteService.class.getName());
bindService(intent, mConnection, BIND_AUTO_CREATE);

在与Service连接成功后, 需要获取到对应的接口对象

iService = IMyAidlInterface.Stub.asInterface(service);

调用Service方法

new Thread(new Runnable() {
    @Override
    public void run() {
        try {
            NewsEntity entity = iService.getData(1, "Hello Activity");
            System.out.println(entity.toString());
        }catch (Exception e){
            e.printStackTrace();
        }
    }
).start();

为什么另起线程调用?

在AIDL文件中, 若没有在对应的方法中声明oneway, 则调线程将会处于阻塞状态,直到被调用的方法执行完毕.在客户端的主线程中跨进程调用,有可能导致ANR的发生.

服务端回调客户端

要实现服务端回调客户端, 需要借助到RemoteCallbackList类.RemoteCallbackList的主要作用是为了负责维护一个远程接口列表,比如执行服务端对客户端的回调.

  1. 新建一个.aidl文件: IClientInterface.aidl
// IClientInterface.aidl
package th.how.bean;

// Declare any non-default types here with import statements

interface IClientInterface {
    void callbackClient();
}
  1. 修原来的.aidl文件,新添加两个方:

register(IClientInterface callback);

unRegister(IClientInterface callback);

package th.how.bean;
import th.how.bean.NewsEntity;
import th.how.bean.IClientInterface;

interface IMyAidlInterface {
    NewsEntity getData(int id, String title);
    void register(IClientInterface callback);
    void unRegister(IClientInterface callback);
}
  1. 修改Service文件, 增加新的RemoteCallbackList成员, 和修改Binder, 实现aidl文件中新增的两个方法, 以及添加一个回调事件:callbackClient().
private RemoteCallbackList mCallback = new RemoteCallbackList<>();

private final IMyAidlInterface.Stub mBinder = new IMyAidlInterface.Stub() {
    @Override
    public NewsEntity getData(int id, String title) throws RemoteException {
        System.out.println("Server: Thread.currentThread().getName() = " + Thread.currentThread().getName());
        try {
            Thread.sleep(2000);
            callbackClient();
        }catch (Exception e){
            e.printStackTrace();
        }
        return new NewsEntity(id, title);
    }

    @Override
    public void register(IClientInterface callback) throws RemoteException {
        mCallback.register(callback);
    }

    @Override
    public void unRegister(IClientInterface callback) throws RemoteException {
        mCallback.unregister(callback);
    }
};
    
private void callbackClient(){
    int num = mCallback.beginBroadcast();
    for(int i = 0; i < num; i ++){
        IClientInterface iClientInterface = mCallback.getBroadcastItem(i);
        if(iClientInterface != null){
            try{
                iClientInterface.callbackClient();
            }catch(Exception e){
                e.printStackTrace();
            }
        }
    }
    mCallback.finishBroadcast();
}
  1. 在客户端实现IClientInterface.aidl编译后生成的接口, 并调用service 的register方法
private IClientInterface client = new IClientInterface.Stub() {
    @Override
    public void callbackClient() throws RemoteException {
        System.out.println("Client: Thread.currentThread().getName() = " + Thread.currentThread().getName());
    }
};

private ServiceConnection mConnection = new ServiceConnection() {
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        iService = IMyAidlInterface.Stub.asInterface(service);
        System.out.println("mConnection: Thread.currentThread().getName() = " + Thread.currentThread().getName());
        try {
            iService.register(client);
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        NewsEntity entity = iService.getData(1, "Hello Activity");
                        System.out.println(entity.toString());
                    }catch (Exception e){
                        e.printStackTrace();
                    }
                }
            }).start();

        }catch (Exception e){
            e.printStackTrace();
        }
    }

    @Override
    public void onServiceDisconnected(ComponentName name) {

    }
};

通过以上步骤,就大概实现了Service和Client的双向通信.

补充

  • DeathRecipient IBinder的死亡通知机制

DeathRecipient是一个接口类,设计出来是为了当对应Ibinder的进程死亡时另一进程能接受回调通知

IBinder.java

/**
     *为这个BInder注册一个死亡通知容器.如果这个Binder意外死
     *亡,则对应的DeathRecipient对象的binderDied方法将会被调
     *用
     *它只能用来接受来自远端Binder的通知,而不能用来处理本地
     *IBinder的死亡
     * Register the recipient for a notification if this binder
     * goes away.  If this binder object unexpectedly goes away
     * (typically because its hosting process has been killed),
     * then the given {@link DeathRecipient}'s
     * {@link DeathRecipient#binderDied DeathRecipient.binderDied()} method
     * will be called.
     * 
     * 

You will only receive death notifications for remote binders, * as local binders by definition can't die without you dying as well. * * @throws RemoteException if the target IBinder's * process has already died. * * @see #unlinkToDeath */ public void linkToDeath(DeathRecipient recipient, int flags) throws RemoteException;

/**
     *移除已经注册了的死亡通知, 如果这个IBinder对象死掉,容器也不会再
     *发出通知
     * Remove a previously registered death notification.
     * The recipient will no longer be called if this object
     * dies.
     * 
     * @return {@code true} if the recipient is successfully
     * unlinked, assuring you that its
     * {@link DeathRecipient#binderDied DeathRecipient.binderDied()} method
     * will not be called;  {@code false} if the target IBinder has already
     * died, meaning the method has been (or soon will be) called.
     * 
     * @throws java.util.NoSuchElementException if the given
     * recipient has not been registered with the IBinder, and
     * the IBinder is still alive.  Note that if the recipient
     * was never registered, but the IBinder has already died, then this
     * exception will not be thrown, and you will receive a false
     * return value instead.
     */
    public boolean unlinkToDeath(DeathRecipient recipient, int flags);
  • RemoteCallbackList

负责维护一个远程接口列表, 如执行从服务端到客户端的回调.它保持了对一系列注册的回调事件的追踪.通过调用IInterface.asBinder()方法可以分辨出它们.

使用mCallbacks的对象锁,应对多线程调用.并在不持有列表锁的情况下,可以线程安全的遍历列表.

public class RemoteCallbackList {

    //一个存储IBinder与Callback键值对的Map
    /*package*/ ArrayMap mCallbacks
            = new ArrayMap();
    private Object[] mActiveBroadcast;
    private int mBroadcastCount = -1;
    private boolean mKilled = false;
    
    //实现DeathRecipient接口, 当IBinder对应的进程死亡后,移除对应的Callback
    private final class Callback implements IBinder.DeathRecipient {
        final E mCallback;
        final Object mCookie;
        
        Callback(E callback, Object cookie) {
            mCallback = callback;
            mCookie = cookie;
        }

        public void binderDied() {
            synchronized (mCallbacks) {
                mCallbacks.remove(mCallback.asBinder());
            }
            onCallbackDied(mCallback, mCookie);
        }
    }

    /**
     * Simple version of {@link RemoteCallbackList#register(E, Object)}
     * that does not take a cookie object.
     */
    public boolean register(E callback) {
        return register(callback, null);
    }
    /**
     *使用synchronized关键字, 保证对mCallbacks操作的线程安全
     *构造Callback对象, 完成binder注册死亡通知事件,
     *存储键值对
     * @see #unregister
     * @see #kill
     * @see #onCallbackDied
     */
    public boolean register(E callback, Object cookie) {
        synchronized (mCallbacks) {
            if (mKilled) {
                return false;
            }
            IBinder binder = callback.asBinder();
            try {
                Callback cb = new Callback(callback, cookie);
                binder.linkToDeath(cb, 0);
                mCallbacks.put(binder, cb);
                return true;
            } catch (RemoteException e) {
                return false;
            }
        }
    }

    /**
     *从mCallbacks中移除对应的callback
     *并解绑死亡通知
     * @see #register
     */
    public boolean unregister(E callback) {
        synchronized (mCallbacks) {
            Callback cb = mCallbacks.remove(callback.asBinder());
            if (cb != null) {
                cb.mCallback.asBinder().unlinkToDeath(cb, 0);
                return true;
            }
            return false;
        }
    }

    /**
     *清空mCallbacks
     *
     * @see #register
     */
    public void kill() {
        synchronized (mCallbacks) {
            for (int cbi=mCallbacks.size()-1; cbi>=0; cbi--) {
                Callback cb = mCallbacks.valueAt(cbi);
                cb.mCallback.asBinder().unlinkToDeath(cb, 0);
            }
            mCallbacks.clear();
            mKilled = true;
        }
    }

    /**
     *callback对应的进程死亡后的回调
     */
    public void onCallbackDied(E callback) {
    }
    
    /**
     * Called when the process hosting a callback in the list has gone away.
     * The default implementation calls {@link #onCallbackDied(E)}
     * for backwards compatibility.
     * 
     * @param callback The callback whose process has died.  Note that, since
     * its process has died, you can not make any calls on to this interface.
     * You can, however, retrieve its IBinder and compare it with another
     * IBinder to see if it is the same object.
     * @param cookie The cookie object original provided to
     * {@link #register(E, Object)}.
     * 
     * @see #register
     */
    public void onCallbackDied(E callback, Object cookie) {
        onCallbackDied(callback);
    }

    /**
     *准备调用已经注册的回调, 为数组mActiveBroadcast赋值
     *需要注意的是同一时间,只能有一个broadcast是激活的.
     * @see #getBroadcastItem
     * @see #finishBroadcast
     */
    public int beginBroadcast() {
        synchronized (mCallbacks) {
            if (mBroadcastCount > 0) {
                throw new IllegalStateException(
                        "beginBroadcast() called while already in a broadcast");
            }
            
            final int N = mBroadcastCount = mCallbacks.size();
            if (N <= 0) {
                return 0;
            }
            Object[] active = mActiveBroadcast;
            if (active == null || active.length < N) {
                mActiveBroadcast = active = new Object[N];
            }
            for (int i=0; i
     * This function is useful to decide whether to schedule a broadcast if this
     * requires doing some work which otherwise would not be performed.
     * 

* * @return The size. */ public int getRegisteredCallbackCount() { synchronized (mCallbacks) { if (mKilled) { return 0; } return mCallbacks.size(); } } }

RemoteCallbackList帮助我们维护了一个Map,并且处理了远端进程死亡后的事件, 从Map中移除了对应的callback,实现了对资源的清理.

Binder连接池

Binder连接池是为了解决客户端通过多个AIDL的方式实现IPC需要创建多个Service的问题.
可以参考这篇文章

Android IPC机制(四):细说Binder连接池

本文完整代码

NewsEntity.aidl

package th.how.bean;
parcelable NewsEntity;

IMyAidlInterface.aidl

package th.how.bean;
import th.how.bean.NewsEntity;
import th.how.bean.IClientInterface;

interface IMyAidlInterface {
    NewsEntity getData(int id, String title);
    void register(IClientInterface callback);
    void unRegister(IClientInterface callback);
}

IClientInterface.aidl

package th.how.bean;

interface IClientInterface {
    void callbackClient();
}

RemoteService.java

package th.ipc.aidl;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.support.annotation.Nullable;

import th.how.bean.IClientInterface;
import th.how.bean.IMyAidlInterface;
import th.how.bean.NewsEntity;
import th.how.utils.DpUtils;

/**
 * Created by me_touch on 2017/8/16.
 *
 */

public class RemoteService extends Service{

    private RemoteCallbackList mCallback = new RemoteCallbackList<>();

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

    private final IMyAidlInterface.Stub mBinder = new IMyAidlInterface.Stub() {
        @Override
        public NewsEntity getData(int id, String title) throws RemoteException {
            System.out.println("Server: Thread.currentThread().getName() = " + Thread.currentThread().getName());
            try {
                Thread.sleep(2000);
                int[] value = {10,20,1,5};
                int[] space = {6, 3, 12, 1};
                int value1 = DpUtils.completePack(4, space, value, 11);
                System.out.println("value1 = " + value1);
                callbackClient();
            }catch (Exception e){
                e.printStackTrace();
            }
            return new NewsEntity(id, title);
        }

        @Override
        public void register(IClientInterface callback) throws RemoteException {
            mCallback.register(callback);
        }

        @Override
        public void unRegister(IClientInterface callback) throws RemoteException {
            mCallback.unregister(callback);
        }
    };

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

    @Override
    public boolean onUnbind(Intent intent) {
        return super.onUnbind(intent);
    }

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

    private void callbackClient(){
        int num = mCallback.beginBroadcast();
        for(int i = 0; i < num; i ++){
            IClientInterface iClientInterface = mCallback.getBroadcastItem(i);
            if(iClientInterface != null){
                try{
                    iClientInterface.callbackClient();
                }catch(Exception e){
                    e.printStackTrace();
                }
            }
        }
        mCallback.finishBroadcast();
    }
}

AidlClientActivity

package th.ipc.aidl;

import android.content.ComponentName;
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 th.how.app.R;
import th.how.bean.IClientInterface;
import th.how.bean.IMyAidlInterface;
import th.how.bean.NewsEntity;

public class AidlClientActivity extends AppCompatActivity {

    private IMyAidlInterface iService;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_aidl_client);
        Intent intent = new Intent(this, RemoteService.class);
        intent.setAction(RemoteService.class.getName());
        bindService(intent, mConnection, BIND_AUTO_CREATE);
    }

    private IBinder.DeathRecipient deathRecipient = new IBinder.DeathRecipient() {
        @Override
        public void binderDied() {
            try {
                iService.asBinder().unlinkToDeath(deathRecipient, 0);
            }catch (Exception e){
                e.printStackTrace();
            }finally {
                iService = null;
            }

        }
    };

    private IClientInterface client = new IClientInterface.Stub() {
        @Override
        public void callbackClient() throws RemoteException {
            System.out.println("Client: Thread.currentThread().getName() = " + Thread.currentThread().getName());
        }
    };

    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            iService = IMyAidlInterface.Stub.asInterface(service);
            System.out.println("mConnection: Thread.currentThread().getName() = " + Thread.currentThread().getName());
            try {
                service.linkToDeath(deathRecipient, 0);
                iService.register(client);
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            NewsEntity entity = iService.getData(1, "Hello Activity");
                            System.out.println(entity.toString());
                        }catch (Exception e){
                            e.printStackTrace();
                        }
                    }
                }).start();

            }catch (Exception e){
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };

    @Override
    protected void onDestroy() {
        super.onDestroy();
        unbindService(mConnection);
    }
}

你可能感兴趣的:(Android进程间通信(二))