远程服务例子3

本文详细讲解了远程服务回调接口的使用,包括注销接口、权限校验和远程服务挂掉后的重连策略。重点介绍了RemoteCallbackList的工作原理,如何在客户端注册和注销接口,并展示了如何通过linkToDeath和unlinkToDeath来处理服务端异常终止的情况,确保服务的自动恢复。

本例子将会介绍远程服务回调接口的用法以及注销回调接口的方法、权限校验、远程服务挂掉后重连的方法等。

直接上代码,远程服务aidl接口及服务代码:

package com.example.aidl4.aidl;

import java.util.List;
import com.example.aidl4.aidl.PersionParcel;

interface IAddPersionListener {

    void addPersion(in PersionParcel newPersion);
}


 

package com.example.aidl4.aidl;

import java.util.List;
import com.example.aidl4.aidl.PersionParcel;
import com.example.aidl4.aidl.IAddPersionListener;

interface IPersionBinder {

    String getName(in PersionParcel p);
    List<PersionParcel> getAllPersions();
    void registerListener(IAddPersionListener listener);
    void unregisterListener(IAddPersionListener listener);
}


 

public class AidlService extends Service {

    private static final String mAllowPackages[] = { "com.example.aidl5" };
    private AtomicBoolean mIsServiceRunning = new AtomicBoolean(true);
    private CopyOnWriteArrayList<PersionParcel> mPersionList = new CopyOnWriteArrayList<PersionParcel>();
    private RemoteCallbackList<IAddPersionListener> mListenerLists = new RemoteCallbackList<IAddPersionListener>();

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

    @Override
    public void onCreate() {
        super.onCreate();
        mPersionList.add(new PersionParcel("zhangsan", 20, "男"));
        new Thread() {
            @Override
            public void run() {
                // 每隔30秒添加一人并调用接口中定义的方法
                while (mIsServiceRunning.get()) {
                    SystemClock.sleep(30 * 1000);
                    PersionParcel persionParcel = new PersionParcel("lili", 20,
                            "女");
                    onAddPersion(persionParcel);
                }
            }
        }.start();
    }

    private void onAddPersion(PersionParcel persionParcel) {
        mPersionList.add(persionParcel);
        final int N = mListenerLists.beginBroadcast();
        for (int i = 0; i < N; i++) {
            try {
                mListenerLists.getBroadcastItem(i).addPersion(persionParcel);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
        mListenerLists.finishBroadcast();
    }

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

    private Binder mBinder = new IPersionBinder.Stub() {

        @Override
        public String getName(PersionParcel p) throws RemoteException {
            return p.getName();
        }

        @Override
        public List<PersionParcel> getAllPersions() throws RemoteException {
            return mPersionList;
        }

        @Override
        public void registerListener(IAddPersionListener listener)
                throws RemoteException {
            mListenerLists.register(listener);
        }

        @Override
        public void unregisterListener(IAddPersionListener listener)
                throws RemoteException {
            mListenerLists.unregister(listener);
        };

        @Override
        public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
                throws RemoteException {
            String pkgName = null;
            String[] packages = AidlService.this.getPackageManager()
                    .getPackagesForUid(getCallingUid());
            if (packages != null && packages.length > 0) {
                pkgName = packages[0];
            }
            for (String item : mAllowPackages) {
                if (!item.equals(pkgName)) {
                    System.out.println("pkgName:" + pkgName + "被拒绝访问服务");
                    return false;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }
    };

}


客户端调用代码:

public class MainActivity extends Activity {

    private IPersionBinder mBinder;
    private Handler mHandler = new Handler() {
        public void handleMessage(android.os.Message msg) {
            System.out.println("添加了新人");
        };
    };
    private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
        
        @Override
        public void binderDied() {
            System.out.println("线程名1:" + Thread.currentThread().getName());
            if (mBinder == null) {
                return;
            }
            mBinder.asBinder().unlinkToDeath(mDeathRecipient, 0);
            mBinder = null;
            // 这里重新绑定远程服务
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        bindAidlService();
        Button button = (Button) findViewById(R.id.button);
        button.setOnClickListener(new OnClickListener() {
            
            @Override
            public void onClick(View v) {
                if (mBinder != null) {
                    try {
                        List<PersionParcel> list = mBinder.getAllPersions();
                        for (PersionParcel item : list) {
                            System.out.println("姓名:" + item.getName() + ", 年龄:"
                                    + item.getAge() + ", 性别:" + item.getSex());
                        }
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        unbindService(mConn);
        if (mBinder != null && mBinder.asBinder().isBinderAlive()) {
            try {
                mBinder.unregisterListener(mListener);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
    }

    private void bindAidlService() {
        Intent service = new Intent("com.example.aidl4.AidlService");
        bindService(service, mConn, Context.BIND_AUTO_CREATE);
    }

    private ServiceConnection mConn = new ServiceConnection() {
        
        @Override
        public void onServiceDisconnected(ComponentName name) {
            mBinder = null;
            System.out.println("线程名:" + Thread.currentThread().getName());
        }
        
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mBinder = IPersionBinder.Stub.asInterface(service);
            try {
                mBinder.registerListener(mListener);
                service.linkToDeath(mDeathRecipient, 0);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
    };

    private IAddPersionListener mListener = new IAddPersionListener.Stub() {

        @Override
        public void addPersion(PersionParcel newPersion)
                throws RemoteException {
            mHandler.sendEmptyMessage(0);
        }
    };

}


注销自定义接口时,需要使用RemoteCallbackList,它是系统专门提供的用于删除跨进程listener接口的。

public class RemoteCallbackList<E extends IInterface>

RemoteCallbackList是一个泛型,支持任意的AIDL接口。

工作原理:它内部有一个Map结构专门用来保存所有的AIDL回调,这个Map的key是IBinder类型,value是Callback类型。

ArrayMap<IBinder, Callback> mCallbacks = new ArrayMap<IBinder, Callback>();

其中Callback中封装了真正的远程listener,当客户端注册listener时,它会把这个listener的信息存入mCallbacks中,key和value分别通过下面的方式获得:

IBinder key = listener.asBinder();

Callback value = new Callback(listener, cookie);

虽然说跨进程传输客户端的同一个对象会在服务端生成不同的对象,但是这些新生成的对象有一个共同点,那就是它们底层的Binder对象是同一个。RemoteCallbackList还有一个很有用的功能,那就是当客户端进程终止后,它能够自动移除客户端所注册的listener。

遍历RemoteCallbackList时必须按照代码中所示,其中beginBroadcast和finishBroadcast必须配对使用。

Binder运行在服务端进程,如果服务端进程由于某种原因异常终止,此时客户端到服务端的Binder连接断裂,会导致远程调用失败。为了解决这个问题,BInder提供了两个配对的方法linkToDeath和unlinkToDeath,通过linkToDeath可以给Binder设置一个死亡代理,当Binder死亡时就会收到通知,此时可以重新发起连接请求从而恢复连接。

使用方法如代码上所示。在onServiceDisconnected方法中也可以重连远程服务。另外通过Binder的isBinderAlive方法也可以判定Binder是否死亡。

区别:onServiceDisconnected在客户端的UI线程中被回调,而binderDied是在客户端的Binder线程池中被回调。也就是说,在binderDied方法中不能访问UI。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值