Android——AIDL使用

2019-10-22  本文已影响0人  四喜汤圆

一、作用

二、概念

1. 多进程带来的问题

多进程间不能通过内存来共享数据。

2. Binder

Binder 是 Android 中的一个类,它实现了IBinder接口。

3. AIDL 接口

一个 AIDL 性质的接口,只需要继承IInterface接口即可。在 Binder 中传输的接口都需要继承IInterface接口。

4. 忠告

三、使用

1. Android 中如何开启多进程

只有一个方法:在 AndroidManifest 文件中为四大组件(Activity、Service、Receiver、ContentProvider)配置android:process属性。也就是说,我们无法给一个线程或一个实体类指定其运行时所在的进程。

<activity
            android:name=".animator.AnimatorActivity"
            android:process="com.example.myapplication.remote" />
<activity
            android:name=".diyview.MyDiyViewActivity"
            android:process=":remote" />
<service
            android:name=".LibraryManagerService"
            android:process=":aidl_remote" />
<activity android:name=".ipc.AIDLActivity">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>

不同的进程名配置方法。假设包名是com.example.myapplication

2. 创建 AIDL 文件

(1)创建 AIDL 文件,声明服务端要暴露给客户端的接口
main-new-Directory,新建『aidl』包

为了方便开发,一般把 AIDL 相关文件放在同一包中,这样当客户端是另一个应用时可方便地把整个包复制到客户端工程中。

最终的 AIDL 文件包如下:


(2)各个文件

// IBookManager.aidl
package com.example.myapplication;

// Declare any non-default types here with import statements
import com.example.myapplication.Book;
import com.example.myapplication.OnNewBookArrivedListener;
interface IBookManager {
    // 图书列表查询
    List < Book > getBookList();
    // 添加图书
    void addBook( in Book book);
    // 注册
    void registerListener(OnNewBookArrivedListener listener);
    // 取消注册
    void unregisterListener(OnNewBookArrivedListener listener);
}
// Book.aidl
package com.example.myapplication;

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

parcelable Book;
// OnNewBookArrivedListener.aidl
package com.example.myapplication;

// Declare any non-default types here with import statements
import com.example.myapplication.Book;
interface OnNewBookArrivedListener {
    // 新书到达
    void onNewBookArrived( in Book book);
}

(3)IBookManager.aidl对应的生成类

public interface IBookManger extends IInterface{}
public class Stub extends Binder implements IBookManager {
    // 将服务端的 Binder 对象转化成客户端所需 AIDL 接口类型的对象。
    // 这种转换是区分进程的。如果服务端和客户端在同一进程:返回服务端的 Stub 对象本身;不在同一进程:返回系统封装后的Stub.Proxy对象
    public static IBookManager asInterface(IBinder obj) {

    }

    // 返回当前 Binder 对象
    // IBookManager extends IInterface,IIterface接口中的抽象方法 IInterface::asBinder()
    @Override
    public IBinder asBinder() {

    }

    // 此方法运行在服务端的 Binder 线程池中,当客户端发起跨进程请求时,远程请求通过系统底层封装后交由此方法处理。
    // code:服务端通过code判断执行目标方法
    // data:携带目标方法所需参数
    // reply:将返回值写入 reply 中。
    @Override
    public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {

    }

    // 此方法运行在客户端
    @Override
    public List < Book > getBookList() {

    }

    // 此方法运行在客户端
    @Override
    public void addBook(Book book) {}

}
private static class Proxy implements IBookManager{}

(4)注意事项
使用Android studio创建的AIDL编译时找不到自定义类的解决办法

3. 服务端

先创建一个 AIDL(IBookManager.aidl)接口,声明服务端要暴露给客户端的接口,然后创建一个 Service 监听客户端的连接请求。

public class LibraryManagerService extends Service {
    private static final String TAG = "LibraryManagerService";
    /**
     * 图书列表
     */
    private CopyOnWriteArrayList < Book > mBookList = new CopyOnWriteArrayList < > ();
    /**
     * 观察者列表
     */
    private RemoteCallbackList < com.example.myapplication.OnNewBookArrivedListener > mListenerList =
        new RemoteCallbackList < > ();

