Android 之 Service(二)AIDL

一、介紹

AIDL(Android Interface Definition Language)是一種接口定義語(yǔ)言。
它允許你定義一個(gè)編程接口,用于約束兩個(gè)進(jìn)程間的通訊規(guī)則,提供給編譯器生成代碼,實(shí)現(xiàn)Android設(shè)備上的兩個(gè)進(jìn)程間通信(IPC)。
在Android系統(tǒng)中,一個(gè)進(jìn)程通常不能訪問(wèn)另一個(gè)進(jìn)程的內(nèi)存。所以他們需要將他們的對(duì)象拆解成操作系統(tǒng)所能識(shí)別的原語(yǔ)數(shù)據(jù)(primitives),然后傳入到另一個(gè)進(jìn)程之后再替你組裝成對(duì)象。
也就是說(shuō)進(jìn)程之間的通信信息,首先會(huì)被轉(zhuǎn)換成AIDL協(xié)議消息,然后發(fā)送給對(duì)方,對(duì)方收到AIDL協(xié)議消息后再轉(zhuǎn)換成相應(yīng)的對(duì)象。
由于進(jìn)程之間的通信信息需要雙向轉(zhuǎn)換,所以Android采用代理類(lèi)在背后實(shí)現(xiàn)了信息的雙向轉(zhuǎn)換,代理類(lèi)由Android編譯器生成。

二、適用場(chǎng)景

一般適用于為其它應(yīng)用程序提供公共服務(wù)的Service,這種Service即為系統(tǒng)常駐的Service(如:天氣服務(wù)等)。

三、優(yōu)缺點(diǎn)

優(yōu)點(diǎn)
1.AIDL有自己的獨(dú)立進(jìn)程,不會(huì)受到其它進(jìn)程的影響;
2.可以被其它進(jìn)程復(fù)用,提供公共服務(wù);
3.具有很高的靈活性。
缺點(diǎn)
相對(duì)普通服務(wù),占用系統(tǒng)資源較多,使用AIDL進(jìn)行IPC也相對(duì)麻煩。

四、具體操作(此處我們創(chuàng)建一個(gè)服務(wù)端程序,一個(gè)客戶端程序)

1.生成AIDL文件(服務(wù)端創(chuàng)建)
選擇 new->aidl->aidl file


Studio生成AIDL文件

填寫(xiě)創(chuàng)建的名稱(chēng)


創(chuàng)建的 aidl的名稱(chēng).png

會(huì)在main目錄下為你創(chuàng)建一個(gè)aidl文件夾,并在里面與你的包名對(duì)應(yīng)創(chuàng)建了你的aidl文件,此文件默認(rèn)會(huì)給你創(chuàng)建一個(gè)方法,該方法只是作為類(lèi)型案例解釋?zhuān)梢圆恍枰?br>
創(chuàng)建的aidl文件

Gradle構(gòu)建

點(diǎn)擊Gradle圖標(biāo)讓編譯器為我們生成對(duì)應(yīng)AIDL的 類(lèi) 文件

2.創(chuàng)建Service提供服務(wù),AIDL涉及到IPC通信,所以需要使用綁定服務(wù)。

public class MyService extends Service{

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

    IMyAidlInterface.Stub aidl = new IMyAidlInterface.Stub(){

        public static final String TAG = "AIDL";

        //此處重寫(xiě)所有的抽象方法,實(shí)現(xiàn)提供對(duì)外暴露的接口
        @Override
        public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
            Log.i(TAG,"服務(wù)正在執(zhí)行...");
        }
    };
}

3.注冊(cè)服務(wù)

  <application android:allowBackup="true"
        android:label="@string/app_name"
        android:icon="@mipmap/ic_launcher"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">

         <!--注冊(cè)服務(wù)-->
        <service android:name=".MyService"
                 android:process="com.my.remote.service" >
            <intent-filter>
                <action android:name="com.myaidl.server"/>
            </intent-filter>
        </service>

    </application>

