简介

  同进程接口回调在开发中经常可以用的到。方法就是A实现一个interface,并且向B传入一个interface实例引用C,然后B在必要的时候调用C的方法,以此实现接口回调。其实是可以将interface的方法直接声明并实现在A中,并且通过向B直接传入A的对象引用也可以实现相同的效果,但是基于java多态和抽象的特性,通过传入一个interface的引用可以让调用者B并不用关心传入的具体是谁的引用,这就降低了程序的耦合度。具体的接口回调实现可以参考我的另外一篇博客:
  
  在Android开发中跨进程通信有多种方式,比如通过ContentProvider访问其他应用程序的数据,或者通过接收BroadcastReceiver来接收Intent携带的数据。
  调用可以分为主动调用和被动响应。
  ConentProvider可以通过主动查询去获取数据然后在执行其他操作,或者通过ContentObserver监听数据库被动响应。但是当获取的数据量比较小时去开辟一个数据库去实现数据共享代价是比较大的。
  通过BroadcastReceiver接收属于被动响应,缺点是无论有没有程序注册了广播监听,共享方仍然会发出广播,这就造成了无谓的资源浪费。
  还有一种跨进程访问的方式就是通过AIDL方式,通过获取远程Server的Binder对象共享Server的数据。但获取到Binder对象只能主动去调用服务端接口,当服务端有变化时难以实时通知到客户端。所以有必要是现实一个回调接口,让客户端实时响应服务端的变化。好在Android在一开始设计的时候就想到了这一点,从API level 1开始就加入了相应的api : RemoteCallbackList 。 其实这只是个callback的一个容器,管理是一个aidl接口对象。其实这可以完全可以由开发者自己实现,但是既然谷歌已经实现,并且是经过base版本的Android一直沿用至今,还是推荐使用RemoteCallbackList 的。
  接下来让我们看看怎样实现跨进程接口回调。

实现

1.定义AIDL

首先我们定义已个aidl文件IMyAidlInterfaceService.aidl用来获取远程服务的Binder对象。

// IMyAidlInterfaceService.aidl
package com.yellow.demo.aidl;

import com.yellow.demo.aidl.IMyAidlInterfaceCallback;

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

interface IMyAidlInterfaceService {

    void registerCallback(IMyAidlInterfaceCallback callback);

    void unRegisterCallback(IMyAidlInterfaceCallback callback);
}

  从接口服务中我们定义两个接口,分别用来注册回调和反注册回调接口,其中IMyAidlInterfaceCallback是一个回调接口,由于是远程回调,在RemoteCallbackList.java的声明中

public class RemoteCallbackList<E extends IInterface> {/**/}

  我们可以看到RemoteCallbackList是一个泛型类,其中E是一个IInterface的子类,所以我们不能传入一个普通的interface作为接口回调的回调对象,我们需要传入一个aidl格式的文件。让我们看看IMyAidlInterfaceCallback的定义:
  

// IMyAidlInterfaceCallback.aidl
package com.yellow.demo.aidl;

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

interface IMyAidlInterfaceCallback {

    void onCallback(int result);

}

在回调接口中,定义了一个用来获取数据的接口用来接收回调,这里只是用来比较简单的 int 数据类型。
注意:在使用中必须在Client端和Remote端都必须声明这两个aidl文件,并且包名要保持一致。

2.Remote端实现
由于demo比较简单,就把全部代码贴上来了:
远程服务实现,这里新开了一个线程用来每隔两秒更新数据,如果RemoteCallbackList有保存跨进程的接口回调对象一并也会更新。

package com.yellow.demo.remotecallbackdemoserver;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.util.Log;

import com.yellow.demo.aidl.IMyAidlInterfaceCallback;
import com.yellow.demo.aidl.IMyAidlInterfaceService;

import java.util.Observable;

public class RemoteService extends Service {

    private static final String TAG = "RemoteService";

    /**
     * 声明一个RemoteCallbackList对象,用来保存客户端回调接口对象
     */
    private RemoteCallbackList<IMyAidlInterfaceCallback> mRemoteCallbackList;

    private OnDataChangedListener mOnDataChangedListener;

    private boolean mTag = true;

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

    private void initMember() {
        mRemoteCallbackList = new RemoteCallbackList<>();
    }

