Android12_IPC方式之AIDL

Messenger是以串行的方式处理客户端发来的消息;

如果大量消息同时发送到服务端,服务端仍然只能一个个处理;

如果有大量的并发请求,用Messenger就不合适了;

Messenger主要作用是传递消息,有时候我们需要跨进程调用服务端的方法;

这就需要AIDL来实现跨进程调用服务端的方法;

1、服务端

创建一个Service来监听服务端的连接请求;

创建一个AIDL文件,将暴露给客户端的接口在这个AIDL文件中声明;

最后在Service中实现这个AIDL接口;

2、客户端

首先需要绑定服务端的Service,绑定成功之后,将服务端返回的Binder对象转成AIDL接口所属的类型;

接着就可以调用AIDL中的方法了;

3、AIDL接口的创建

为了方便AIDL的开发,建议把所有和AIDL相关的类和文件全部放入同一个包中;

这样做的好处是:当客户端要用时,可以直接把整个包复制到客户端工程中;

 

 1 package com.example.aidltest_server;
 2 
 3 import android.os.Parcel;
 4 import android.os.Parcelable;
 5 
 6 public class Book implements Parcelable {
 7     private String name;
 8 
 9     public Book(String name){
10         this.name = name;
11     }
12 
13     public String getName(){
14         return name;
15     }
16 
17     public void setName(String name){
18         this.name = name;
19     }
20 
21     @Override
22     public String toString(){
23         return "book name: "+name;
24     }
25 
26     @Override
27     public int describeContents(){
28         return 0;
29     }
30 
31     @Override
32     public void writeToParcel(Parcel dest,int flags){
33         dest.writeString(this.name);
34     }
35 
36     public void readFromParcel(Parcel dest){
37         name = dest.readString();
38     }
39 
40     protected Book(Parcel in){
41         this.name = in.readString();
42     }
43 
44     public static final Creator<Book> CREATOR = new Creator<Book>() {
45         @Override
46         public Book createFromParcel(Parcel source) {
47             return new Book(source);
48         }
49 
50         @Override
51         public Book[] newArray(int size) {
52             return new Book[size];
53         }
54     };
55 }

AIDL经常要用到Parcelable对象,除了定义一个类,比如Book类去实现Parcelable接口之外;

还需要创建一个和它同名的AIDL文件,并在其中声明为Parcelable类型;如上面所示;

服务端还要声明一个BookController.aidl

其具体实现放在了Service中;

 1 // BookController.aidl
 2 package com.example.aidltest_server;
 3 import com.example.aidltest_server.Book;
 4 import com.example.aidltest_server.AIDLCallBack;
 5 // Declare any non-default types here with import statements
 6 
 7 interface BookController {
 8     List<Book> getBookList();
 9     void addBookInOut(inout Book book);
10     void registerCallback(in AIDLCallBack cb);
11     void unregisterCallBack(in AIDLCallBack cb);
12 }

AIDLCallBack.aidl

这个是客户端提供给服务端的回调接口,在服务端中声明,但其具体实现由客户端提供;

// AIDLCallBack.aidl
package com.example.aidltest_server;

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

interface AIDLCallBack {
    void OnReply();
}

