BindService标准写法(包含服务端与客户端)

BindService标准写法

在使用bindservice时,经常会忽略掉死亡回调的作用,下面提供一个标准的bindService的使用流程(客户端),仅供参考


import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
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 ITestBinder;

public class Test extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Intent intent = new Intent("xxxx");
        bindService(intent,serviceConnection, Context.BIND_AUTO_CREATE); //绑定service
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        unbindService(serviceConnection); //解绑service
    }

    ITestBinder iBinder;
    ServiceConnection serviceConnection = new ServiceConnection() {
        // 注意这个函数是在ui线程回调的
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            iBinder = ITestBinder.Stub.asInterface(service);
            try {
                // 注册死亡回调
                iBinder.asBinder().linkToDeath(mDeathRecipient, 0);
                //TODO 想要进行的主业务
            } catch (RemoteException e) {
                Log.e("TAG", "onServiceConnected:" + e.getMessage());
            }


        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

            iBinder = null; //资源释放
        }
    };


    /**
     * 死亡回调 service异常结束时 客户端会在这个回调函数里得到通知
     */
    IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
        @Override
        public void binderDied() {

            iBinder.asBinder().unlinkToDeath(mDeathRecipient, 0); //释放死亡回调
            iBinder = null; //资源释放
            //TODO 其他处理,例如重新绑定service等

        }
    };
}

服务端的流程如下:主要是RemoteCallbackList的应用

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

import common.utils.WLog;
import ITestBinder;
import ITestClientCallback;

import java.util.concurrent.atomic.AtomicInteger;

public class TestService extends Service {

    public static final String TAG = "TestService";

    /**
     * RemoteCallbackList是系统提供的专门用于删除跨进程listener的接口.
     * 用RemoteCallbackList,而不用ArrayList的原因是, 客户端的对象注册进来后, 服务端会通过它反序列化出一个新的对象保存一起,
     * 所以说已经不是同一个对象了. 在客户端调用解除注册方法时, 在list中根本就找不到它的对象,
     * 也就无法从list中删除客户端的对象. 而RemoteCallbackList的内部保存的是客户端对象底层的binder对象,
     * 这个binder对象在客户端对象和反序列化的新对象中是同一个对象,  RemoteCallbackList的实现原理就是利用的这个特性.
     */
    private RemoteCallbackList<ITestClientCallback> mListenerList = new RemoteCallbackList<ITestClientCallback>() {

        //回调 注意这个回调 有一个特性是,如果客户端异常死亡了,这个对应的回调会自己消失 所以在这里要对callback 进行死亡监听
        @Override
        public void onCallbackDied(ITestClientCallback callback, Object cookie) {

            Log.v(TAG, "RemoteCallbackList onCallbackDied !!!!!!!!!!!!!");
            CONNECTION_COUNT.decrementAndGet();
            super.onCallbackDied(callback, cookie);
        }
    };


    /**
     * 用来记录当前有多少个callback 连接到 service 上
     */
    public static AtomicInteger CONNECTION_COUNT = new AtomicInteger(0);


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

    @Override
    public void onDestroy() {

        mListenerList.kill();
        CONNECTION_COUNT.set(0);
        Log.i(TAG, "onDestroy !!!!!!");
        super.onDestroy();
    }

    @Override
    public IBinder onBind(Intent intent) {
        //也可以将onTransact方法中的处理移到该位置
        return mBinder;
    }

    /**
     * 这里的方法 不可以阻塞时间过长,否则会影响对下一个client调用的响应。 service本身支持并发访问,所以操作数据时 要保证同步性 client传递到server端的对象是重新被构造出来的
     */
    Binder mBinder = new ITestBinder.Stub() {

        @Override
        public void registerCallback(ITestClientCallback callback) throws RemoteException {
            //增加客户端连接计数
            CONNECTION_COUNT.incrementAndGet();
            mListenerList.register(callback);
            final int N = mListenerList.beginBroadcast();
            mListenerList.finishBroadcast();
            WLog.v(TAG, "getListenerListSize, current size:" + N);

            doSomeThing();

        }

        @Override
        public void unregisterCallback(ITestClientCallback callback) throws RemoteException {
            //减去客户端连接计数
            CONNECTION_COUNT.decrementAndGet();
            mListenerList.unregister(callback);
            final int N = mListenerList.beginBroadcast();
            mListenerList.finishBroadcast();
            Log.v(TAG, "unregisterCallback,current size:" + N);
        }

        // 每次有客户端client 连接进来都会走这个方法
        @Override
        public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {

            //通过getCallingUid()得到客户端的uid, 再通过PackageManager根据uid查到package name进行检查.进行一些相应的限制
            String packageName = null;
            String[] packages = getPackageManager().getPackagesForUid(
                    getCallingUid());
            if (packages != null && packages.length > 0) {
                packageName = packages[0];
            }
            Log.d(TAG, "onTransact: " + packageName);
            if (!packageName.startsWith("com.xxx")) {
                return false;
            }
            return super.onTransact(code, data, reply, flags);
        }

    };


    /**
     * RemoteCallbackList 并不是一个List ,不能像 List 一样去操作它,遍历RemoteCallbackList
     * 必须要以下面的方式进行,其中 beginBroadcast 和 finishBroadcast 必须配套使用,哪怕我们仅仅是想要获取
     * RemoteCallbackList 中的元素个数
     */
    private void doSomeThing() {

        final int N = mListenerList.beginBroadcast();
        for (int i = 0; i < N; i++) {
            ITestClientCallback iTestClientCallback = mListenerList.getBroadcastItem(i);
            try {
                if (iTestClientCallback != null) { //执行客户端的回调
                    iTestClientCallback.xxx();
                }
            } catch (RemoteException e) {
                // 此异常属于系统异常,编码层面无能力干预,开发者无需过多关注
                WLog.e(TAG, " RemoteException ServiceCallback failed:" + e.getMessage());
            }
        }
        mListenerList.finishBroadcast();
    }

}

注:
客户端远程调用服务端的方法,被调用的方法运行在服务端的 Binder 线程池中,同时客户端线程会被挂起,此时如果服务端方法执行比较耗时,则会导致客户端线程长时间阻塞在这里,如果此时客户端线程是 UI 线程,则会导致客户端ANR ,因此如果我们明确知道某个远程方法是耗时的,则要避免在客户端的 UI 线程中去访问远程方法。
由于客户端的 onServiceConnected 和 onServiceDisconnected 方法运行在 UI线程中,故也不可以在他们里面直接调用服务端的耗时方法。
服务端方法本身就运行在服务端的Binder 线程池中,故服务端的方法本身就可以进行大量的耗时操作,此时切记不要在服务端开线程去进行异步任务,除非你明确知道自己在干什么,否则不建议这么做

同理,当远程服务端需要调用客户端的 listener 中的方法时,被调用的方法运行在客户端的 Binder 池中,故我们同样不可以在服务端调用客户端耗时方法,

Binder是可能意外死亡的,这往往是由于服务端进程意外停止导致的,此时我们需要重新连接服务。
1、给Binder 设置DeathRecipient 监听,当Binder 死亡时,我们会收到 binderDied 方法的回调,在 binderDied 方法中我们可以重新绑定远程服务
2、在onServiceDisconnected 中重连远程服务
这两种方法的区别在于:onServiceDisconnected 在客户端的 UI 线程中被回调,而 binderDied 在客户端的Binder 线程池中被回调,即在binderDied 方法中我们不能访问 UI

你可能感兴趣的:(Android)