Android日記之AIDL的基本使用

前言

AIDL其實是屬于IPC方面的東西,而Android的IPC是一個比較大的內(nèi)容秉沼,主要涉及到Android的跨進程通信的內(nèi)容桶雀,此篇文章會先介紹一下Android的IPC。

Android IPC簡介

IPC是Inter-Process Communication的縮寫唬复,意思就是進程間通信或者跨進程的通信矗积,就是兩個進程之間交換數(shù)據(jù)的過程。說到進程敞咧,就不得不說到進程和線程這兩個東西棘捣,這兩個是完全不一樣的概念,簡單描述就是休建,一個進程可以包含多個線程乍恐,也就是一對多的關(guān)系。進程一般指一個執(zhí)行單元测砂,線程是CPU調(diào)度的最小單元茵烈。在Android里面,進程里可以只有一個線程砌些,即主線程呜投,在Android里面也可以叫作UI線程,一般在這個線程里面都是進行UI更新的操作存璃,而耗時的操作都是交給子線程去進行仑荐,這樣可以防止Android的ANR狀態(tài)。

Android的多進程模式

再講IPC之前我們先來講講Android的多進程模式纵东,在Android中多進程是值一個應用中存在多個進程的情況粘招,所以這里暫時不討論兩個應用之間的情況。在Android使用多進程的只有一種方法篮迎,那就是給四大組件在AndroidMenifest中指定android:process屬性男图。參數(shù)為你要自定義的包名示姿,如果沒有指定進程的話甜橱,那么就會運行在默認的進程中逊笆。

<activity android:name=".MainActivity"
    android:process=":remote">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />

        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>

但是,看起來使用多進程模式很簡單岂傲,但其實是會造成一些問題的难裆,比如:

  • 靜態(tài)成員和單例模式完全失效。
  • 線程同步機制完全失效镊掖。
  • SharedPreferences的可靠性下降乃戈。
  • Application會多次創(chuàng)建。

雖然多進程會帶來很多問題亩进,但是不能因為就這樣而不去正視它們症虑,為了解決這個問題,系統(tǒng)提供了很多跨進程通信的方法归薛,雖說不能直接的共享內(nèi)存谍憔,但是通過跨進程通信我們還是可以實現(xiàn)數(shù)據(jù)的交互,實現(xiàn)跨進程的通信方式有很多種方法主籍,我們后續(xù)會進行講解习贫。

AIDL的使用

剛剛也說過了,跨進程的通信方式有很多千元,比如通過BInder來進行跨進程通信苫昌,還有ContentProvider,它天生就是支持跨進程訪問的幸海,此外也可以使用Bundle祟身,Messager等等方式,還有一種方式就是AIDL物独,AIDL 意思即 Android Interface Definition Language袜硫,翻譯過來就是Android接口定義語言,是用于定義服務器和客戶端通信接口的一種描述語言议纯,可以拿來生成用于IPC的代碼父款。從某種意義上說AIDL其實是一個模板,因為在使用過程中瞻凤,實際起作用的并不是AIDL文件憨攒,而是據(jù)此而生成的一個IInterface的實例代碼。


Android的多進程方式如圖阀参,侵刪

而剛剛說的像Messager之類的底層其實就是AIDL肝集,只是Messager做了一些封裝。還有一個就是Binder蛛壳,它也是Android里跨進程通信里的一種機制杏瞻,而AIDL是對Binder的封裝所刀。因為要實現(xiàn)IBinder來支持遠程調(diào)用,應從Binder類派生一個類捞挥。而使用AIDL浮创,它會自動生成Binder的派生類,可以直接用砌函,那么怎么用AIDL進行跨進程通信呢

我們這里舉一個例子斩披,創(chuàng)建一個App,我們稱之為服務端讹俊,然后先創(chuàng)建一個一個AIDL文件垦沉,然后自定義一個方法,創(chuàng)建后記住要build一下仍劈,build之后AIDL就會快速生成對應的java文件了厕倍。

