搞清楚AIDL

AIDL是 Android Interface definition language的縮寫,一看就明白,它是一種Android內部進程通信接口的描述語言,通過它我們可以定義進程間的通信接口。
client app可通過aidl調用直接喚醒server app,類似于intent啟動另一個app的activity,相比而言,在5.0以后,broadcast已經無法喚醒另一個未啟動過的app。

AIDL的使用

最終效果
client調用aidl接口向server打招呼,server回調client的callback實現回應招呼。

客戶端向服務端打招呼

服務端回調,回應客戶端打招呼

一般是這樣用的,client遠程調用server端提供的service接口執行任務,server端回調,異步通知client端任務執行的狀態。
客戶端:

MainActivity

public class MainActivity extends Activity {

    private Button bindBtn;
    private Button greetBtn;
    private Button unbindBtn;
    private int count = 0;

    private IGreetBinder iGreetBinder;
    private Handler handler = new Handler();


    private IGreetCallback mCallback = new IGreetCallback.Stub() {

        @Override
        public void greetBack(final Person someone) throws RemoteException {
            handler.post(new Runnable() {
                @Override
                public void run() {
                    Toast.makeText(MainActivity.this,"greet back"+someone,Toast.LENGTH_LONG).show();
                }
            });
        }
    };

    private ServiceConnection conn = new ServiceConnection() {

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            Log.i("ServiceConnection", "onServiceConnected() called");
            iGreetBinder = IGreetBinder.Stub.asInterface(service);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            //This is called when the connection with the service has been unexpectedly disconnected,
            //that is, its process crashed. Because it is running in our same process, we should never see this happen.
            Log.i("ServiceConnection", "onServiceDisconnected() called");
        }
    };

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

        bindBtn = (Button) findViewById(R.id.bindBtn);
        bindBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent("android.intent.action.AIDLService");
                bindService(intent, conn, Context.BIND_AUTO_CREATE);

                bindBtn.setEnabled(false);
                greetBtn.setEnabled(true);
                unbindBtn.setEnabled(true);
            }
        });

        greetBtn = (Button) findViewById(R.id.greetBtn);
        greetBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                try {
                    count++;
                    String retVal = iGreetBinder.greet(new Person(count,"person"+count),mCallback);
                    Toast.makeText(MainActivity.this, retVal, Toast.LENGTH_SHORT).show();
                } catch (RemoteException e) {
                    Toast.makeText(MainActivity.this, "error", Toast.LENGTH_SHORT).show();
                }
            }
        });

        unbindBtn = (Button) findViewById(R.id.unbindBtn);
        unbindBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                unbindService(conn);

                bindBtn.setEnabled(true);
                greetBtn.setEnabled(false);
                unbindBtn.setEnabled(false);
            }
        });
    }
}

build.gradle

    sourceSets {
        main {
            java.srcDirs = ['src/main/java', 'src/main/aidl']
        }
    }

服務端:


AIDLService

public class AIDLService extends Service {

    private static final String TAG = "AIDLService";
    IGreetBinder.Stub stub = new IGreetBinder.Stub() {
        @Override
        public String greet(final Person someone, final IGreetCallback iGreetCallback) throws RemoteException {
            Log.i(TAG, "greet() called");
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        Thread.sleep(100);
                        iGreetCallback.greetBack(someone);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }).start();
            return "hello, " + someone;
        }
    };
    @Override
    public IBinder onBind(Intent intent) {
        Log.i(TAG, "onBind() called");
        return stub;
    }

    @Override
    public boolean onUnbind(Intent intent) {
        Log.i(TAG, "onUnbind() called");
        return true;
    }

    @Override
    public void onStart(Intent intent, int startId) {
        super.onStart(intent, startId);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.i(TAG, "onDestroy() called");
    }
}

AndroidManifest.xml

<service android:name=".AIDLService">
    <intent-filter>
        <action android:name="android.intent.action.AIDLService" />
        // 需定義action,提供給客戶端調起;
        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
</service>

build.gradle

    sourceSets {
        main {
            java.srcDirs = ['src/main/java', 'src/main/aidl']
        }
    }

AIDL部分
服務端和客戶端共用aidl部分。
Person.java

public class Person implements Parcelable {
    public int age;
    public String name;

    public Person(int age, String name) {
        this.age = age;
        this.name = name;
    }

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

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