如果客戶端與服務(wù)端在同個(gè)App中,AndroidManifest.xml中設(shè)置Remote Service的andorid:process屬性時(shí),如果被設(shè)置的進(jìn)程名是以一個(gè)冒號(hào)(:)開(kāi)頭的,則這個(gè)新的進(jìn)程對(duì)于這個(gè)應(yīng)用來(lái)說(shuō)是私有的,當(dāng)它被需要或者這個(gè)服務(wù)需要在新進(jìn)程中運(yùn)行的時(shí)候,這個(gè)新進(jìn)程將會(huì)被創(chuàng)建。如果這個(gè)進(jìn)程的名字是以小寫(xiě)字符開(kāi)頭的,則這個(gè)服務(wù)將運(yùn)行在一個(gè)以這個(gè)名字命名的全局的進(jìn)程中,當(dāng)然前提是它有相應(yīng)的權(quán)限。這將允許在不同應(yīng)用中的各種組件可以共享一個(gè)進(jìn)程,從而減少資源的占用。

4.拷貝服務(wù)端aidl文件到客戶端目錄下(文件夾路徑不可變化)
點(diǎn)擊Gradle圖標(biāo)讓編譯器為我們生成對(duì)應(yīng)AIDL的 類(lèi) 文件

拷貝AIDL文件

5.客戶端連接代碼

public class MainActivity extends AppCompatActivity {

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

        Intent intent = new Intent();
        //服務(wù)注冊(cè)的行為
        intent.setAction("com.myaidl.server");
        //5.0之后 需要設(shè)置服務(wù)所在的包名(服務(wù)APP的服務(wù)所在包名)
        //否則會(huì)報(bào) IllegalArgumentException: Service Intent must be explicit
        intent.setPackage("nightingale.aidl_server");
        //綁定服務(wù)
        bindService(intent,conn,BIND_AUTO_CREATE);
    }

    ServiceConnection conn = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            //通過(guò)我們自己定義的AIDL文件的Stub.asInterface方法 傳入服務(wù)傳遞過(guò)來(lái)的iBinder對(duì)象 返回我們的AIDL對(duì)象
            IMyAidlInterface aidl = IMyAidlInterface.Stub.asInterface(iBinder);

            try {
                //此處方法為你在AIDL文件中,自己定義的方法 此處對(duì)應(yīng)我們的AIDL文件
                aidl.basicTypes(0,0,true,0,0,null);
            } catch (RemoteException e) {
                e.printStackTrace();
            }

        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {

        }
    };

    @Override
    protected void onDestroy() {
        unbindService(conn);
        super.onDestroy();
    }
}

如果無(wú)法調(diào)用AIDL對(duì)象(自定義的IMyAidlInterface)那么需要使用Gradle 構(gòu)建一下,或者檢查你是否拷貝了服務(wù)端的aidl文件到客戶端

6.安裝服務(wù)端,安裝客戶端執(zhí)行結(jié)果

執(zhí)行結(jié)果.png

五、AIDL參數(shù)的傳遞(基礎(chǔ)數(shù)據(jù)類(lèi)型 與 自定類(lèi)型)

AIDL默認(rèn)支持下面幾種數(shù)據(jù)類(lèi)型:
Java編程語(yǔ)言中的基礎(chǔ)數(shù)據(jù)類(lèi)型(int,long,char,boolean等)
String,CharSequence,List,Map,在List 中的所有元素必須是上面支持的類(lèi)型或者是其他由AIDL生成的接口,或者你申明的實(shí)現(xiàn)了Parcelable接口的類(lèi)型。
List可能被選用為泛型類(lèi),比如 List<String>.實(shí)際在接受服務(wù)一側(cè)生成的類(lèi)為永遠(yuǎn)是 ArrayList。盡管生成的方法使用的是 List接口。
Map中的雖有元素必須是上面類(lèi)型或者是其他由AIDL生成的接口,或者你申明的實(shí)現(xiàn)了Parcelable接口的類(lèi)型。泛型例如 Map<String,Integer>不支持。實(shí)際在接受服務(wù)一側(cè)生成的類(lèi)為永遠(yuǎn)是HashMap。盡管生成的方法使用的是 Map接口。
你必須要為每一個(gè)上面未列出類(lèi)型添加import申明,盡管他們是作為接口定義在同一個(gè)包里面。

示例:
1.寫(xiě)一個(gè)類(lèi) 實(shí)現(xiàn) Parcelable

public class User implements Parcelable {
    private String name;
    private int age;

    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 User(){

    }
    protected User(Parcel in) {
        name = in.readString();
        age = in.readInt();
    }
    public static final Creator<User> CREATOR = new Creator<User>() {
        @Override
        public User createFromParcel(Parcel in) {
            return new User(in);
        }

        @Override
        public User[] newArray(int size) {
            return new User[size];
        }
    };
    @Override
    public int describeContents() {
        return 0;
    }
    @Override
    public void writeToParcel(Parcel parcel, int i) {
        parcel.writeString(name);
        parcel.writeInt(age);
    }
}

