Android 之 Service(二)AIDL

一辅愿、介紹

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

二忘衍、適用場景

一般適用于為其它應(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文件

填寫創(chuàng)建的名稱


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

會(huì)在main目錄下為你創(chuàng)建一個(gè)aidl文件夾枢纠,并在里面與你的包名對(duì)應(yīng)創(chuàng)建了你的aidl文件像街,此文件默認(rèn)會(huì)給你創(chuàng)建一個(gè)方法,該方法只是作為類型案例解釋晋渺,可以不需要
創(chuàng)建的aidl文件

Gradle構(gòu)建

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

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";

        //此處重寫所有的抽象方法,實(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)(:)開頭的,則這個(gè)新的進(jìn)程對(duì)于這個(gè)應(yīng)用來說是私有的既忆,當(dāng)它被需要或者這個(gè)服務(wù)需要在新進(jìn)程中運(yùn)行的時(shí)候,這個(gè)新進(jìn)程將會(huì)被創(chuàng)建。如果這個(gè)進(jìn)程的名字是以小寫字符開頭的,則這個(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的 文件

拷貝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) {
            //通過我們自己定義的AIDL文件的Stub.asInterface方法 傳入服務(wù)傳遞過來的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();
    }
}

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

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

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

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

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

示例:
1.寫一個(gè)類 實(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中定義獲取該類的方法

package nightingale.aidl_server;

//***導(dǎo)入對(duì)應(yīng)的類,必須***
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類到客戶端
注意:此處拷貝的時(shí)候,拷貝過去的類所在包名需要跟服務(wù)器所在包名一致疗疟,此處我創(chuàng)建了一個(gè) aidl_server包


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

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";

        //此處重寫所有的抽象方法,實(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) {
            //通過我們自己定義的AIDL文件的Stub.asInterface方法 傳入服務(wù)傳遞過來的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.reibang.com/p/6e23037d6d20

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末栓袖,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子店诗,更是在濱河造成了極大的恐慌叽赊,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,402評(píng)論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件必搞,死亡現(xiàn)場離奇詭異,居然都是意外死亡囊咏,警方通過查閱死者的電腦和手機(jī)恕洲,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門塔橡,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人霜第,你說我怎么就攤上這事葛家。” “怎么了泌类?”我有些...
    開封第一講書人閱讀 162,483評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵癞谒,是天一觀的道長。 經(jīng)常有香客問我刃榨,道長弹砚,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,165評(píng)論 1 292
  • 正文 為了忘掉前任枢希,我火速辦了婚禮桌吃,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘苞轿。我一直安慰自己茅诱,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,176評(píng)論 6 388
  • 文/花漫 我一把揭開白布搬卒。 她就那樣靜靜地躺著瑟俭,像睡著了一般。 火紅的嫁衣襯著肌膚如雪契邀。 梳的紋絲不亂的頭發(fā)上摆寄,一...
    開封第一講書人閱讀 51,146評(píng)論 1 297
  • 那天,我揣著相機(jī)與錄音蹂安,去河邊找鬼椭迎。 笑死,一個(gè)胖子當(dāng)著我的面吹牛田盈,可吹牛的內(nèi)容都是我干的畜号。 我是一名探鬼主播,決...
    沈念sama閱讀 40,032評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼允瞧,長吁一口氣:“原來是場噩夢啊……” “哼简软!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起述暂,我...
    開封第一講書人閱讀 38,896評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤痹升,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后畦韭,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體疼蛾,經(jīng)...
    沈念sama閱讀 45,311評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,536評(píng)論 2 332
  • 正文 我和宋清朗相戀三年艺配,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了察郁。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片衍慎。...
    茶點(diǎn)故事閱讀 39,696評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖皮钠,靈堂內(nèi)的尸體忽然破棺而出稳捆,到底是詐尸還是另有隱情,我是刑警寧澤麦轰,帶...
    沈念sama閱讀 35,413評(píng)論 5 343
  • 正文 年R本政府宣布乔夯,位于F島的核電站,受9級(jí)特大地震影響款侵,放射性物質(zhì)發(fā)生泄漏末荐。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,008評(píng)論 3 325
  • 文/蒙蒙 一喳坠、第九天 我趴在偏房一處隱蔽的房頂上張望鞠评。 院中可真熱鬧,春花似錦壕鹉、人聲如沸剃幌。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽负乡。三九已至,卻和暖如春脊凰,著一層夾襖步出監(jiān)牢的瞬間抖棘,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評(píng)論 1 269
  • 我被黑心中介騙來泰國打工狸涌, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留切省,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,698評(píng)論 2 368
  • 正文 我出身青樓朝捆,卻偏偏與公主長得像,于是被迫代替她去往敵國和親懒豹。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,592評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容

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