AndroidIPC-AIDL

AndroidIPC-AIDL

一、概述

AIDL意思即Android Interface Definition Language,翻譯過來就是android接口定義語言,是用於定義服務器和客戶端通信接口的一種描述語言,可以拿來生成用於IPC的代碼。從某種意義上說AIDL其實是一個模板,因爲在使用過程中,實際起作用的並不是AIDL文件,而是據此生成的一個Interface的實例代碼,AIDL其實是爲了避免我們重複編寫代碼而出現的一個模板。

通過AIDL,可以在一個進程中獲取另一個進程的數據和調用其暴露出來的方法,從而滿足進程間通信的需求。通常,暴露方法給其他應用進行調用的應用稱爲服務端,調用其他應用的方法的應用稱爲客戶端,客戶端通過綁定服務端的Service來進行交互。

二、語法

AIDL的語法十分簡單,與Java語言基本保持一致,需要記住的規則有以下幾點:

1.AIDL文件以.aidl爲後綴名

2.AIDL支持的數據類型分爲以下幾種

  • 八種基本數據類型
  • String,CharSequence
  • 實現了Parcelable接口的數據類型
  • List類型,List承載的數據必須是AIDL支持的類型,或者是其他生命的AIDL對象
  • Map類型,Map承載的數據必須是AIDL支持的類型,或者是其他生命的AIDL對象

3.AIDL文件可以分爲兩類。一類用來聲明實現了Parcelable接口的數據類型,以供其他AIDL文件使用那些非默認支持的數據類型。還有一類用來定義接口方法,聲明要暴露哪寫接口給客戶端調用,定向Tag用來標註這些方法的參數

4.定向Tag。定向Tag表示在跨進程通信中數據的流向,用於標註方法的參數值,分別爲in,out,inout三種。其中in表示數據只能從客戶端流向服務端,out表示數據只能由服務端流向客戶端,而inout則表示數據可以在服務端口和客戶端之間雙向流通。此外,如果AIDL方法接口的參數值類型是基本數據類型,那麼這些參數的定向Tag默認且只能是in

5.明確導包。在AIDL文件中需要明確標明引用到的數據類型所在的包名,即使兩個文件處在同一個包下。

三、服務端編碼

新建一個工程,包名爲com.example.aidlserver

首先,在應用中需要用到一個Student類,而Student類是兩個應用間都需要用到的,所以也需要在AIDL中聲明Student類,爲了避免出現類名重複導致無法創建文件的錯誤,這裏需要先建立StudentAidl文件,之後再創建Student類

右鍵點擊新建一個AIDL文件,命名爲Student。創建完成之後,系統會默認創建一個aidl文件夾

可以刪掉文件中自動生成的接口和方法,因爲這個aidl文件主要是爲了聲明Student類

在這裏插入圖片描述

接下來定義Student類,並且實現Parcelable接口

public class Student implements Parcelable {
    private String name;
    private int age;
    private boolean isAdult;

    public Student(){}
    public Student(String name, int age, boolean isAdult) {
        this.name = name;
        this.age = age;
        this.isAdult = isAdult;
    }

    protected Student(Parcel in) {
        name = in.readString();
        age = in.readInt();
        isAdult = in.readByte() != 0;
    }

    public static final Creator<Student> CREATOR = new Creator<Student>() {
        @Override
        public Student createFromParcel(Parcel in) {
            return new Student(in);
        }

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

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public boolean isAdult() {
        return isAdult;
    }

    public void setAdult(boolean adult) {
        isAdult = adult;
    }

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

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(name);
        dest.writeInt(age);
        dest.writeByte((byte) (isAdult ? 1 : 0));
    }

    public void readFromParcel(Parcel parcel){
        name = parcel.readString();
        age = parcel.readInt();
        isAdult = parcel.readByte()==1;
    }
}

然後,定義接口aidl文件,我們提供四個方法

interface IMyAidlInterface {
    String getName();

    void sendStudent(inout Student student);

    void sendStudentIn(in Student student);

    void sendStudentOut(out Student student);
}

第一個方法用於向客戶端返回數據

第二,三,四個方法用於客戶端向服務端提交數據,分別驗證三個定向Tag的區別

之前說過,在進程間通信中真正起作用的並不是AIDL文件,而是系統據此而生成的文件,可以在以下目錄查看系統生成的文件。
在這裏插入圖片描述

創建或者修改AIDL文件後,需要Make Project,這樣系統才能生成我們需要的文件

接下來需要創建一個Service供客戶端遠程綁定了

public class MyService extends Service {

