梳理Android的IPC進程間通訊(最新AndroidStudio的AIDL操做)

前言

前面梳理了Android的線程間的通訊《Thread、Handler和HandlerThread關係何在?》,這些都是在同一個進程中,那進程間的通訊,或者說不一樣的應用間的通訊該如何實現呢?這個時候就要用到AIDL(Android Interface Definition LanguageAndroid接口定義語言 )。java

使用方法(AndroidStudio)

我發現如今AIDL的教程基本上仍是eclipse的,可是在AndroidStudio裏面使用AIDL仍是有一些不一樣的,來看看怎麼用,首先新建一個工程當作server服務端:android

建立好後在任意文件夾右鍵New-->AIDL-->AIDL File,編輯文件名後會自動在src/main目錄下面新建aidl文件夾,包的目錄結構以下:app

  • maineclipse

    • aidlide

      • com.example.tee.testapplication.aidl
    • javagradle

      • com.example.tee.testapplication
    • res
    • AndroidManifest.xml

自動生成的aidl文件以下:ui

// AidlInterface.aidl
package com.example.tee.testapplication.aidl;

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

interface AidlInterface {
    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
            double aDouble, String aString);
}

咱們能夠看到aidl文件的代碼格式跟java很像,支持java的基礎類型以及List、Map等,若是是自定義類的話須要手動導入,咱們後面再說,先來最簡單的,新建一個 IMyAidlInterface.aidl文件,修改以下:.net

package com.example.tee.testapplication.aidl;

interface IMyAidlInterface {
     String getValue();
}

在接口中定義一個getValue方法,返回一個字符串,如今能夠編譯一下工程,找到app/build/generated/source/aidl/debug目錄,在咱們應用包名下會發現生成了一個Interface類,名字跟咱們定義的aidl的文件名字同樣,這說明其實aidl文件在最後仍是會轉換成接口來實現,並且這個文件不須要咱們維護,在編譯後自動生成。線程

而後新建一個類繼承Service:debug

public class MAIDLService extends Service{
    public class MAIDLServiceImpl extends IMyAidlInterface.Stub{
        @Override
        public String getValue() throws RemoteException {
            return "get value";
        }
    }
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return new MAIDLServiceImpl();
    }
}

在MAIDLService類中定義一個內部類繼承IMyAidlInterface.Stub,而且重寫咱們在aidl也就是在接口中定義的getValue方法,返回字符串get value

到了這裏,咱們就新建好了這個服務端,做用是在調用後返回一個字符串,最後在AndroidManifest文件中聲明:

<service
     android:name=".MAIDLService"
     android:process=":remote"//加上這句的話客戶端調用會建立一個新的進程
     android:exported="true"//默認就爲true,可去掉,聲明是否能夠遠程調用
    >
     <intent-filter>
        <category android:name="android.intent.category.DEFAULT" />
        <action android:name="com.example.tee.testapplication.aidl.IMyAidlInterface" />
     </intent-filter>
</service>

android:process=":remote"這一行的做用是聲明是否調用時新建進程,接下來寫客戶端代碼,新建一個工程,將剛纔建立的aidl文件拷貝到這個工程中,注意一樣也是要放在aidl文件夾下,而後在MainActivity中編寫代碼以下:

public class MainActivity extends AppCompatActivity {
    private TextView mValueTV;
    private IMyAidlInterface mAidlInterface = null;
    private ServiceConnection mServiceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mAidlInterface = IMyAidlInterface.Stub.asInterface(service);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        Intent intent = new Intent("com.example.tee.testapplication.aidl.IMyAidlInterface");
        bindService(intent, mServiceConnection, BIND_AUTO_CREATE);
        mValueTV = (TextView) findViewById(R.id.tv_test_value);
        mValueTV.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                try {
                    mValueTV.setText(mAidlInterface.getValue());
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        });
    }
    
    @Override
    protected void onDestroy() {
        if(mAidlInterface != null){
            unbindService(mServiceConnection);
        }
        super.onDestroy();
    }
}

