AIDL内部对象判等、Binder死亡监听、不死服务实现

背景

事情的起因是解决如何判断aidl服务端使用的客户端对象是否为同一个对象。于是引发了asBinder的使用,RemoteCallbackList使用,以及很少使用的IBinder.DeathRecipient。

需求场景

此篇介绍可以帮助解决aidl使用中的生命周期控制、对象维护以及资源释放,保活服务问题。

对象判等

当服务端接口有接收客户端aidl定制的对象时,例如维护一个listener列表。你可能需要避免相同对象被重复添加到集合。这个时候需要在服务端接口内对对象执行asBinder操作,此操作返回的是客户端的Binder对象。
原理:
1、看Proxy代理

	private static class Proxy implements IAssistantOemService {
            private android.os.IBinder mRemote;

            Proxy(android.os.IBinder remote) {
                mRemote = remote;
            }

            @Override
            public android.os.IBinder asBinder() {
                return mRemote;
            }
	}            

2、Proxy创建是在Stub代理中

	public static abstract class Stub extends android.os.Binder implements IAssistantOemService {
        /**
         * Cast an IBinder object into an com.google.assistant.IAssistantOemService interface,
         * generating a proxy if needed.
         */
        public static com.google.assistant.IAssistantOemService asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.google.assistant.IAssistantOemService))) {
                return ((com.google.assistant.IAssistantOemService) iin);
            }
            return new com.google.assistant.IAssistantOemService.Stub.Proxy(obj);
        }

结论:很明显,stub是客户端代理,proxy服务端代理最后总是取得客户端的对象。所以aidl服务端判断相等需要取asBinder对象,否则取到的就是Proxy对象,无法判等。

服务端如何存储对象

1、比较简答的写法,维护一个map就好了。

private SafeIterableMap<IBinder, IListener> mObservers =
            new SafeIterableMap<>();

但是客户端死掉了怎么处理垃圾Listener?
2、使用可以绑定IBinder死掉的工具类RemoteCallbackList。

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

里边最主要的实现了IBinder.DeathRecipient。当然RemoteCallbackList是一个很不错的对象集合实现,当你需要在服务端保存一个对象集合时,可以考虑用它。

IBinder的死亡监听

1、IBinder关于死亡监听的几个方法

	/**检查需要使用的Binder是否存活
     * Check to see if the object still exists.
     * 
     * @return Returns false if the
     * hosting process is gone, otherwise the result (always by default
     * true) returned by the pingBinder() implementation on the other
     * side.
     */
    public boolean pingBinder();
	
	/**需要监听Binder死亡的需要实现这个接口,这个接口在调用linkToDeath后会被注册
     * Interface for receiving a callback when the process hosting an IBinder
     * has gone away.
     * 
     * @see #linkToDeath
     */
    public interface DeathRecipient {
        public void binderDied();
    }
	
	/**简单说你需要调用这个方法传入自定义的DeathRecipient进行注册。
     * 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.
     * 
     * <p>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(@NonNull DeathRecipient recipient, int flags)
            throws RemoteException;

2、IBinder.linkToDeath的实现

	/**Binder.java中这个方法的实现是个空方法,也就是说Binder创建的进程不需要处理这个
     * Local implementation is a no-op.
     */
    public void linkToDeath(@NonNull DeathRecipient recipient, int flags) {
    }
	
	/**BinderProxy.java这个是跨进程的代理Binder,实现是个native方法
     * See {@link IBinder#linkToDeath(DeathRecipient, int)}
     */
    public native void linkToDeath(DeathRecipient recipient, int flags)
            throws RemoteException;

AIDL特殊使用

1、pingBinder()方法会返回当前远程服务的状态(true|false)
2、客户端可以捕获RemoteException(DeadObjectException)得知服务端状态,可以用于释放资源或者重启服务。
3、使用RemoteCallbackList存储对象集合,用于避免对象重复。
3、客户端实现IBinder.DeathRecipient接口,用于监听服务器死亡状态,可以用于释放资源或者重启服务。
4、服务端实现IBinder.DeathRecipient接口,用于监听客户端死亡状态,去除保留的客户端对象。(这点还蛮重要的,有利于避免aidl服务端对多个客户端时的空指针,内存占用问题)

猜你喜欢

转载自blog.csdn.net/archie_7/article/details/107215374