    private void doWork() {
        new Thread(new Runnable() {

            int result = 0;

            @Override
            public void run() {
                while (mTag) {
                    result++;
                    notifyDataChanged(result); // 本地界面更新
                    broadCastItem(result); // 远程界面更新
                    Log.i(TAG, "The current result is : " + result);
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }).start();
    }

    // call back the result
    private void broadCastItem(int result) {
        /**
         * 其实RemoteCallbackList类似于java中{@link java.util.Observable},用来批量处理接口回调对象,
         * 其实如果确保只有一个客户端会bind到这个服务,只需要保存一个IMyAidlInterfaceCallback即可。
         * 但是如果有多个,强烈推荐使用其实RemoteCallbackList
         */
        int callBackSize = mRemoteCallbackList.beginBroadcast();
        if (callBackSize == 0) {
            return;
        }
        /**
         * 逐一进行回调
         */
        for (int i = 0; i < callBackSize; i++) {
            try {
                mRemoteCallbackList.getBroadcastItem(i).onCallback(result);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
        /**
         * 回调完成之后一定要确保调用finishBroadcast,不然在下一次执行beginBroadcast会抛出
         * IllegalStateException异常。这类似与{@link Observable#setChanged()}
         * 和{@link Observable#clearChanged()},但是RemoteCallbackList设计的更加谨慎,
         * 为了确保一次Broadcast仅正对当前的状态或者数据变化。
         */
        mRemoteCallbackList.finishBroadcast();
    }

    private void notifyDataChanged(int data) {
        if (mOnDataChangedListener != null) {
            mOnDataChangedListener.onDataChanged(data);
        }
    }

    @Override
    public IBinder onBind(Intent intent) {
        return new LocalBinder();
    }


    public void registerListener(OnDataChangedListener listener) {
        mOnDataChangedListener = listener;
    }

    public void unRegisterListener() {
        mOnDataChangedListener = null;
    }

    /**
     * 实现IMyAidlInterfaceService.aidl
     */
    public class LocalBinder extends IMyAidlInterfaceService.Stub {

        public RemoteService getService() {
            return RemoteService.this;
        }

        @Override
        public void registerCallback(IMyAidlInterfaceCallback callback) throws RemoteException {
            mRemoteCallbackList.register(callback);
        }

        @Override
        public void unRegisterCallback(IMyAidlInterfaceCallback callback) throws RemoteException {
            mRemoteCallbackList.unregister(callback);
        }
    }

    @Override
    public void onDestroy() {
        Log.i(TAG, "onDestroy !!!");
        mTag = false;
        super.onDestroy();
    }
}

客户端实现了比较简单的界面,分别写了两个Button用来注册和反注册接口回调,并把接口更新在TextView上面:

package com.yellow.demo.remotecallbackdemoclient;

import android.app.Activity;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

import com.yellow.demo.aidl.IMyAidlInterfaceCallback;
import com.yellow.demo.aidl.IMyAidlInterfaceService;

public class MainActivity extends Activity implements View.OnClickListener {


    private static final String TAG = "ClientMainActivity";

    private static final String REMOTE_PKG_NAME = "com.yellow.demo.remotecallbackdemoserver";
    private static final String REMOTE_CLS_NAME = "com.yellow.demo.remotecallbackdemoserver.RemoteService";
    private static final String REMOTE_SERVICE_ACTION = "com.yellow.demo.remotecallbackdemoserver.REMOTESERVICE";

    private Button mRegisterBt, mUnregisterBt;
    private TextView mResultTv;

    private IMyAidlInterfaceCallback mRemoteCallback;
    private IMyAidlInterfaceService mRemoteService;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        initView();
        initCallBack();
        bindRemoteService();
    }

    private void initView() {
        setContentView(R.layout.activity_main);
        mRegisterBt = (Button) findViewById(R.id.bt_register);
        mUnregisterBt = (Button) findViewById(R.id.bt_unregister);
        mResultTv = (TextView) findViewById(R.id.tv_show_call_back_result);

        mRegisterBt.setOnClickListener(this);
        mUnregisterBt.setOnClickListener(this);
    }

    private void bindRemoteService() {
        ServiceConnection connection = new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                mRemoteService = IMyAidlInterfaceService.Stub.asInterface(service);
            }

            @Override
            public void onServiceDisconnected(ComponentName name) {

            }
        };
        Intent service = new Intent(REMOTE_SERVICE_ACTION);
        service.setComponent(new ComponentName(REMOTE_PKG_NAME, REMOTE_CLS_NAME));
        bindService(service, connection, BIND_AUTO_CREATE);
    }

    private void initCallBack() {
        mRemoteCallback = new IMyAidlInterfaceCallback.Stub() {

            @Override
            public void onCallback(final int result) throws RemoteException {
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        mResultTv.setText("The result is : " + result);
                    }
                });
            }
        };
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.bt_register: {
                registerRemoteCallBack();
                break;
            }
            case R.id.bt_unregister: {
                unRegisterRemoteCallback();
                break;
            }
            default: {
                break;
            }

        }
    }

    private void registerRemoteCallBack() {
        if (mRemoteService != null) {
            try {
                mRemoteService.registerCallback(mRemoteCallback);
            } catch (RemoteException e) {
                Log.i(TAG, "There is an Exception when register : " + e.toString());
            }
        }
    }

    private void unRegisterRemoteCallback() {
        if (mRemoteService != null) {
            try {
                mRemoteService.unRegisterCallback(mRemoteCallback);
            } catch (RemoteException e) {
                Log.i(TAG, "There is an Exception when unregister : " + e.toString());
            }
        }
    }

}