4、服务端的实现

  1 package com.example.aidltest_server;
  2 
  3 import android.app.Service;
  4 import android.content.Intent;
  5 import android.os.IBinder;
  6 import android.os.RemoteCallbackList;
  7 import android.os.RemoteException;
  8 import android.util.Log;
  9 
 10 import java.util.ArrayList;
 11 import java.util.List;
 12 
 13 public class AIDLService extends Service {
 14 
 15     private static final String TAG = "Server";
 16     private static final String SERVICE_ACTION = "com.example.aidltest_server.AIDLTEST";
 17     private List<Book> bookList;
 18     //远程回调函数注册机制,首先定义一个回调函数列表;
 19     private RemoteCallbackList<AIDLCallBack> mCallBackList = new RemoteCallbackList<>();
 20 
 21     public AIDLService() {
 22         Log.d(TAG, "AIDLService: ");
 23     }
 24 
 25     @Override
 26     public void onCreate(){
 27         super.onCreate();
 28         Log.d(TAG, "onCreate: ");
 29         bookList = new ArrayList<>();
 30         initData();
 31     }
 32 
 33     @Override
 34     public IBinder onBind(Intent intent) {    //一旦服务端和客户端建立连接,返回BookController AIDL的实现类stub
 35         Log.d(TAG, "onBind: ");
 36         if(intent.getAction().equals(SERVICE_ACTION )){
 37             Log.d(TAG, "onBind Return ibinder: ");
 38             return stub;
 39         }
 40         Log.d(TAG, "onBind Return null: ");
 41         return null;
 42     }
 43 
 44     @Override
 45     public void onDestroy(){
 46         Log.d(TAG, "onDestroy: ");
 47     }
 48 
 49     private void initData(){
 50         Book book1 = new Book("或者");
 51         Book book2 = new Book("活着");
 52         Book book3 = new Book("火者");
 53         bookList.add(book1);
 54         bookList.add(book2);
 55         bookList.add(book3);
 56     }
 57 
 58     private final BookController.Stub stub = new BookController.Stub() {   //这里是AIDL接口的实现
 59         @Override
 60         public List<Book> getBookList() throws RemoteException{
 61             return bookList;
 62         }
 63 
 64         @Override
 65         public void addBookInOut(Book book) throws RemoteException{
 66             if(book != null){
 67                 bookList.add(book);
 68                 sendResponse();
 69             }
 70             else
 71             {
 72                 Log.e(TAG,"接收到空对象");
 73             }
 74         }
 75 
 76         @Override
 77         public void registerCallback(AIDLCallBack cb){   //客户端会调用该接口注册回调函数
 78             if(cb != null){
 79                 mCallBackList.register(cb);
 80             }
 81         }
 82 
 83         @Override
 84         public void unregisterCallBack(AIDLCallBack cb){  //客户端调用该接口去注册回调函数
 85             if(cb != null){
 86                 mCallBackList.unregister(cb);
 87             }
 88         }
 89     };
 90 
 91     private void sendResponse(){              //服务端调用回调函数的封装函数
 92         int len = mCallBackList.beginBroadcast();
 93         for(int i = 0; i<len; i++){
 94             try{
 95                 mCallBackList.getBroadcastItem(i).OnReply();
 96             }catch(RemoteException e){
 97                 e.printStackTrace();
 98             }
 99         }
100         mCallBackList.finishBroadcast();
101     }
102 }

