Android Studio中使用AIDL

一、什麼是AIDL

AIDL 的全名是 Android Interface definition language,按照字面理解,就是一種在安卓的接口定義語言,而它的作用就是主要用於不同進程之間的通信

二、應用場景

Android中不同進程是不共享同一片內存區域的,通常情況下,一個App就是一個進程。而我們也可以在AndroidManifest裏進行配置,讓某一個組件獨立出去,最常見的就是將Service獨立的運行在一個進程裏。例如支付服務,音樂服務,導航服務,守護進程等,這樣可以保證服務不被程序的其它部分所幹擾,或是保證2個進程不會同時被系統殺死,或是一定程度上提高服務的安全性。但是,要想與這些不在同一個進程中的服務進行通信的話,傳統的單例啦什麼的都是不可用的,於是就需要使用AIDL了

三、使用

在Eclipse裏AIDL的教程很多,但是現在都開始逐步轉變成Android Studio了,我們就看一看Studio裏如何使用AIDL與Service進行通信

1.建立Activity

首先我們建立一個Activity,裏面有3個按鈕,分別是BindService、ControlService、ChangeData,分別用來 鏈接服務、模擬對服務的控制、和與服務進行傳值

activity _main.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    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:id="@+id/btn_bind"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="綁定服務" />

    <Button
        android:id="@+id/btn_control"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="控制服務" />

    <Button
        android:id="@+id/btn_change"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="傳值" />
</LinearLayout>

在Activity裏綁定佈局,並設施監聽,看以下 MainActivity

public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    private Button bindBtn,controlBtn,changeDataBtn;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        bindBtn = (Button) findViewById(R.id.btn_bind);
        controlBtn = (Button) findViewById(R.id.btn_control);
        changeDataBtn = (Button) findViewById(R.id.btn_change);

        bindBtn.setOnClickListener(this);
        controlBtn.setOnClickListener(this);
        changeDataBtn.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()){
            case R.id.btn_bind://綁定服務
                break;
            case R.id.btn_control://控制服務
                break;
            case R.id.btn_change://傳遞數據
                break;
        }
    }
}

着就是簡單的佈局

2.我們去寫一TestService

public class TestService extends Service {
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
}

然後去AndroidManifest把我們剛寫的服務註冊一下

<service android:name=".TestService"
         android:process=":remote">
            <intent-filter>
                <action android:name="com.example.chenfengyao.aidldemo.TestService"/>
            </intent-filter>
</service>
我們的服務是要用隱式Intent啓動的,所以要有action屬性,而
android:process=":remote"
着一句的意思就是,有該屬性的服務將在一個獨立的進程中運行了,加上“:remote”則,進程名字將是這個App的進程名加:remot,另外,這裏的“:remote”是可以寫別的的,不過大家都習慣於寫它了。

3.生成AIDL文件


選擇 File->New->AIDL->AIDL File
然後我們輸入名字 MyTestAIDL,點擊Finish,我們就創建了一個AIDL文件,看看系統幫我們寫了什麼
// MyTestAIDL.aidl
package com.example.chenfengyao.aidldemo;

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

