跨進程通信AIDL

AIDL是什麼,作用是什麼

項目中涉及到兩個應用即兩個進程間進行數據通信,而Android中進程之間的內存地址是相互獨立的,一個進程無法訪問另一個進程的內存地址。這裏簡單介紹下進程的概念,進程是一個程序或者應用的實體,每個進程都擁有自己獨立的內存地址空間。官方文檔上講,爲進行通信,進程需將其對象分解成可供操作系統理解的原語,並將其編組爲可供您操作的對象。編寫執行該編組操作的代碼較爲繁瑣,因此 Android 會使用 AIDL來完成跨進程通信。

Android中的跨進程其實就是IPC(Inter-Process Communication)通信,而AIDL正是IPC通信的機制。
AIDL全稱是Android Interface Definition Language,是安卓接口定義的意思,通過定義相關的接口來實現跨進程通信。


AIDL使用

1.AIDl支持的數據類型
AIDL 使用一種簡單語法,允許您通過一個或多個方法(可接收參數和返回值)來聲明接 口。參數和返回值可爲任意類型,甚至是 AIDL 生成的其他接口。
您必須使用 Java 編程語言構建 .aidl 文件。每個 .aidl 文件均須定義單個接口,並且只需要接口聲明和方法簽名。
默認情況下,AIDL 支持下列數據類型:
Java 編程語言中的所有原語類型(如 int、long、char、boolean 等java基本數據類型)
1.String
2.CharSequence
3.List
List 中的所有元素必須是以上列表中支持的數據類型,或者您所聲明的由 AIDL 生成的其他接口或 Parcelable 類型。您可選擇將 List 用作“泛型”類(例如,List)。儘管生成的方法旨在使用 List 接口,但另一方實際接收的具體類始終是 ArrayList。
4. Map
Map 中的所有元素必須是以上列表中支持的數據類型,或者您所聲明的由 AIDL 生成的其他接口或 Parcelable 類型。不支持泛型 Map(如 Map<String,Integer> 形式的 Map)。儘管生成的方法旨在使用 Map 接口,但另一方實際接收的具體類始終是 HashMap。
5.其他 AIDL 生成的接口
6.實現 Parcelable 的類數據類型

2.編寫AIDL相關代碼
編寫服務端代碼
創建實體類Person類,實現Parcelable接口,用於跨進程通信的實體類。

public class People implements Parcelable {
            private String id;
            private String name;

   @Override
   public void writeToParcel(Parcel dest, int flags) {
            dest.writeString(id);
            dest.writeString(name);
    }

    @Override
    public int describeContents() {
           return 0;
    }

    public static final Creator<People> CREATOR = new Creator<People>() {
        @Override
        public People createFromParcel(Parcel in) {
             People people =  new People();
             people.setId(in.readString());
             people.setName(in.readString());
             return people;
        }

        @Override
        public People[] newArray(int size) {
             return new People[size];
        }
    };

    public String getId() {
         return id;
    }

    public void setId(String id) {
         this.id = id;
    }

    public String getName() {
         return name;
    }

    public void setName(String name) {
         this.name = name;
    }
}

創建.aldl文件,這裏包括兩個文件,一個是實體類People的文件,一個是接口文件

// People.aidl
package com.uniubi.aidlapplication;

parcelable People;
  // People.aidl
package com.uniubi.aidlapplication;
import com.uniubi.aidlapplication.People;

interface IRemoteService {
   void basicTypes(int a, long b, in People person, in String d);
}

這裏要記住需要導入People類,否則.aidl文件會找不到。

實現接口

點擊android studio的make project
會在build/generated//Users/chenxuming/generated/aidl_source_output_dir/debug/compileDebugAidl/out/目錄下生成IRemoteService.java文件。
在這裏插入圖片描述
記住一定要在當前moudle的gradle文件裏指定.aidl文件路徑,否則編譯會找不到People這個類,
在這裏吃了個大虧。

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']
       }
   }

查看生成的java文件,可以看出接口IRemoteService類繼承android.os.IInterface接口,裏面包含了一個繼承Binder的Sub子類,並且這個Sub子類實現IRemoteService接口,後面我們將會具體講講這個生成的IRemoteService類具體源碼。