5、客户端的实现

  1 package com.example.aidltest_client;
  2 
  3 import androidx.appcompat.app.AppCompatActivity;
  4 
  5 import android.content.ComponentName;
  6 import android.content.Context;
  7 import android.content.Intent;
  8 import android.content.ServiceConnection;
  9 import android.os.Bundle;
 10 import android.os.IBinder;
 11 import android.os.RemoteException;
 12 import android.util.Log;
 13 import android.view.View;
 14 
 15 import com.example.aidltest_server.AIDLCallBack;
 16 import com.example.aidltest_server.Book;
 17 import com.example.aidltest_server.BookController;
 18 
 19 import java.util.List;
 20 
 21 public class MainActivity extends AppCompatActivity {
 22 
 23     private final String TAG = "Client";
 24 
 25     private BookController bookController;
 26 
 27     private boolean connected;
 28 
 29     private List<Book> bookList;
 30     private AIDLCallBack callback = new AIDLCallBack.Stub() {   //AIDLCallBack  AIDL接口的实现
 31         @Override
 32         public void OnReply() throws RemoteException {
 33             Log.e(TAG,"CallBack from Server");
 34         }
 35     };
 36 
 37     private ServiceConnection serviceConnection = new ServiceConnection() {  //服务连接类,客户端必须重写一些回调函数
 38         @Override
 39         public void onServiceConnected(ComponentName name, IBinder service) {   //连接-回调函数
 40             Log.e(TAG,"回调了onServiceConnected");
 41             bookController = BookController.Stub.asInterface(service);
 42             try {
 43                 bookController.registerCallback(callback);         //建立连接时,注册回调函数
 44             }catch(RemoteException e){
 45                 e.printStackTrace();
 46             }
 47             connected = true;
 48         }
 49 
 50         @Override
 51         public void onServiceDisconnected(ComponentName name) {             //断开连接-回调函数
 52             connected = false;
 53         }
 54     };
 55 
 56     private View.OnClickListener clickListener = new View.OnClickListener() {
 57         @Override
 58         public void onClick(View v) {
 59             switch (v.getId()){
 60                 case R.id.btn_getBookList:
 61                     if(connected){
 62                         try{
 63                             bookList = bookController.getBookList();
 64                         }catch (RemoteException e){
 65                             e.printStackTrace();
 66                         }
 67                         log();
 68                     }
 69                     break;
 70                 case R.id.btn_addBook_inout:
 71                     if(connected){
 72                         Book book = new Book("new Book");
 73                         try{
 74                             bookController.addBookInOut(book);
 75                             Log.e(TAG,"向服务器以Inout的方式添加了一本新书");
 76                             Log.e(TAG,"新书名:"+book.getName());
 77                         }catch(RemoteException e){
 78                             e.printStackTrace();
 79                         }
 80                     }
 81                     break;
 82             }
 83         }
 84     };
 85 
 86     @Override
 87     protected void onCreate(Bundle savedInstanceState) {
 88         super.onCreate(savedInstanceState);
 89         setContentView(R.layout.activity_main);
 90         findViewById(R.id.btn_getBookList).setOnClickListener(clickListener);
 91         findViewById(R.id.btn_addBook_inout).setOnClickListener(clickListener);
 92         bindService();
 93     }
 94 
 95     @Override
 96     protected void onDestroy(){
 97         super.onDestroy();
 98         if(connected){
 99             try {
100                 bookController.unregisterCallBack(callback);     //申请断开连接前,先去注册回调函数
101             }catch (RemoteException e){
102                 e.printStackTrace();
103             }
104             unbindService(serviceConnection);                //断开连接
105         } 
106     }
107 
108     private void bindService(){
109         Intent intent = new Intent();
110         intent.setPackage("com.example.aidltest_server");
111         intent.setAction("com.example.aidltest_server.AIDLTEST");
112         bindService(intent,serviceConnection, Context.BIND_AUTO_CREATE);  //对服务端发起连接请求
113     }
114 
115     private void log(){
116         for(Book book:bookList){
117             Log.e(TAG,book.toString());
118         }
119     }
120 }

6、一些问题:

如果通过AIDL方式远程调用耗时任务,可能会导致客户端产生ANR;所以要注意客户端如果要调用耗时任务时,可以把调用放在非UI线程中进行;

还有一种情况,Binder可能意外死亡,往往是由于服务端进程意外停止了;这时候我们需要重新连接服务,有两种方法:

1、给Binder设置DeathRecipient监听,当Binder死亡时,我们会收到binderDied回调;->在Binder的线程池中被回调

  onServiceConnected函数中,当客户端绑定远程服务时,给binder设置死亡代理,当Binder死亡时,客户端会收到通知,可以在该通知中重连远程服务;

 1         private IBinder.DeathRecipient mBinderPoolDeathRecipient = new IBinder.DeathRecipient() {
 2             @Override
 3             public void binderDied() {
 4                 Log.w(TAG, "binderDied: ");
 5                 mBinderPool.asBinder().unlinkToDeath(mBinderPoolDeathRecipient, 0);
 6                 mBinderPool = null;
 7                 connectBinderPoolService();
 8             }
 9         };
10     };

2、在onServiceDisconnected中重连远程服务;->在客户端的UI线程被回调

原文地址:https://www.cnblogs.com/grooovvve/p/12484440.html