安卓aidl詳解

1,爲什麼要有AIDL?
無論學什麼東西,最先弄明白爲什麼要有這個東西,不要說存在即合理,存在是肯定的,但是你還是沒有明白對於AIDL有一些人的淺顯概念就是:AIDL可以跨進程訪問其它應用程序和其它應用程序通訊,那我告訴你很多技術都可以訪問如廣播(應用A在AndroidManifest.xml中註冊指定Action的廣播)應用B發送指定Action的廣播,A就能收到消息,這樣也能看成不同應用之間完成了通訊(但是這種通訊是單向的);還有ContentProvider通過UI接口暴露數據給其他應用訪問,但是這種算不上是應用之間的通訊那麼爲什麼還要有AIDL呢,官方文檔介紹AIDL中有這麼一句話:

Using AIDL is necessary only if you allow clients from different
applications to access your service for IPC and want to handle
multithreading in your service. If you do not need to perform
concurrent IPC across different applications, you should create your
interface by implementing a Binder or, if you want to perform IPC, but
do not need to handle multithreading, implement your interface using a
Messenger. Regardless, be sure that you understand Bound Services
before implementing an AIDL.

第一句最重要,“只有當你允許來自不同的客戶端訪問你的服務並且需要處理多線程、多客戶端併發訪問的時候你必須使用AIDL”,其他情況下你都可以使用其他的方法,如使用廣播,ContentProvide也能跨進程通訊。課件AIDL是處理多線程,多客戶端併發訪問的。

2,AIDL的使用如下:

  1. 創建一個“類名.aidl”的文件(該文件定義了客戶端可用的方法和數據的接口)。
  2. 創建一個繼承“類名.Stub”的類並且實現在“類名.aidl”中聲明的方法。
  3. 新建另外一個客戶端工程,同樣需要添加AIDL協議文件(這是一個標準的協議文件,定義對外服務的接口)。
  4. 向客戶端公開接口(如果是編寫服務),應該繼承service並且重載service.onBind(Intent)以返回實現了接口的對象實例。

    AIDL服務只支持有限的數據類型,因此,如果用AIDL服務傳遞一些複雜的數據就需要做更一步處理。AIDL服務支持的數據類型如下:
    Java的簡單類型(int、char、boolean等)。不需要導入(import)。
    String和CharSequence。不需要導入(import)。
    List和Map。但要注意,List和Map對象的元素類型必須是AIDL服務支持的數據類型。不需要導入(import)。

AIDL常使用的數據類型:

  1. Java的簡單類型(int、char、boolean等)。不需要導入(import)。
  2. String和CharSequence。不需要導入(import)。
  3. List和Map。但要注意,List和Map對象的元素類型必須是AIDL服務支持的數據類型。不需要導(import)。
  4. AIDL自動生成的接口。需要導入(import)。
  5. 實現android.os.Parcelable接口的類。需要導入(import)。(例如,實現android.os.Parcelable接口的類)的步驟略顯複雜。除了要建立一個實現android.os.Parcelable接口的類外,還需要爲這個類單獨建立一個aidl文件,並使用parcelable關鍵字進行定義。

3,讓我們看一下代碼方便大家的理解:
(1)創建aidl的服務端定義IRemoteService.aidl 文件裏面聲明對外訪問的接口,返回Student類的對象。

package com.example.aidl;
import com.example.aidl.Student;
interface IRemoteService{
     Student getStudent();
}

(2)Student類必須實現android.os.Parcelable的接口

package com.example.aidl;

import android.os.Parcel;
import android.os.Parcelable;

public class Student implements Parcelable {
    private int age;
    private String name;
    private String address;
    private String phone;

    public Student() {
    }

    public int getAge() {
        return age;
    }

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

    public String getName() {
        return name;
    }

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

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public String getPhone() {
        return phone;
    }

    public void setPhone(String phone) {
        this.phone = phone;
    }

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

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

    public static final Creator<Student> CREATOR = new Parcelable.Creator<Student>() {
        public Student createFromParcel(Parcel in) {
            Student student = new Student();
            student.age = in.readInt();
            student.name = in.readString();
            student.address = in.readString();
            student.phone = in.readString();
            return student;
        }

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

    @Override
    public String toString() {
        return "Student [age=" + age + ", name=" + name + ", address=" + address + ", phone=" + phone + "]";
    }
}

注意:在實體類我們要的事情

  1. 實現Parcelable接口
  2. 實現Creator和writeToParcel這兩個方法,分別代表用於對象讀取數據和寫入數據。
  3. 同時提供Student.aidl文件,下面則是Student.aidl文件
package com.example.aidl;
import com.example.aidl.Student;
parcelable Student;

下面我們定義一個service我們實現在IRemoteService.aidl聲明的接口方法

package com.example.aidl;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;

public class DDService extends Service {

    private static final String TAG = "DDService";

    @Override
    public void onCreate() {
        Log.e(TAG, "--- onCreate()-----");
    }

    @Override
    public IBinder onBind(Intent intent) {
        Log.e(TAG, "--- onBind()");
        return bunder;
    }

    IRemoteService.Stub bunder = new IRemoteService.Stub() {

        @Override
        public Student getStudent() throws RemoteException {
            Student student = new Student();
            student.setAge(20);
            student.setAddress("山東省");
            student.setName("張三");
            student.setPhone("12321");
            return student;
        }
    };

}

記得service在AndroidManifest.xml文件中需要註冊

<service android:name="com.example.aidl.DDService" >
            <intent-filter>
                <action android:name="com.example.aidl.DDService" />
                <category android:name="android.intent.category.DEFAULT"/>
            </intent-filter>
        </service>

我們最後在客戶端進行訪問服務端定義的aidl的文件

  1. 首先把我們在服務端定義的aidl文件同時拷貝到我們的客戶端我們這裏有Student.java,IRemoteService.aidl,Student.aidl 這三個文件此外記得這三個文件所在工程的包名要與服務端定義aidl文件的包名要保持一致,否則會報錯

  2. bindService綁定服務,同時實現ServiceConnection類複寫onServiceConnected(ComponentName name, IBinder service)方法調用IRemoteService.Stub.asInterface(service)方法返回實現aidl文件定義接口的實現類對象,用實現類對象調用接口中的方法。

package com.example.aidl2;

import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;

import com.example.aidl.IRemoteService;
import com.example.aidl.Student;

public class MainActivity extends Activity implements OnClickListener {
    private String TAG = "MainActivity";
    IRemoteService remoteService;
    private Button button;
    private ServiceConnection conn;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        button = (Button) findViewById(R.id.button);
        button.setOnClickListener(this);
        conn = new ServiceConnection() {

            @Override
            public void onServiceDisconnected(ComponentName name) {
                unbindService(conn);
            }

            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                try {
                    remoteService = IRemoteService.Stub.asInterface(service);
                    Student student = remoteService.getStudent();
                    Log.e(TAG, student.toString());
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        };
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
        case R.id.button:
            Intent intent = new Intent();
            intent.addCategory(Intent.CATEGORY_DEFAULT);
            intent.setAction("com.example.aidl.DDService");
            bindService(intent, conn, Context.BIND_AUTO_CREATE);
            break;

        default:
            break;
        }
    }

}

同時運行客戶端和服務端:
這裏寫圖片描述
點擊“開啓服務”button 在客戶端打印log如下:
這裏寫圖片描述

最後祝大家學習愉快。。

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