interface MyTestAIDL {
    /**
     * 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文件裏已經寫好了一個方法,而且通過讀註釋,我們知道,利用AIDL傳遞int,long,boolean,float,double,String這些數據的時候是不需要別的工作的,直接傳就好了。我們先把系統的這個方法刪除了,寫一個自己的方法。
interface MyTestAIDL {
    void controlService(int flag);
}
寫完了之後,我們編譯一下,只有編譯了,系統纔會將這個AIDL文件給我們生成對應的JAVA代碼來使用,不然是用不了的。

4.與服務綁定

編譯完成之後,我們就可以使用我們的AIDL文件了,首先是Service端的綁定
public class TestService extends Service {

    MyTestAIDL.Stub stub = new MyTestAIDL.Stub() {
        @Override
        public void controlService(int flag) throws RemoteException {
            Log.i("TestService","flag:"+flag);
        }
    };

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return stub;
    }
}
這裏我們首先 創建一個Stub對象,在這個Stub對象裏,會要求我們重寫剛剛在AIDL裏寫的方法的,然後將這個Stub對象通過onBind方法返回,這樣服務端就寫好了。

5.與Activity的綁定

public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    private Button bindBtn,controlBtn,changeDataBtn;
    
    private MyTestAIDL myTestAIDL;
    private ServiceConnection connection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            myTestAIDL = MyTestAIDL.Stub.asInterface(service);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            myTestAIDL = null;
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        bindBtn = (Button) findViewById(R.id.btn_bind);
        controlBtn = (Button) findViewById(R.id.btn_control);
        changeDataBtn = (Button) findViewById(R.id.btn_change);

        bindBtn.setOnClickListener(this);
        controlBtn.setOnClickListener(this);
        changeDataBtn.setOnClickListener(this);
        
        
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()){
            case R.id.btn_bind://綁定服務
                Intent intent = new Intent("com.example.chenfengyao.aidldemo.TestService");
                intent.setPackage("com.example.chenfengyao.aidldemo");
                bindService(intent,connection,BIND_AUTO_CREATE);
                break;
            case R.id.btn_control://控制服務
                if(myTestAIDL!=null){
                    try {
                        myTestAIDL.controlService(0);
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                }
                break;
            case R.id.btn_change://傳遞數據
                break;
        }
    }
}
首先,我們創建一個AIDL對象,然後在ServiceConnection的連接成功方法裏,將IBinder通過Stub的asInterface來給AIDL賦值,然後在連接服務的按鈕裏建立一個Intent對象,因爲是利用隱式Intent啓動的,所以需要給它action標籤裏的內容,另外需要注意的是,Android5.0之後Intent還必須加上目標的包名,不然會報錯的。最後通過bindService方法將Activity也連接上。再在controlService按鈕的回調裏,調用AIDL對象裏controlService方法,就可以調用服務,並將值傳給Service

6.傳遞複雜數據

最後,我們來看看AIDL傳遞複雜數據怎麼寫。首先我們先建立一個數據類TestBean,讓它實現Parcelable接口
public class TestBean implements Parcelable{
    String testString;
    int testInt;
    public TestBean(){}

    protected TestBean(Parcel in) {
        testString = in.readString();
        testInt = in.readInt();
    }

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

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

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

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


然後我們把這個數據類拖到AIDL文件夾裏

然後我們再建一個和數據類同名的AIDL文件,TestBean.aidl
// TestBean.aidl
package com.example.chenfengyao.aidldemo;

parcelable TestBean;
然後在我們最初的AIDL文件裏引包,並加入方法
// MyTestAIDL.aidl
package com.example.chenfengyao.aidldemo;

// Declare any non-default types here with import statements
import com.example.chenfengyao.aidldemo.TestBean;
interface MyTestAIDL {
    void controlService(int flag);
    void changeData(in TestBean testBean);
}
之後在app的build.gradle加上以下代碼,把我們的AIDL文件夾也變成資源文件夾
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']
        }
    }
編譯,回到我們的Service裏修改一下,實現我們新加入的方法
public class TestService extends Service {

    MyTestAIDL.Stub stub = new MyTestAIDL.Stub() {
        @Override
        public void controlService(int flag) throws RemoteException {
            Log.i("TestService","flag:"+flag);
        }

        @Override
        public void changeData(TestBean testBean) throws RemoteException {
            Log.i("TestService","testBean:"+testBean.testString);
        }
    };

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return stub;
    }
}
最後再在Activity裏去測試一下
 case R.id.btn_change://傳遞數據
                TestBean testBean= new TestBean();
                testBean.testString = "Test";
                try {
                    myTestAIDL.changeData(testBean);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
                break;
至此利用AIDL傳值就實現啦。

發佈了29 篇原創文章 · 獲贊 201 · 訪問量 12萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章