zoukankan      html  css  js  c++  java
  • 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线程被回调

  • 相关阅读:
    同步gitlab与github
    配置hosts快速访问GitHub
    Linux下Julia安装
    LATEX图片位置
    IPOPT安装
    sqlplus传入shell变量
    users表空间满导致应用无法连接
    坏块修复 ORA-00701
    Oracle中INITRANS和MAXTRANS参数(转)
    DBMS_ROWID包的使用
  • 原文地址:https://www.cnblogs.com/grooovvve/p/12484440.html
Copyright © 2011-2022 走看看