    /**
     * service是否被销毁
     */
    private AtomicBoolean mIsServiceDestroyed = new AtomicBoolean(false);

    // Stub就是一个Binder类
    private Binder mBinder = new com.example.myapplication.IBookManager.Stub() {
        @Override
        public List < Book > getBookList() throws RemoteException {
            return mBookList;
        }

        @Override
        public void addBook(Book book) throws RemoteException {
            mBookList.add(book);
        }

        @Override
        public void registerListener(com.example.myapplication.OnNewBookArrivedListener listener)
        throws RemoteException {

            //            if (!mListenerList.contains(listener)) {
            //                mListenerList.add(listener);
            //            } else {
            //                Log.d(TAG, "registerListener: aleady exits");
            //            }

            mListenerList.register(listener);
        }

        @Override
        public void unregisterListener(com.example.myapplication.OnNewBookArrivedListener listener)
        throws RemoteException {
            //            if (mListenerList.contains(listener)) {
            //                mListenerList.remove(listener);
            //                Log.d(TAG, "unregisterListener: success");
            //            } else {
            //                Log.d(TAG, "unregisterListener: can not register");
            //            }
            //            Log.d(TAG, "unregisterListener: current size=" + mListenerList.size());

            Log.d(TAG, "unregisterListener: begin unreginster listener " + listener);
            int old = mListenerList.beginBroadcast();
            mListenerList.finishBroadcast();
            Log.d(TAG, "unregisterListener: current listenerList size=" + old);
            mListenerList.unregister(listener);
            Log.d(TAG, "unregisterListener: after unreginster listener " + listener);
            int fresh = mListenerList.beginBroadcast();
            mListenerList.finishBroadcast();
            Log.d(TAG, "unregisterListener: current listenerList size=" + fresh);
        }
    };

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

    @Override
    public void onCreate() {
        super.onCreate();
        mBookList.add(new Book(1, "Android"));
        mBookList.add(new Book(2, "java"));

        new Thread(new ServiceWorker()).start();
    }

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

    private class ServiceWorker implements Runnable {
        @Override
        public void run() {
            while (!mIsServiceDestroyed.get()) {
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                int bookId = mBookList.size() + 1;
                Book newBook = new Book(bookId, "new Book#" + bookId);
                try {
                    onNewBookArrived(newBook);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }

        }
    }

    /**
     * 新书到达时
     * 
     * @param newBook
     */
    private void onNewBookArrived(Book newBook) throws RemoteException {
        mBookList.add(newBook);
        Log.d(TAG, "onNewBookArrived: notify Listeners:");
        //        for (int i = 0; i < mListenerList.size(); i++) {
        //            com.example.myapplication.OnNewBookArrivedListener onNewBookArrivedListener = mListenerList.get(i);
        //            if (onNewBookArrivedListener != null) {
        //                onNewBookArrivedListener.onNewBookArrived(newBook);
        //            }
        //        }

        final int N = mListenerList.beginBroadcast();
        for (int i = 0; i < N; i++) {
            com.example.myapplication.OnNewBookArrivedListener broadcastItem = mListenerList.getBroadcastItem(i);
            if (broadcastItem != null) {
                broadcastItem.onNewBookArrived(newBook);
            }
        }
        mListenerList.finishBroadcast();
    }

}

4. 客户端

public class AIDLActivity extends AppCompatActivity {
    private static final String TAG = "AIDLActivity";

    private static final int MSG_NEW_BOOK_ARRIVED = 1;

    private IBookManager mRemoteManager;

    private Myhandler mHandler = new Myhandler();