interface IAppServiceRemoteBinder {
    /**
     * 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);
    
    
    //定義一個set方法
    void setData(String data);
}
AIDL文件創(chuàng)建如圖

這里說明一下,AIDL支持的數(shù)據(jù)類型有以下這些:

  • 基本數(shù)據(jù)類型(int贩疙、long讹弯、char、boolean屋群、double)
  • String和CharSequence
  • List和Map集合
  • 集合內(nèi)元素必須是AIDL支持的數(shù)據(jù)類型
  • 服務端具體使用的集合必須是ArrayList和HashMap
  • Parcelable:實現(xiàn)了Parcelable接口的對象
  • AIDL本身接口也可以在AIDL文件使用闸婴。

接著我們創(chuàng)建一個服務,因為其實AIDL就是基于Binder芍躏,所以我們就直接在onBind()里面進行返回邪乍。

public class AppService extends Service {

    private String data = "默認數(shù)據(jù)";
    private boolean running = false;

    public AppService() {
    }

    @Override
    public IBinder onBind(Intent intent) {


        //我們這里直接返回這個接口
        return new IAppServiceRemoteBinder.Stub() {
            @Override
            public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {

            }

            @Override
            public void setData(String data) throws RemoteException {
                //客戶端傳過來的值賦值給data
                AppService.this.data = data;
            }
        };
    }

    @Override
    public void onCreate() {
        super.onCreate();
        Log.d("TAG", "service started");
        //創(chuàng)建一個測試線程,把客戶端傳過來的數(shù)據(jù)進行循環(huán)輸出对竣。
        new Thread() {
            @Override
            public void run() {
                super.run();
                running = true;
                while (running) {
                    try {
                        Thread.sleep(1000);
                        Log.d("TAG", data);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                }
            }
        }.start();
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.d("TAG", "service onDestroy");
        running = false;
    }
    
}

接著我們創(chuàng)建一個客戶端庇楞,來遠程啟動這個服務,首先我們要把之前創(chuàng)建的那個AIDL文件給復制過來否纬,這里需要注意的是吕晌,包名的路徑是要相同的,然后記住要build临燃,然后設置一下布局睛驳。

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <Button
        android:id="@+id/btn_start_service"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text=" 啟動外部服務" />

    <Button
        android:id="@+id/btn_stop_service"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text=" 停止外部服務" />


    <Button
        android:id="@+id/btn_bind_service"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text=" 綁定外部服務" />

    <Button
        android:id="@+id/btn_unbind_service"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text=" 解除外部服務" />

    <Button
        android:id="@+id/btn_edit_service"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text=" 發(fā)送值給服務端" />

</LinearLayout>

然后在MainActivity里輸入以下代碼。

//要實現(xiàn)ServiceConnection方法
public class MainActivity extends AppCompatActivity implements View.OnClickListener, ServiceConnection {


    private Button btnStart;
    private Button btnStop;
    private Button btnBind;
    private Button btnUnBind;
    private Button btnEdit;
    private Intent serviceIntent;
    private IAppServiceRemoteBinder binder = null;

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

        serviceIntent = new Intent();
        //這里設置intent的包名膜廊,第一個參數(shù)為要啟動app的包名乏沸,第二個為服務包名
        serviceIntent.setComponent(new ComponentName("com.ju.startservicefromanotherapp", "com.ju.startservicefromanotherapp.AppService"));

        btnStart = findViewById(R.id.btn_start_service);
        btnStart.setOnClickListener(this);
        btnStop = findViewById(R.id.btn_stop_service);
        btnStop.setOnClickListener(this);
        btnBind = findViewById(R.id.btn_bind_service);
        btnBind.setOnClickListener(this);
        btnUnBind = findViewById(R.id.btn_unbind_service);
        btnUnBind.setOnClickListener(this);
        btnEdit = findViewById(R.id.btn_edit_service);
        btnEdit.setOnClickListener(this);

    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.btn_start_service:
                //遠程啟動服務
                startService(serviceIntent);
                break;
            case R.id.btn_stop_service:
                //遠程開始服務
                stopService(serviceIntent);
                break;
            case R.id.btn_bind_service:
                //遠程綁定服務
                bindService(serviceIntent, this, Context.BIND_AUTO_CREATE);
                break;
            case R.id.btn_unbind_service:
                //遠程解綁服務
                unbindService(this);
                binder = null;
                break;
            case R.id.btn_edit_service:
                //這里給服務端傳值設置
                if (binder != null) {
                    try {
                        binder.setData("hello");
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                }
                break;
            default:
        }
    }

    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        Log.d("TAG", "Bind Service");
        Log.d("TAG", "IBinder:" + service);
        //這里進行實例化接口,不能強轉(zhuǎn)爪瓜。
        binder = IAppServiceRemoteBinder.Stub.asInterface(service);
    }

    @Override
    public void onServiceDisconnected(ComponentName name) {

    }


}

這里我們通過IAppServiceRemoteBinder.Stub.asInterface()方法來獲取到Service里的Binder,然后就可以直接調(diào)用Binder里的setData()方法了蹬跃。最后把這啟動這個App,點擊綁定外部服務按鈕铆铆,服務就會運行起來了蝶缀,然后點擊發(fā)送給值客戶端的按鈕丹喻,就會遠程發(fā)送一個值給服務端進行接收了。

服務運行起來如圖

發(fā)送服務端給值效果如圖

如果說傳輸?shù)氖且粋€Object對象的話翁都,就需要對Object進行序列化操作碍论,Java的序列化是繼承Serialzable接口,而Android也有一個自己的序列化荐吵,就是繼承Parcelable接口骑冗。

參考

Android中AIDL的理解和使用
[任玉剛] Android開發(fā)藝術(shù)探索

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末赊瞬,一起剝皮案震驚了整個濱河市先煎,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌巧涧,老刑警劉巖薯蝎,帶你破解...
    沈念sama閱讀 211,884評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異谤绳,居然都是意外死亡占锯,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,347評論 3 385
  • 文/潘曉璐 我一進店門缩筛,熙熙樓的掌柜王于貴愁眉苦臉地迎上來消略,“玉大人,你說我怎么就攤上這事瞎抛∫昭荩” “怎么了?”我有些...
    開封第一講書人閱讀 157,435評論 0 348
  • 文/不壞的土叔 我叫張陵桐臊,是天一觀的道長胎撤。 經(jīng)常有香客問我,道長断凶,這世上最難降的妖魔是什么伤提? 我笑而不...
    開封第一講書人閱讀 56,509評論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮认烁,結(jié)果婚禮上肿男,老公的妹妹穿的比我還像新娘。我一直安慰自己却嗡,他們只是感情好舶沛,可當我...
    茶點故事閱讀 65,611評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著稽穆,像睡著了一般冠王。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上舌镶,一...
    開封第一講書人閱讀 49,837評論 1 290
  • 那天柱彻,我揣著相機與錄音豪娜,去河邊找鬼。 笑死哟楷,一個胖子當著我的面吹牛瘤载,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播卖擅,決...
    沈念sama閱讀 38,987評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼鸣奔,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了惩阶?” 一聲冷哼從身側(cè)響起挎狸,我...
    開封第一講書人閱讀 37,730評論 0 267
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎断楷,沒想到半個月后锨匆,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,194評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡冬筒,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,525評論 2 327
  • 正文 我和宋清朗相戀三年恐锣,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片舞痰。...
    茶點故事閱讀 38,664評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡土榴,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出响牛,到底是詐尸還是另有隱情玷禽,我是刑警寧澤,帶...
    沈念sama閱讀 34,334評論 4 330
  • 正文 年R本政府宣布娃善,位于F島的核電站论衍,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏聚磺。R本人自食惡果不足惜坯台,卻給世界環(huán)境...
    茶點故事閱讀 39,944評論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望瘫寝。 院中可真熱鬧蜒蕾,春花似錦、人聲如沸焕阿。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,764評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽暮屡。三九已至撤摸,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背准夷。 一陣腳步聲響...
    開封第一講書人閱讀 31,997評論 1 266
  • 我被黑心中介騙來泰國打工钥飞, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人衫嵌。 一個月前我還...
    沈念sama閱讀 46,389評論 2 360
  • 正文 我出身青樓读宙,卻偏偏與公主長得像,于是被迫代替她去往敵國和親楔绞。 傳聞我的和親對象是個殘疾皇子结闸,可洞房花燭夜當晚...
    茶點故事閱讀 43,554評論 2 349

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