創建Service,並在其中創建binder對象,通過onBind方法返回這個binder對象,到此爲止服務端代碼編寫完成。

public class RemoteService extends Service {

  private static final String TAG = "RemoteService";
  private IRemoteService.Stub binder = new IRemoteService.Stub() {

       @Override
       public void basicTypes(int a, long b, People person, String d) throws RemoteException {
           Log.i(TAG, "basicTypes" + person.getName());
       }
   };

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

向客戶端公開接口
1.將服務端src/main下面的整個aidl目錄下的文件拷貝到客戶端相同的位置,在客戶端項目下同樣別忘記了在當前module下的gradle配置sourceSets,能讓.aidl文件找到People類,然後make project。
2.綁定遠程服務,這裏必須是綁定遠程服務,“com.uniubi.aidlapplication.RemoteService”是遠程服務的actiob name,通過隱式的方式綁定。

 Intent intent1 = new Intent();
 intent1.setComponent(new ComponentName("com.uniubi.aidlapplication", "com.uniubi.aidlapplication.RemoteService"));
 bindService(intent1, serviceConnection, BIND_AUTO_CREATE);
private IRemoteService iRemoteService;

private ServiceConnection serviceConnection = new ServiceConnection() {

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            Log.i("MainActivity", "onServiceConnected");
            iRemoteService = IRemoteService.Stub.asInterface(service);

        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            Log.i("MainActivity", "onServiceDisconnected");
        }
    };

通過代碼可以看出綁定遠程服務成功後,能拿到IBinder對象,然後通過IRemoteService.Stub.asInterface(service)轉成我們需要的iRemoteService對象,然後通過
iRemoteService對象,就可以進行跨進程請求了。

 try {
       if(iRemoteService != null){
          iRemoteService.basicTypes(1, 2, people, "呵呵");
       }
 } catch (RemoteException e) {
        Log.i("MainActivity", e.getMessage());
         e.printStackTrace();
}

AIDL源碼分析
生成的代碼格式比較亂,最好先格式化下方便閱讀。

 /*
 * This file is auto-generated.  DO NOT MODIFY.
 * Original file: /Users/chenxuming/AndroidStudioProjects/AIDLClient/app/src/main/aidl/com/uniubi/aidlapplication/IRemoteService.aidl
 */
package com.uniubi.aidlapplication;

public interface IRemoteService extends android.os.IInterface {
    /**
     * Local-side IPC implementation stub class.
     */
    public static abstract class Stub extends android.os.Binder implements com.uniubi.aidlapplication.IRemoteService {
        private static final java.lang.String DESCRIPTOR = "com.uniubi.aidlapplication.IRemoteService";

        /**
         * Construct the stub at attach it to the interface.
         */
        public Stub() {
            this.attachInterface(this, DESCRIPTOR);
        }

        /**
         * Cast an IBinder object into an com.uniubi.aidlapplication.IRemoteService interface,
         * generating a proxy if needed.
         */
        public static com.uniubi.aidlapplication.IRemoteService asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.uniubi.aidlapplication.IRemoteService))) {
                return ((com.uniubi.aidlapplication.IRemoteService) iin);
            }
            return new com.uniubi.aidlapplication.IRemoteService.Stub.Proxy(obj);
        }

        @Override
        public android.os.IBinder asBinder() {
            return this;
        }

        @Override
        public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
            java.lang.String descriptor = DESCRIPTOR;
            switch (code) {
                case INTERFACE_TRANSACTION: {
                    reply.writeString(descriptor);
                    return true;
                }
                case TRANSACTION_basicTypes: {
                    data.enforceInterface(descriptor);
                    int _arg0;
                    _arg0 = data.readInt();
                    long _arg1;
                    _arg1 = data.readLong();
                    com.uniubi.aidlapplication.People _arg2;
                    if ((0 != data.readInt())) {
                        _arg2 = com.uniubi.aidlapplication.People.CREATOR.createFromParcel(data);
                    } else {
                        _arg2 = null;
                    }
                    java.lang.String _arg3;
                    _arg3 = data.readString();
                    this.basicTypes(_arg0, _arg1, _arg2, _arg3);
                    reply.writeNoException();
                    return true;
                }
                default: {
                    return super.onTransact(code, data, reply, flags);
                }
            }
        }

        private static class Proxy implements com.uniubi.aidlapplication.IRemoteService {
            private android.os.IBinder mRemote;

            Proxy(android.os.IBinder remote) {
                mRemote = remote;
            }

            @Override
            public android.os.IBinder asBinder() {
                return mRemote;
            }

            public java.lang.String getInterfaceDescriptor() {
                return DESCRIPTOR;
            }

            @Override
            public void basicTypes(int a, long b, com.uniubi.aidlapplication.People person, java.lang.String d) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    _data.writeInt(a);
                    _data.writeLong(b);
                    if ((person != null)) {
                        _data.writeInt(1);
                        person.writeToParcel(_data, 0);
                    } else {
                        _data.writeInt(0);
                    }
                    _data.writeString(d);
                    mRemote.transact(Stub.TRANSACTION_basicTypes, _data, _reply, 0);
                    _reply.readException();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }
        }

        static final int TRANSACTION_basicTypes = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
    }

    public void basicTypes(int a, long b, com.uniubi.aidlapplication.People person, java.lang.String d) throws android.os.RemoteException;
}