注意這裏新建Intent的傳入的參數字符串是在manifest裏面自定義的action標籤,而且在onDestroy記得取消綁定服務。

執行結果就是咱們在點擊TextView時會顯示服務端給咱們返回的get value字符串

自定義的對象

剛纔咱們使用的是基礎類型String,在使用咱們本身定義的類的時候用上面的方法是不行的,用咱們自定義的類須要手動導入,修改剛纔咱們建立的做爲服務端的工程

首先在開始生成的aidl包下(全部aidl相關的文件都要放在這個包下)新建Student.java

public class Student implements Parcelable{
    public String name;
    public int age;
    protected Student(Parcel in) {
        readFromParcel(in);
    }
    public Student() {
    }

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

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

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

    public void readFromParcel(Parcel in){
        age = in.readInt();
        name = in.readString();
    }

    @Override
    public String toString() {
        return String.format(Locale.ENGLISH, "STUDENT[%s:%d]", name, age);
    }
}

須要實現Parcelable序列化接口,AndroidStudio會自動生成靜態內部類CREATORdescribeContents方法,這些部分咱們都不須要修改,用自動生成的就好。而後重寫writeToParcel方法,自定義readFromParcel方法,注意這兩個方法裏面的屬性順序必須一致,一個是寫入,一個是讀取。在構造方法Student(Parcel in)中調用readFromParcel(in)方法。

接下來新建Student.aidl文件(也是在aidl包中):

// Student.aidl
package com.example.tee.testapplication.aidl;

// Declare any non-default types here with import statements
parcelable Student;

注意這裏Student前面的關鍵字parcelable首字母是小寫哦,再修改IMyAidlInterface.aidl文件以下:

// IMyAidlInterface.aidl
package com.example.tee.testapplication.aidl;

// Declare any non-default types here with import statements
import com.example.tee.testapplication.aidl.Student;

interface IMyAidlInterface {
     Student getStudent();
     void setStudent(in Student student);
     String getValue();
}

定義了兩個方法,一個是設置Student,一個是獲取Student,在setStudent這個方法注意參數在類型前面有個in關鍵字,在aidl裏參數分爲in輸入,out輸出

如今在MAIDLService.java中重寫新加的兩個方法:

private Student mStudent;
public class MAIDLServiceImpl extends IMyAidlInterface.Stub{


    @Override
    public Student getStudent() throws RemoteException {
        return mStudent;
    }

    @Override
    public void setStudent(Student student) throws RemoteException {
        mStudent = student;
    }

    @Override
    public String getValue() throws RemoteException {
            return "get value : " + Thread.currentThread().getName() + Thread.currentThread().getId();
    }
}

服務端代碼修改完畢,來到客戶端工程,一樣要把剛纔的aidl包下的文件拷貝覆蓋過來,保持兩邊一致,而後在MainActivity.java中修改以下:

mValueTV = (TextView) findViewById(R.id.tv_test_value);
mStudentTV = (TextView) findViewById(R.id.tv_test_student);
mValueTV.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        try {
            mValueTV.setText(mAidlInterface.getValue());
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }
});
mStudentTV.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        try {
            Student student = new Student();
            student.age = 10;
            student.name = "Tom";
            mAidlInterface.setStudent(student);
            mStudentTV.setText(mAidlInterface.getStudent().toString());
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }
});

如今編譯工程,會發現工程會報錯,找不到類Student,咱們須要在app目錄下的build.gradle文件添加代碼以下:

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

也就是指定一下文件目錄,如今再編譯就沒有問題了

總結

Android的IPC使用起來仍是挺簡單的,AIDL文件的語法也跟咱們平時使用接口的時候很類似,可是它只支持基礎類型,只能引用AIDL文件,須要使用自定義類的時候要稍微麻煩一點。

相關文章
相關標籤/搜索