美文网首页
进程间的通信——AIDL

进程间的通信——AIDL

作者: 132xin | 来源:发表于2020-08-11 23:06 被阅读0次

    简介

    进程之间的通信——Messenger的文章中说到了通过Messenger进行进程间的通信,可以发现Messenger是以串行的方式处理客户端发送来的消息,如果大量的消息同时发送到服务端,服务端依然只能一个个处理,如果有大量的并发请求,那么Messenger就不太实用了。同时,Messenger的作用主要是为了传递消息,很多时候我们可能需要跨进程调用服务端的方法,这个时候Messenger就无法做到了,但是我们可以使用AIDL来实现跨进程的方法调用。

    AIDL接口

    实现AIDL的跨进程通信,需要创建AIDL接口,ALDL接口支持的数据类型:

    • 基本数据类型(int、long、char 、boolean、double等)
    • String和CharSequence
    • List:只支持ArrayList,里面每个元素都必须能够被AIDL支持。
    • Map:只支持HashMap,里面的每个元素都必须被AIDL支持。
    • Parcelable: 所有实现了Parcelable接口的对象。
    • AIDL:所有的AIDL接口本身都可以在AIDL中支持。

    其中自定义的Parcelable对象和AIDL对象必须要显示import进来,不管它们是否和当前的AIDL文件位于同一个包内。
    另外需要注意的是,如果AIDL文件中用到了自定义的Parcelable对象,必须新建一个和它同名得到AIDL文件,比在其中声明是Parcelable
    AIDL中除了基本数据类型,其他类型的参数必须标上方向:in、out或者inout。in表示输入型参数,out表示输出型参数,inout表示输入输出型参数。根据实际情况指定。
    还需要注意的是AIDL的包结构在服务端和客服端需要保持一致,否则运行会出错,这是因为客服端需要反序列化服务端中和ALDL接口相关的所有类,如果类的完整路径不一样的话,就无法成功反序列化,程序就无法正常运行。如果是先创建了服务端的接口,就将服务端的AIDL文件复制到客户端对应的目录下,不要改动包名。

    服务端

    服务端首先创建一个Service用来监听客户端的连接请求,然后创建一个AIDL文件,将暴露给客户端的连接在这个AIDL文件中声明,最后在Service中实现这个AIDL接口即可。

    客户端

    需要绑定服务端的Service,绑定成功后,将服务顿返回的Binder对象转化成AIDL接口所属于的类型,接着就可以调用AIDL的方法了。

    示例

    现在有这样一个要求,在服务端中有个书库,客户端可以向书库中添加书本,并且客户端可以获取书库中的所有书本,如果服务端中添加了新的书本,可以发出通知告诉客户端。

    服务端

    1.创建AIDL接口


    image.png

    这里有一个Book的Java类,如果这个类放在aidl的包下的,需要在gradle(app)的Android节点中添加这个代码:

        sourceSets {
            main {
                manifest.srcFile 'src/main/AndroidManifest.xml'
                java.srcDirs = ['src/main/java', 'src/main/aidl']
                resources.srcDirs = ['src/main/java', 'src/main/aidl']
                aidl.srcDirs = ['src/main/aidl']
                res.srcDirs = ['src/main/res']
                assets.srcDirs = ['src/main/assets']
            }
        }
    

    或者放在普通的包下,但是这样有个缺点就是在客户端中也要在普通的包下创建一个book的类,而且还需要注意导包的问题。建议上面那种解决方法。接下来看各个类的代码
    Book.java
    》 这个类要实现 Parcelable的接口

    
    public class Book implements Parcelable {
        public int bookId;
        public  String bookName;
        public Book(int bookId,String bookName){
            this.bookId=bookId;
            this.bookName=bookName;
        }
    
        protected Book(Parcel in) {
            bookId = in.readInt();
            bookName = in.readString();
        }
    
        public static final Creator<Book> CREATOR = new Creator<Book>() {
            @Override
            public Book createFromParcel(Parcel in) {
                return new Book(in);
            }
    
            @Override
            public Book[] newArray(int size) {
                return new Book[size];
            }
        };
    
        /**
         * 当我们写完属性,和构造发现之后再实现Parcelable的时候
         * 可自动生成以下的代码。
         *
         */
    
    
        @Override
        public int describeContents() {
            return 0;
        }
    
        @Override
        public void writeToParcel(Parcel dest, int flags) {
            dest.writeInt(bookId);
            dest.writeString(bookName);
        }
    
        /**
         * 重写equals,如果Id相同,且名字相同则说明是同一本书
         * @param obj
         * @return
         */
        @Override
        public boolean equals(@Nullable Object obj) {
            return this.bookId== ((Book)obj).bookId && this.bookName.equals(((Book)obj).bookName);
        }
    
    }
    

    因为这个Book是自己自定义而定Parcelable对象,所以必须新建一个和它同名的AIDL文件,并在其中声明它为Parcelable,否则会构建失败。

    // Book.aidl
    package com.example.hx.servicesaidl;
    
    parcelable Book;
    

    IBookManger.aidl
    这个AIDL的接口,里面提供了四个方法,将在Service中实现。

    // IBookManager.aidl
    package com.example.hx.servicesaidl;
    
    // Declare any non-default types here with import statements
    import com.example.hx.servicesaidl.Book;
    import com.example.hx.servicesaidl.IOnNewBookArrivedListener;
    interface IBookManager {
         List<Book>  getBookList();
         void addBook(in Book book);
         void registerListener(IOnNewBookArrivedListener listener);
         void unRegisterListener( IOnNewBookArrivedListener listener);
    }
    
    

    定义这个接口是为了,进行通知客户端的方法,让客户端进行回调。

    // IonNewBookArrivedListener.aidl
    package com.example.hx.servicesaidl;
    
    // Declare any non-default types here with import statements
    import com.example.hx.servicesaidl.Book;
    interface IOnNewBookArrivedListener {
        void onNewBookArrived(in Book newBook);
    }
    
    

    接下来就是BooKMangerService,这是一个服务,需要在AndroidManifest.xml,并且设置action。

    <service
                android:name=".BookManagerService"
                android:enabled="true"
                android:exported="true">
                <intent-filter>
                    <action android:name="android.intent.action.START_SERVICE"/>
                </intent-filter>
    

    BookManagerService.java

    public class BookManagerService extends Service {
        private static final String TAG="BookManagerService";
        private AtomicBoolean atomicBoolean=new AtomicBoolean(false);
        public BookManagerService() {
        }
    
        //使用RemoteCallbackList来用于删除进程的接口
        private static RemoteCallbackList<IOnNewBookArrivedListener> mOnNewBookArrivedListeners =
                new RemoteCallbackList<>();
        private  static  CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<>();
    
        @Override
        public void onCreate() {
            super.onCreate();
            mBookList.add(new Book(1, "第一行代码"));
            mBookList.add(new Book(2, "Android群英传"));
            new Thread(new ServiceWorker()).start();
    
        }
    
        static class BookManagerBinder extends IBookManager.Stub {
    
    
            @Override
            public List<Book> getBookList() throws RemoteException {
                return mBookList;
            }
    
            @Override
            public void addBook(Book book) throws RemoteException {
                if (!mBookList.contains(book)){
                    mBookList.add(book);
                }
            }
    
            @Override
            public void registerListener(IOnNewBookArrivedListener listener) throws RemoteException {
                if (listener!=null){
                    mOnNewBookArrivedListeners.register(listener);
                    Log.e(TAG, "@@@ 注册了监听器 " );
                }
            }
    
            @Override
            public void unRegisterListener(IOnNewBookArrivedListener listener) throws RemoteException {
                if (listener!=null){
                    mOnNewBookArrivedListeners.unregister(listener);
                    Log.e(TAG, "@@@ 取消注册了监听器 " );
                }
            }
        }
    
        @Override
        public IBinder onBind(Intent intent) {
            return new BookManagerBinder();
        }
    
        /**
         * 添加新的书本,调用该方法,通知用户
         * @param book
         */
        private void onNewBookArrived(Book book) throws RemoteException {
            if (mBookList.contains(book)){
                return;
            }
            mBookList.add(book);
            //RemoteCallbackList与List的遍历方式不同
            final  int num=mOnNewBookArrivedListeners.beginBroadcast();
            for (int i=0;i<num;i++){
                IOnNewBookArrivedListener listener=mOnNewBookArrivedListeners.getBroadcastItem(i);
                listener.onNewBookArrived(book);
                Log.e(TAG, "@@@ 通知客户端,服务端添加了新的书本 "+book.bookId+"  "+book.bookName);
            }
            mOnNewBookArrivedListeners.finishBroadcast();
        }
        /**
         * 为了实现在服务端中添加书本的效果,开启一条线程进行添加
         *
         */
        private class ServiceWorker implements Runnable{
    
            @Override
            public void run() {
                while (!atomicBoolean.get()){
                    int id=mBookList.size()+1;
                    Book book=new Book(id,"书本1");
                    try {
                        //添加书
                        onNewBookArrived(book);
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    
        @Override
        public void onDestroy() {
            atomicBoolean.set(true);
            super.onDestroy();
        }
    }
    
    

    这里的继承的IBookManager.Stub是重点,这个是AIDL自动生成,也可以通过匿名类的方式初始化,然后再onBind()方法中返回,然后再客户端连接中就可以获得IBookManager对象,然后执行方法的时候,就会调用服务端重新的方法,实现了交互。
    在上面的代码中用了CopyOnWriteArrayList保存书本,前面说到AIDL中所支持的只有ArrayList,但是我们用了CopyOnWriteArrayList,为什么能正常工作呢?这是因为AIDL中所支持的是抽象的List,而List是一个接口,因此虽然服务端返回CopyOnWriteArrayList,但是Binder中会按照List的规范去访问数据并最终形成一个新得ArrayList传递给客户端,所以在服务端使用CopyOnWriteArrayList是完成可以的。

    客户端

    将服务端的aidl的文件复制到main下面,不需改动任何东西。


    image.png

    客户端连接service,通过IBookManager.Stub.asInterface(service)等到对象之后就可以进行交互了。

    public class MainActivity extends AppCompatActivity {
        private final static String TAG = "MainActivity";
        private static IBookManager mBookManager;
        public static Handler handler = new MyHandler();
        private MyServiceConnection myServiceConnection;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            //通过隐式的方式启动服务
            Intent intent = new Intent("android.intent.action.START_SERVICE");
            myServiceConnection = new MyServiceConnection();
            intent.setComponent(new ComponentName("com.example.hx.servicesaidl", "com.example.hx.servicesaidl.BookManagerService"));
            bindService(intent, myServiceConnection, Context.BIND_AUTO_CREATE);
    
        }
    
        private static class MyServiceConnection implements ServiceConnection {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                //获取IBookManager
                mBookManager = IBookManager.Stub.asInterface(service);
                try {
                    //获取所有的书本
                    List<Book> list = mBookManager.getBookList();
                    Log.e(TAG, "客户端: 获取服务端的书本 " + list.size());
                    //添加书本
                    Book book = new Book(3, "Android开发艺术与探索");
                    mBookManager.addBook(book);
                    Log.e(TAG, "客户端: 添加书本到服务端 " + book.bookId + "  " + book.bookName);
                    //重新获取书本
                    List<Book> listNew = mBookManager.getBookList();
                    Log.e(TAG, "客户端: 获取服务端的书本 " + listNew.size());
                    //注册监听者
                    mBookManager.registerListener(mIOnNewBookArrivedListener);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
    
            @Override
            public void onServiceDisconnected(ComponentName name) {
    
            }
    
        }
    
        private static IOnNewBookArrivedListener mIOnNewBookArrivedListener = new IOnNewBookArrivedListener.Stub() {
            @Override
            public void onNewBookArrived(Book newBook) throws RemoteException {
    
                /**
                 * 这个方法的回调是在客户端的Binder线程上执行的
                 * 如果要进行Ui的操作,就需要切换到主线程上。
                 * @param newBook
                 * @throws RemoteException
                 */
                //do someThing
                Message message = Message.obtain(handler, 1);
                Bundle bundle = new Bundle();
                bundle.putParcelable("book", newBook);
                message.setData(bundle);
                handler.sendMessage(message);
    
            }
        };
    
        static class MyHandler extends Handler {
            @Override
            public void handleMessage(@NonNull Message msg) {
                switch (msg.what) {
                    case 1:
                        Bundle bundle = msg.getData();
                        Book book = (Book) bundle.getParcelable("book");
                        assert book != null;
                        Log.e(TAG, "@@@  服务端中添加新的书本 " + book.bookId + "  " + book.bookName);
                        break;
                    default:
                        super.handleMessage(msg);
                }
            }
        }
        //销毁的时候取消注册
    
        @Override
        protected void onDestroy() {
            if (mBookManager != null && mBookManager.asBinder().isBinderAlive()) {
                try {
                    mBookManager.unRegisterListener(mIOnNewBookArrivedListener);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
            unbindService(myServiceConnection);
            super.onDestroy();
    
        }
    }
    

    因为这里的客户端和服务端是两个不同的应用,如果启动Service,可以参考这个链接Android在一个app中启动其他app中的service或者Activity

    结果:
    服务端:


    image.png

    客户端:


    image.png

    以上是实现AIDL的简单例子,但是在实际使用的时候需要注意,客户端调用远程服务的方法,被调用的方法运行在服务端的Binder线程池中,同时客户端会被挂起,这个时候如果服务端方法执行比较耗时的,就会导致客户端线程长时间阻塞在这里,而如果这个客户端线程是UI线程的话,就会导致客户端ANR。因此如果我们明确知道某个远程方法是耗时的,那么就要避免在UI线程上执行。客户端的onServiceConnected和onServiceDisconnected方法都运行在UI线程中,所以也不可以在这两个方法中直接调用远程的方法,本例子是为了方便,在实际开发中不要这样做。另外需要注意的是,由于服务端的方法本身就运行在服务端的Binder线程池中,所以服务端方法本身就可以执行大量耗时操作,这个时候切记不要在服务端方法中开启线程去进行异步任务,除非你明确知道自己在干什么,否则不要这样做。

    相关文章

      网友评论

          本文标题:进程间的通信——AIDL

          本文链接:https://www.haomeiwen.com/subject/pymydktx.html