    public void readFromParcel(Parcel in){
        name = in.readString();
        age = in.readInt();
    }

    protected Person(Parcel in) {
        age = in.readInt();
        name = in.readString();
    }

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

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

    @Override
    public String toString() {
        return age + ":" + name;
    }
}

傳遞Person需要實現Parcelable接口。Aidl支持基本數據類型(int、long、char 等)String 和 CharSequence,List:只支持ArrayList,里面的每個元素都必須被AIDL支持。,Map: 只支持HashMap, 里面的每個元素都必須被AIDL支持。Parcelable: 所有實現了Parcelable接口的對象。
Parcelable 接口
Parcelable 可以是 android 系統將 objectes 分解成可以被進程處理的原始種類必須提供一個名為CREATOR的static final屬性 該屬性需要實現android.os.Parcelable.Creator接口,writeToParcel 注意寫入變量和讀取變量的順序應該一致,不然得不到正確的結果,readFromParcel 注意讀取變量和寫入變量的順序應該一致,不然得不到正確的結果。
Person.aidl

package com.test.aidldemo;
parcelable Person;

IGreetBinder.aidl

package com.test.aidldemo;
import com.test.aidldemo.IGreetCallback;
import com.test.aidldemo.Person;
interface IGreetBinder {
       String greet(inout Person person,IGreetCallback cb);
}

添加服務端對客戶端的回調。
IGreetCallback.aidl

package com.test.aidldemo;
import com.test.aidldemo.Person;

interface IGreetCallback {
    void greetBack(inout Person person);
}

使用Person類型參數時,需要提供方向指示符,其包括in、out、和inout。in表示由客戶端設置,out表示由服務端設置,inout表示客戶端和服務端都設置了該值。如不設置方向指示符,將報錯,基本類型int、String等無需設置方向指示符。

源碼

client端:https://github.com/woshizmxin/AidlClientDemo
server端:https://github.com/woshizmxin/AidlServerDemo

在該demo運行在5.0以上設備上,會報錯
** IllegalArgumentException: Service Intent must be explicit**
解決辦法參考: Service Intent must be explicit的解決方法

使用Messager

如果想做app進程間通信,aidl寫起來還是比較麻煩的,meassager則相當于是一個簡化版本。它基于Message,相信大家都很熟悉,不需要編寫aidl文件。
AIDL和Messager區別:

  1. Messenger本質也是AIDL,只是進行了封裝,開發的時候不用再寫.aidl文件。
    結合我自身的使用,因為不用去寫.aidl文件,相比起來,Messenger使用起來十分簡單。但前面也說了,Messenger本質上也是AIDL,故在底層進程間通信這一塊,兩者的效率應該是一樣的。
  2. 在service端,Messenger處理client端的請求是單線程的,而AIDL是多線程的。使用AIDL的時候,service端每收到一個client端的請求時,就會啟動一個線程(非主線程)去執行相應的操作。而Messenger,service收到的請求是放在Handler的MessageQueue里面,Handler大家都用過,它需要綁定一個Thread,然后不斷poll message執行相關操作,這個過程是同步執行的。
    當然是有辦法解決的,在server端,Messenger的Handler可以只當作一個轉發器,不處理請求,只轉發請求到相應的處理線程(多是相應的HandlerThread),這樣也可以達到異步的效果。
  3. client的方法,使用AIDL獲取返回值是同步的,而Messenger是異步的。Messenger只提供了一個方法進行進程間通信,就是send(Message msg)方法,發送的是一個Message,沒有返回值,要拿到返回值,需要把client的Messenger作為msg.replyTo參數傳遞過去,service端處理完之后,在調用客戶端的Messenger的send(Message msg)方法把返回值傳遞回client,這個過程是異步的,而AIDL你可以自己指定方法,指定返回值,它獲取返回值是同步的。

具體使用請移步:
Android 基于Message的進程間通信 Messenger完全解析

參考文章

Android中使用AIDL時的跨進程回調—Server回調Client
Service Intent must be explicit的解決方法
Android 基于Message的進程間通信 Messenger完全解析

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,237評論 6 537
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,957評論 3 423
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,248評論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,356評論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,081評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,485評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,534評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,720評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,263評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,025評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,204評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,787評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,461評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,874評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,105評論 1 289
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,945評論 3 395
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,205評論 2 375

推薦閱讀更多精彩內容