第二章-IPC机制-Android中的各种IPC方式(AIDL)

上一节我们介绍了使用Messenger来进行进程间通信的方法,可以发现,Messenger是以串行的方式处理客户端发来的消息,如果大量的消息同时发送到服务端,服务端仍然只能一个个处理,如果有大量的并发请求,那么用Messenger就不太合适了。同时,Messenger的作用主要是为了传递消息,很多时候我们可能需要跨进程调用服务端的方法,这种情形用Messenger就无法做到了,但是我们可以使用AIDL来实现跨进程的方法调用。AIDL是Messenger的底层实现,因此Messenger本质上也是AIDL,只不过系统为我们做了封装,从而方便上层的调用而已。在上一节中,我们介绍了Binder的概念,大家对Binder也有了一定的了解,在Binder的基础上我们可以更加容易地理解AIDL。这里先介绍使用AIDL来进行进程间通信的流程,分为服务端和客户端两个方面。

1、服务端

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

2、客户端

客户端所要做事情就稍微简单一些,首先需要绑定服务端的Service,
绑定成功后,将服务端返回的Binder对象转成AIDL接口所属的类型,
接着就可以调用AIDL中的方法了

上面描述的只是一个感性的过程,AIDL的实现过程远不止这么简单,接下来会对其中的细节和难点进行详细介绍,并完善我们在Binder那一节所提供的的实例。

3、AIDL接口的创建

首先看AIDL接口的创建,如下所示,我们创建了一个后缀为AIDL.的文件,在里面声明了一个接口和两个接口方法。

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

import com.example.chapter_2.aidl.Book;//需要显示的导入这个Book类

interface IBookManager {
    List<Book> getBookList();
    void addBook(in Book book);//aidl文件中这个自定义的Book一定要加 in , 否则会报语法错误
}

在这里插入图片描述

// Book.aidl
package com.example.chapter_2.aidl;
parcelable Book; //注意是小写

在这里插入图片描述

4、远程服务端Service的实现代码

上述我们讲了如何定义一个AIDL的接口,接下来我们就需要实现这个接口了。我们先创建一个Service,称为BookManagerService,代码如下:

public class BookManagerService extends Service {
    private static final String TAG = "BookManagerService";

    private CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<>();

    //步骤2:创建一个mBinder对象,实现了aidl中抽象类Stub定义的方法
    private Binder mBinder = new 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 onCreate() {
        super.onCreate();

        //步骤1:onCreate中初始化2本图书
        mBookList.add(new Book(1,"Android"));
        mBookList.add(new Book(1,"IOS"));
    }

    @Override
    public IBinder onBind(Intent intent) {
        //步骤3:客户端绑定成功就将这个实现了接口的Binder对象返回给客户端
        return mBinder;
    }
}

注册服务

<service
     android:name=".BookManagerService"
     android:process=":remote" >
</service>

在这里插入图片描述

5、客户端的实现代码

客户端的实现就比较简单了,我们绑定远程服务,绑定成功之后返回的Binder对象转换成AIDL接口,然后就可以通过这个接口去调用服务端的远程方法,代码如下所示:

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

    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            //将服务端返回的binder对象转换成客户端本地的aidl接口
            IBookManager bookManager = IBookManager.Stub.asInterface(service);
            
            //就可以通过这个bookManager Binder对象来调用到其它进程中的方法
            try {
                List<Book> list = bookManager.getBookList();
                Log.i(TAG, "list Type :" + list.getClass()getCanonicalName());
                Log.i(TAG, "list string : " + list.toString());
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_book_manager);

        //绑定服务
        Intent intent = new Intent(this, BookManagerService.class);
        bindService(intent, mConnection, Context.BIND_AUTO_CREATE);

    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        unbindService(mConnection);//解绑服务
    }
}

在这里插入图片描述

这样一个完整的AIDL的IPC过程就完成了。接着看难点。

6、如何去订阅远程服务?

在这里插入图片描述
// IOnNewBookArrivedListener.aidl (服务端)

package com.example.chapter_2.aidl;

import com.example.chapter_2.aidl.Book;

interface IOnNewBookArrivedListener {
   void onNewBookArrived(in Book newBook);
}

除了要新增加一个AIDL外,还需要在原有的接口中添加两个新的方法

// IBookManager.aidl(服务端)

package com.example.chapter_2.aidl;

import com.example.chapter_2.aidl.Book;
import com.example.chapter_2.aidl.IOnNewBookArrivedListener;//自定义的aidl也需要显示的导入

interface IBookManager {
    List<Book> getBookList();
    void addBook(in Book book);
    
    //新增加的2个方法
    void registerListener(IOnNewBookArrivedListener listener);
    void unregisterListener(IOnNewBookArrivedListener listener);
}

接下来,我们的服务端也是要稍微的修改一下,每隔5s向感兴趣的用户提醒

//BookManagerService(服务端)

public class BookManagerService extends Service {

    private static final String TAG = "BMS";

    private CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<>();

    private AtomicBoolean mIsServiceDestoryed = new AtomicBoolean(false);

    //定义一个listener的list,存储所有注册用户的listener
    private CopyOnWriteArrayList<IOnNewBookArrivedListener> mListenerList = new CopyOnWriteArrayList<>();