不難發現IRemoteService這個類由兩部分組成,一部分是聲明的接口,一部分是繼承Binder類的Sub內部類,這個Sub內部類有兩個主要的方法,asInterface() 和onTransact()方法,還有個內部類Proxy,一看名字就知道是代理模式,這裏就不細說代理模式了。
一步步來,首先看下iRemoteService = IRemoteService.Stub.asInterface(service)這行代碼,將服務端的binder對象解析成客戶端的iRemoteService對象,該方法返回的是IRemoteService.Stub.Proxy對象,也就是說iRemoteService對象就是IRemoteService.Stub.Proxy,然後再看調用 iRemoteService.basicTypes(1, 2, people, “呵呵”)的地方,其實看的就是Proxy對象的basicTypes方法,看到關鍵代碼mRemote.transact(Stub.TRANSACTION_basicTypes, _data, _reply, 0);是通過mRemote對象的transact方法來進行通信,mRemote對象就是遠程服務端的Sub對象,點進去查看源碼,主要查看onTransact方法,而上面也講到Sub類裏是實現了Binder類的onTransact方法的(上面已貼出來,這裏不重複貼出)。

 public final boolean transact(int code, @NonNull Parcel data, @Nullable Parcel reply,
            int flags) throws RemoteException {
        if (false) Log.v("Binder", "Transact: " + code + " to " + this);
        if (data != null) {
            data.setDataPosition(0);
        }
        boolean r = onTransact(code, data, reply, flags);
        if (reply != null) {
            reply.setDataPosition(0);
        }
        return r;
    }

發現onTransact是通過code來指定具體調用行爲,關鍵代碼this.basicTypes(_arg0, _arg1, _arg2, _arg3);而這裏的this就是遠程服務端的Sub對象,具體basicTypes方法實現得去服務端看,客戶端是看不到。到了這裏整個客戶端服務端跨進程通信的流程講解完畢。


總結

1.asInterface(android.os.IBinder obj)
用於服務端的Binder對象轉換成客戶端所需的AIDL接口類型的對象,這種轉換過程是區分進程的(如果客戶端和服務端位於同一個進程,那麼此方法返回的就是服務端的Stub對象本身,否則返回的是系統封裝後的Stub.proxy對象)

2.onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags)
客戶端跨進程請求主要是通過遠程服務binder對象的onTransact方法完成。
code : 確定客戶端請求的目標方法是什麼。
data : 如果目標方法有參數的話,就從data取出目標方法所需的參數。
reply : 當目標方法執行完畢後,如果目標方法有返回值,就向reply中寫入返回值。
flag : 0

3.DESCRIPTOR
Binder的唯一標識,一般用當前AIDL接口名錶示。

總之一切都是通過binder對象來完成,遠程服務端持有binder對象實體,本地客戶端Proxy持有服務端binder對象引用,並通過Sub.asInterface()方法轉換成本地的接口類對象。
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章