    private ServiceConnection mServiceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            IBookManager iBookManager = IBookManager.Stub.asInterface(service);
            mRemoteManager = iBookManager;
            // 为Binder设置死亡监听
            try {
                service.linkToDeath(mDeathRecipient, 0);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
            try {
                // 图书列表
                List < Book > bookList = iBookManager.getBookList();
                Log.d(TAG, "onServiceConnected: 添加前: bookList=" + bookList);
                // 添加图书
                iBookManager.addBook(new Book(3, "语文"));
                // 图书列表
                bookList = iBookManager.getBookList();
                Log.d(TAG, "onServiceConnected: 添加后 bookList=" + bookList);
                // 注册
                iBookManager.registerListener(mOnNewBookArrivedListener);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            // 此处重连远程服务
            Log.d(TAG, "onServiceDisconnected: ");
        }
    };

    private OnNewBookArrivedListener mOnNewBookArrivedListener = new OnNewBookArrivedListener.Stub() {
        @Override
        public void onNewBookArrived(Book book) throws RemoteException {
            mHandler.obtainMessage(MSG_NEW_BOOK_ARRIVED, book).sendToTarget();
        }
    };


    // 死亡监听
    private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
        @Override
        public void binderDied() {
            if (mRemoteManager == null) {
                return;
            }
            mRemoteManager.asBinder().unlinkToDeath(mDeathRecipient, 0);
            mRemoteManager = null;
            // 重新绑定 Service
        }
    };

    private static class Myhandler extends Handler {
        @Override
        public void handleMessage(@NonNull Message msg) {
            switch (msg.what) {
                case MSG_NEW_BOOK_ARRIVED:
                    Log.d(TAG, "handleMessage: receive new book " + msg.obj);
                    break;
                default:
                    break;
            }

        }
    }

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_aidl_layout);
        // 客户端一启动就绑定服务端,方便调用服务端提供的服务
        bindNewService();
    }

    private void bindNewService() {
        Intent intent = new Intent(AIDLActivity.this, LibraryManagerService.class);
        bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        // 取消注册
        if (mRemoteManager != null && mRemoteManager.asBinder().isBinderAlive()) {
            try {
                mRemoteManager.unregisterListener(mOnNewBookArrivedListener);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
        // 取消绑定
        unbindService(mServiceConnection);
    }
}

三、重点

1. 新书订阅功能

假设有新的需求:用户不想自己时不时地去查询图书列表,而是由图书馆在有新书时把书的信息通知给客户端。这是一种典型的观察者模式

private OnNewBookArrivedListener mOnNewBookArrivedListener = new OnNewBookArrivedListener.Stub() {
    @Override
    public void onNewBookArrived(Book book) throws RemoteException {
        mHandler.obtainMessage(MSG_NEW_BOOK_ARRIVED, book).sendToTarget();
    }
};
// 添加元素
mListenerList.register(listener);
// 删除元素
mListenerList.unregister(listener);
// 遍历元素
final int N = mListenerList.beginBroadcast();
for (int i = 0; i < N; i++) {
    com.example.myapplication.OnNewBookArrivedListener broadcastItem = mListenerList.getBroadcastItem(i);
    if (broadcastItem != null) {
        broadcastItem.onNewBookArrived(newBook);
    }
}
mListenerList.finishBroadcast();

2. 客户端取消在服务端的注册

如果服务端用普通的 List 保存观察者的话,观察者取消注册失败。需要用RemoteCallbackList保存观察者,这是系统专门提供的用于删除跨进程 listener 的接口。

3. 为保证连接的健壮性,客户端在与服务端断开时重连

客户端关心与服务端的连接是否在稳固的建立着,如果断开的话要有相应的重连机制。
(1)为服务端设置死亡监听
客户端绑定服务端成功后为服务端设置死亡监听,收到消息后就进行重连

private ServiceConnection mServiceConnection = new ServiceConnection() {
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        IBookManager iBookManager = IBookManager.Stub.asInterface(service);
        mRemoteManager = iBookManager;
        // 为Binder设置死亡监听
        try {
            service.linkToDeath(mDeathRecipient, 0);
        } catch (RemoteException e) {

        }
        // .....
    }
}

(2)在onServiceDisconnect()中重连

4. 安全性:权限验证

参考文献

SheHuan-Android多进程通信
Android开发艺术探索

上一篇下一篇

猜你喜欢

热点阅读