    private Binder mBinder = new IBookManager.Stub() {

        @Override
        public List<Book> getBookList() throws RemoteException {
            return mBookList;
        }

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

        //实现IBookManager中的registerListener方法
        @Override
        public void registerListener(IOnNewBookArrivedListener listener) throws RemoteException {
            if(!mListenerList.contains(listener)){
                mListenerList.add(listener);
            }else {
                Log.i(TAG,"already exists");
            }
            Log.i(TAG,"registerListener size:" + mListenerList.size());
        }

        //unregisterListener
        @Override
        public void unregisterListener(IOnNewBookArrivedListener listener) throws RemoteException {
                if(mListenerList.contains(listener)){
                    mListenerList.remove(listener);
                    Log.i(TAG,"remove listener");
                }else {
                    Log.i(TAG,"can not remove listener");
                }
            Log.i(TAG,"unregisterListener size:" + mListenerList.size());
        }
    };

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

        mBookList.add(new Book(1,"Android"));
        mBookList.add(new Book(1,"IOS"));
        new Thread(new ServiceWorker()).start();//开启一个子线程,每隔5秒添加一本书
    }

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

    @Override
    public void onDestroy() {
        mIsServiceDestoryed.set(true);//设置标记位,结束子线程
        super.onDestroy();
    }

    private class ServiceWorker implements Runnable{
        @Override
        public void run() {

            while (!mIsServiceDestoryed.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();
                }
            }
        }
    }

    private void onNewBookArrived(Book book) throws RemoteException{
        mBookList.add(book);//每隔5秒向list中添加一本书
        Log.i(TAG,"onNewBookArrived size:" + mListenerList.size());
        for (int i = 0; i < mListenerList.size(); i++) {
            IOnNewBookArrivedListener listener = mListenerList.get(i);//拿到客户端传递过来的listener
            Log.i(TAG,"listener: "+ listener);
            listener.onNewBookArrived(book);//回调到客户端的onNewBookArrived方法,并且把图书作为参数传递给每个订阅的用户
        }
    }

}

最后,我们还需要修改一下客户端的代码,主要是两方面,首先是客户端要注册IOnNewBookArrivedListener到远程的服务器,这样当有新书时服务端才能通知客户端,同时在我们要在退的Activity的时候接触这个注册;另一方面,当有新书的时候,服务端会回调客户端的onNewBookArrived方法,但是这个方法是在客户端的Binder线程池中执行的,因此,为了便于进行UI操作,我们需要有一个Handler可以将其切换到客户端的主线程去执行,这个原理在Binder中已经做了分析了,这里不多说,客户端代码修改如下:

//BookManagerActivity (客户端)

public class BookManagerActivity extends AppCompatActivity {
    private static final String TAG = "BookManagerActivity";
    public static final int MESSAGE_NEW_BOOK_ARRIVED = 1;

    private IBookManager mRemoteBookManager;//用于接收远程binder转化成客户端AIDL

    private Handler mHhandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MESSAGE_NEW_BOOK_ARRIVED:
                    Log.i(TAG, "recieve new book :" + msg.obj);
                    break;
                default:
                    super.handleMessage(msg);
            }
        }
    };

    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            IBookManager bookManager = IBookManager.Stub.asInterface(service);
            try {
                mRemoteBookManager = bookManager;
                
                List<Book> list = bookManager.getBookList();
                Log.i(TAG, "list Type :" + list.getClass().getCanonicalName());
                Log.i(TAG, "list string : " + list.toString());
                Book newBook = new Book(3, "Android进阶");
                
                bookManager.addBook(newBook);
                Log.i(TAG, "add Book: " + newBook);
                List<Book> newList = bookManager.getBookList();
                Log.i(TAG, "query list : " + newList.toString());
                
                //注册
                bookManager.registerListener(mOnNewBookArrivedListener);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            mRemoteBookManager = null;
        }
    };

    //定义一个客户端的Binder对象  listener,用于传递给服务端注册回调
    private IOnNewBookArrivedListener mOnNewBookArrivedListener = new IOnNewBookArrivedListener.Stub(){
        @Override
        public void onNewBookArrived(Book newBook) throws RemoteException{
            mHandler.obtainMessage(MESSAGE_NEW_BOOK_ARRIVED,newBook).sendToTarget()}
    }
        

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_book_manager);

        Intent intent = new Intent(this, BookManagerService.class);
        bindService(intent, mConnection, Context.BIND_AUTO_CREATE);

    }

    @Override
    protected void onDestroy() {
        try {
            //解注册
            mRemoteBookManager.unregisterListener(mOnNewBookArrivedListener);
        } catch (RemoteException e) {
            e.printStackTrace();
        }
        unbindService(mConnection);
        super.onDestroy();
    }

}

在这里插入图片描述

7、解决解注册失败问题(不是同一个listener)

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

 private RemoteCallbackList<IOnNewBookArrivedListener> mListenerList = new RemoteCallbackList<IOnNewBookArrivedListener>();

然后修改registerListener 和unregisterListener这两个接口的实现

@Override
public void registerListener(IOnNewBookArrivedListener listener) throws RemoteException {
    mListenerList.registener(listener);
}

@Override
public void unregisterListener(IOnNewBookArrivedListener listener) throws RemoteException {
    mListenerList.unregistener(listener);
}

怎么样,使用起来是不是很简单,接下来我们修改onNewBookArrived方法,当有新书的时候,我们就要通知所有已注册的listener

private void onNewBookArrived(Book book) throws RemoteException{
    mBookList.add(book);
    final int N = mListenerList.beginBroadcast();
    for (int i = 0; i < N; i++) {
        IOnNewBookArrivedListener l = mListenerList.getBroadcast(i);
        if(i != null){
            l.onNewBookArrived(book);
        }
    }
    mListenerList.finishBroadcast();//必须配对使用
}
发布了126 篇原创文章 · 获赞 42 · 访问量 10万+

猜你喜欢

转载自blog.csdn.net/gaopinqiang/article/details/102877222