2.在AIDL中定義獲取該類(lèi)的方法

package nightingale.aidl_server;

//***導(dǎo)入對(duì)應(yīng)的類(lèi),必須***
import nightingale.aidl_server.User;

interface IMyAidlInterface {

   //默認(rèn)的 可刪除 可不官 這里案例使用 所以 仍在這里吧
    void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
            double aDouble, String aString);
   //獲取用戶對(duì)象
    User getUser();
}

3.在aidl文件夾中聲明User對(duì)象

聲明User對(duì)象

4.拷貝aidl中的文件到客戶端

拷貝aidl中的文件到客戶端

5.拷貝服務(wù)端的User類(lèi)到客戶端
注意:此處拷貝的時(shí)候,拷貝過(guò)去的類(lèi)所在包名需要跟服務(wù)器所在包名一致,此處我創(chuàng)建了一個(gè) aidl_server包


拷貝服務(wù)端的User類(lèi)到客戶端

6.服務(wù)端Service代碼

public class MyService extends Service{

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

    IMyAidlInterface.Stub aidl = new IMyAidlInterface.Stub(){

        public static final String TAG = "AIDL";

        //此處重寫(xiě)所有的抽象方法,實(shí)現(xiàn)提供對(duì)外暴露的接口
        @Override
        public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
            Log.i(TAG,"服務(wù)正在執(zhí)行...");
        }

        @Override
        public User getUser() throws RemoteException {
            return null;
        }
    };
}

7.客戶端使用代碼

public class MainActivity extends AppCompatActivity {

    private String TAG="aidl";

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

        Intent intent = new Intent();
        //服務(wù)注冊(cè)的行為
        intent.setAction("com.myaidl.server");
        //5.0之后 需要設(shè)置服務(wù)所在的包名(服務(wù)APP的服務(wù)所在包名)
        //否則會(huì)報(bào) IllegalArgumentException: Service Intent must be explicit
        intent.setPackage("nightingale.aidl_server");
        //綁定服務(wù)
        bindService(intent,conn,BIND_AUTO_CREATE);
    }


    ServiceConnection conn = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            //通過(guò)我們自己定義的AIDL文件的Stub.asInterface方法 傳入服務(wù)傳遞過(guò)來(lái)的iBinder對(duì)象 返回我們的AIDL對(duì)象
            IMyAidlInterface aidl = IMyAidlInterface.Stub.asInterface(iBinder);

            try {

                User user = aidl.getUser();
                String name = user.getName();
                if(!TextUtils.isEmpty(name)){
                    Log.i(TAG,name);
                }

            } catch (RemoteException e) {
                e.printStackTrace();
            }

        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {

        }
    };

    @Override
    protected void onDestroy() {
        unbindService(conn);
        super.onDestroy();
    }
}

執(zhí)行結(jié)果


執(zhí)行結(jié)果

至此 AIDL的相關(guān)操作就介紹完了??,不好請(qǐng)吐槽!謝謝

關(guān)于Messenger的介紹,推薦閱讀:陳育 Android IPC機(jī)制(五):詳解Bundle與“信使”——Messenger:http://www.itdecent.cn/p/6e23037d6d20

本文代碼下載:http://download.csdn.net/detail/kooeasy/9610705

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,578評(píng)論 19 139
  • Android跨進(jìn)程通信IPC整體內(nèi)容如下 1、Android跨進(jìn)程通信IPC之1——Linux基礎(chǔ)2、Andro...
    隔壁老李頭閱讀 10,971評(píng)論 13 43
  • Jianwei's blog 首頁(yè) 分類(lèi) 關(guān)于 歸檔 標(biāo)簽 巧用Android多進(jìn)程,微信,微博等主流App都在用...
    justCode_閱讀 6,120評(píng)論 1 23
  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,094評(píng)論 25 709
  • 人生,總是在伴隨著一個(gè)個(gè)得到與失去。你固然得到了這一個(gè),卻失去了另一個(gè)。你沉浸在得到的驚喜當(dāng)中,卻完全沒(méi)有意...
    詩(shī)人阿秀閱讀 270評(píng)論 0 0

友情鏈接更多精彩內(nèi)容