    private IMyAidlInterface iMyAidlInterface = new IMyAidlInterface.Stub() {
        @Override
        public String getName() throws RemoteException {
            return "test";
        }

        @Override
        public void sendStudent(Student student) throws RemoteException {
            Log.d("InOut", "name = " +student.getName());
            Log.d("InOut", "age = "+student.getAge());
            Log.d("InOut", "isAdult = " +student.isAdult());
            student.setName("服務端修改了student的name InOut");
        }

        @Override
        public void sendStudentIn(Student student) throws RemoteException {
            Log.d("In", "name = " +student.getName());
            Log.d("In", "age = "+student.getAge());
            Log.d("In", "isAdult = " +student.isAdult());
            student.setName("服務端修改了student的name In");
        }

        @Override
        public void sendStudentOut(Student student) throws RemoteException {
            Log.d("Out", "name = " +student.getName());
            Log.d("Out", "age = "+student.getAge());
            Log.d("Out", "isAdult = " +student.isAdult());
            student.setName("服務端修改了student的name Out");
        }
    };
    public MyService() {
    }

    @Override
    public IBinder onBind(Intent intent) {
        return iMyAidlInterface.asBinder();
    }
}

可以看到,我們聲明瞭一個IMyAidlInterface的實例,並重寫了它的四個方法。

在onBind方法中我們返回的是iMyAidlInterface.asBinder();

最後,服務端還有一個地方需要注意

因爲服務端的Service需要被客戶端遠程綁定,所以客戶端要能找到這個Service,可以通過先指定包名,之後再配置action的值來找到Service,需要在manifest文件中修改Service的註冊。

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

四、客戶端編碼

客戶端需要再新建一個工程,包名爲com.example.aidlclient

將服務端中的AIDL文件以及Student類複製過來,將aidl文件夾整個複製到和java文件夾同一層級下,不需要改動任何代碼

AIDLServer

在這裏插入圖片描述

AIDLClient

在這裏插入圖片描述

修改佈局文件,添加三個按鈕

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity"
    android:orientation="vertical">

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="InOut"
        android:id="@+id/inout"
        android:onClick="onClick"/>

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="In"
        android:id="@+id/in"
        android:onClick="onClick"/>
    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Out"
        android:id="@+id/out"
        android:onClick="onClick"/>
</LinearLayout>
public class MainActivity extends AppCompatActivity {
    private static final String TAG = "MainActivity";

    private IMyAidlInterface iMyAidlInterface;
    private ServiceConnection connection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            iMyAidlInterface = IMyAidlInterface.Stub.asInterface(service);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Intent intent = new Intent();
        intent.setAction("serverService");
        intent.setPackage("com.example.aidlserver");
        bindService(intent,connection,BIND_AUTO_CREATE);

    }

    public void onClick(View view) {
        switch(view.getId()){
            case R.id.inout:
                Student student = new Student("inout student",20,true);
                try {
                    iMyAidlInterface.sendStudent(student);
                    Log.d(TAG, "新名字 = "+student.getName());
                    Toast.makeText(this, iMyAidlInterface.getName(), Toast.LENGTH_SHORT).show();
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
                break;
            case R.id.out:
                Student studentOut = new Student("out student",20,true);
                try {
                    iMyAidlInterface.sendStudentOut(studentOut);
                    Log.d(TAG, "新名字 = "+studentOut.getName());
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
                break;
            case R.id.in:
                Student studentIn = new Student("in student",20,true);
                try {
                    iMyAidlInterface.sendStudentIn(studentIn);
                    Log.d(TAG, "新名字 = "+studentIn.getName());
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
                break;
        }


    }
}

先綁定ServiceiMyAidlInterface = IMyAidlInterface.Stub.asInterface(service);,通過這行代碼獲取到接口的實例,通過這個調用那四個方法。

我們按順序分別點擊了InOut,In,Out

結果

InOut

AIDLClient
在這裏插入圖片描述

AIDLServer
在這裏插入圖片描述

可以看到,student的信息可以傳到服務端中,而且服務端中對name的修改也會同步到客戶端

In

AIDLClient
在這裏插入圖片描述

AIDLServer
在這裏插入圖片描述

可以看到,數據可以從客戶端傳到服務端,但是服務端對name的修改並沒有同步到客戶端

也就是說數據只能從客戶端傳到服務端

Out

AIDLClient
在這裏插入圖片描述

AIDLServer
在這裏插入圖片描述

可以看到,客戶端的數據並沒有傳入到服務端中,而服務端中對name的修改卻同步到了客戶端

也就是說數據只能從服務端傳到客戶端。

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