說起Android的進程間通信卸奉,想必大家都會不約而同的想起Android中的Binder機制。而提起B(yǎng)inder颖御,想必也有不少同學會想起初學Android時被Binder和AIDL支配的恐懼感榄棵。但是作為一個Android開發(fā)者,Binder是我們必須掌握的知識潘拱。因為它是構(gòu)架整個Android大廈的鋼筋和混凝土疹鳄,連接了Android各個系統(tǒng)服務(wù)和上層應(yīng)用。只有了解了Binder機制才能更加深入的理解Android開發(fā)和Android Framework芦岂。這也是為什么無論是《Android開發(fā)藝術(shù)探索》還是《深入理解Android內(nèi)核涉及思想》這些進階類書籍把進程間通信和Binder機制放到靠前章節(jié)的原因瘪弓,它太重要了,重要到整個Android Framework都離不開Binder的身影禽最。
本篇文章我們暫且不去探討B(tài)inder的底層實現(xiàn)腺怯,因為就目前而言,筆者掌握的程度也不足以去輸出Binder實現(xiàn)原理的的內(nèi)容川无。因此呛占,為了不誤導(dǎo)大家,這里就來寫一寫B(tài)inder的基礎(chǔ)用法以及AIDL懦趋。雖然關(guān)于Binder和AIDL的基礎(chǔ)用法網(wǎng)上的內(nèi)容比比皆是晾虑。但是能把Binder和AIDL寫的淺顯易懂的文章并不多見。也就導(dǎo)致了很多人覺得Binder跟AIDL的使用都很難。
本篇文章帜篇,我將通過我自己的學習思路來帶大家認識Binder機制和AIDL糙捺。
一、進程間通信
在操作系統(tǒng)中笙隙,每個進程都有一塊獨立的內(nèi)存空間洪灯。為了保證程序的的安全性,操作系統(tǒng)都會有一套嚴格的安全機制來禁止進程間的非法訪問逃沿。畢竟婴渡,如果你的APP能訪問到別的APP的運行空間,或者別的APP可以輕而易舉的訪問到你APP的運行空間凯亮,想象一下你是不是崩潰的心都有了边臼。所以,操作系統(tǒng)層面會對應(yīng)用進程進行內(nèi)存隔離假消,以保證APP的運行安全柠并。但是,很多情況下進程間也是需要相互通信的富拗,例如剪貼板的功能臼予,可以從一個程序中復(fù)制信息到另一個程序。這就是進程間通信誕生的背景啃沪。
廣義的講粘拾,進程間通信(Inter-process communication,簡稱IPC)是指運行在不同進程中的若干線程間的數(shù)據(jù)交換。
操作系統(tǒng)中常見的進程間通信方式有共享內(nèi)存创千、管道缰雇、UDS以及Binder等。關(guān)于這些進程間的通信方式本篇文章我們不做深究追驴,了解即可械哟。
共享內(nèi)存(Shared Memory) 共享內(nèi)存方式實現(xiàn)進程間通信依靠的是申請一塊內(nèi)存區(qū)域,讓后將這塊內(nèi)存映射到進程空間中殿雪,這樣兩個進程都可以直接訪問這塊內(nèi)存暇咆。在進行進程間通信時,兩個進程可以利用這塊內(nèi)存空間進行數(shù)據(jù)交換丙曙。通過這樣的方式爸业,減少了數(shù)據(jù)的賦值操作,因此共享內(nèi)存實現(xiàn)進程間通信在速度上有明顯優(yōu)勢河泳。
管道(Pipe) 管道也是操作系統(tǒng)中常見的一種進程間通信方式沃呢,Windows系統(tǒng)進程間的通信依賴于此中方式實現(xiàn)。在進行進程間通信時拆挥,會在兩個進程間建立一根擁有讀(read)寫(write)功能的管道,一個進程寫數(shù)據(jù)薄霜,另一個進程可以讀取數(shù)據(jù)某抓,從而實現(xiàn)進程間通信問題。
UDS(UNIX Domain Socket) UDS也被稱為IPC Socket惰瓜,但它有別于network 的Socket否副。UDS的內(nèi)部實現(xiàn)不依賴于TCP/IP協(xié)議,而是基于本機的“安全可靠操作”實現(xiàn)崎坊。UDS這種進程間通信方式在Android中用到的也是比較多的备禀。
Binder Binder是Android中獨有的一種進程間通信方式。它底層依靠mmap,只需要一次數(shù)據(jù)拷貝奈揍,把一塊物理內(nèi)存同時映射到內(nèi)核和目標進程的用戶空間曲尸。
本篇文章,我們重點就是了解如何使用Binder實現(xiàn)進程間通信男翰。Binder僅僅從名字來看就給人一種很神秘的感覺另患,就因為這個名字可能就會嚇走不少初學者。但其實Binder本身并沒有很神秘蛾绎,它僅僅是Android系統(tǒng)提供給開發(fā)者的一種進程間通信方式昆箕。
而從上述幾種進程間通信方式來看,無論是哪種進程間通信租冠,都是需要一個進程提供數(shù)據(jù)鹏倘,一個進程獲取數(shù)據(jù)。因此顽爹,我們可以把提供數(shù)據(jù)的一端稱為服務(wù)端纤泵,把獲取數(shù)據(jù)的一端稱為客戶端。從這個角度來看Binder是不是就有點類似于HTTP協(xié)議了镜粤?所以夕吻,你完全可以把Binder當成是一種HTTP協(xié)議,客戶端通過Binder來獲取服務(wù)端的數(shù)據(jù)繁仁。認識到這一點,再看Binder的使用就會簡單多了归园。
二黄虱、使用Binder實現(xiàn)進程間通信
使用Binder完成進程間通信其實非常簡單庸诱。我們舉一個查詢成績的例子捻浦,服務(wù)端提供根據(jù)學生姓名查詢學生成績的接口,客戶端連接服務(wù)端通過學生姓名來查詢成績桥爽,而客戶端與服務(wù)端的媒介就是Binder朱灿。
1.服務(wù)端的實現(xiàn)
服務(wù)端自然是要提供服務(wù)的,因此就需要我們開啟一個Service等待客戶端的連接钠四。關(guān)于Android的Service這里就不用多說了吧盗扒,我們實現(xiàn)一個GradeService并繼承Service跪楞,來提供成績查詢接口。代碼如下:
public class GradeService extends Service {
public static final int REQUEST_CODE=1000;
private final Binder mBinder = new Binder() {
@Override
protected boolean onTransact(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags) throws RemoteException {
if (code == REQUEST_CODE) {
String name = data.readString();
// 根據(jù)姓名查詢學生成績并將成績寫入到返回數(shù)據(jù)
int studentGrade = getStudentGrade(name);
if (reply != null)
reply.writeInt(studentGrade);
return true;
}
return super.onTransact(code, data, reply, flags);
}
// 根據(jù)姓名查詢學生成績
public int getStudentGrade(String name) {
return StudentMap.getStudentGrade(name);
}
};
@Nullable
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
}
因為我們要實現(xiàn)的是跨進程通信侣灶,因此甸祭,我們將這個服務(wù)端的Service設(shè)置到遠程進程中,在AndroidManifest文件中如下:
<service
android:name="com.zhpan.sample.binder.server.GradeService"
android:process=":server">
<intent-filter>
<action android:name="android.intent.action.server.gradeservice" />
</intent-filter>
</service>
就這樣褥影,一個遠程的池户,提供成績查詢的服務(wù)就完成了。
2.客戶端的實現(xiàn)
客戶端自然而然的是要連接服務(wù)端進程成績查詢凡怎。因此校焦,我們在客戶端的Activity中取綁定GradeService進行成績查詢。代碼如下:
public class BinderActivity extends AppCompatActivity {
// 遠程服務(wù)的Binder代理
private IBinder mRemoteBinder;
private final ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
// 獲取遠程服務(wù)的Binder代理
mRemoteBinder = iBinder;
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
mRemoteBinder = null;
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_binder);
// 綁定服務(wù)
findViewById(R.id.btn_bind_service).setOnClickListener(view -> bindGradeService());
// 查詢學生成績
findViewById(R.id.btn_find_grade).setOnClickListener(view -> getStudentGrade("Anna"));
}
// 綁定遠程服務(wù)
private void bindGradeService() {
String action = "android.intent.action.server.gradeservice";
Intent intent = new Intent(action);
intent.setPackage(getPackageName());
bindService(intent, mServiceConnection, BIND_AUTO_CREATE);
}
// 從遠程服務(wù)查詢學生成績
private int getStudentGrade(String name) {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
int grade = 0;
data.writeString(name);
try {
if (mRemoteBinder == null) {
throw new IllegalStateException("Need Bind Remote Server...");
}
mRemoteBinder.transact(REQUEST_CODE, data, reply, 0);
grade = reply.readInt();
} catch (RemoteException e) {
e.printStackTrace();
}
return grade;
}
客戶端的代碼就是通過綁定遠程服務(wù)统倒,然后獲取到服務(wù)的Binder代理寨典,來查詢學生成績¢苁恚可見凝赛,使用Binder實現(xiàn)進程間通信是非常簡單的,可以說簡單的有點出乎所料坛缕。那我們之前寫的AIDL是什么呢墓猎?AIDL生成的那一堆是什么玩意兒?我們且往下看赚楚。
三毙沾、代理模式優(yōu)化Binder使用
雖然上一章中的代碼已經(jīng)非常簡單了,但是還是有可以優(yōu)化的空間宠页。我們可以通過設(shè)計模式來進行優(yōu)化左胞,讓代碼更加簡潔。
首先需要定義一個接口查詢成績的接口IGradeInterface举户,代碼如下:
public interface IGradeInterface {
// 查詢成績接口
int getStudentGrade(String name);
}
1.服務(wù)端代碼優(yōu)化
接著烤宙,對服務(wù)端的代碼進行優(yōu)化。我們實現(xiàn)一個自定義的GradeBinder俭嘁,并實現(xiàn)上述接口躺枕,代碼如下:
public class GradeBinder extends Binder implements IGradeInterface {
@Override
public int getStudentGrade(String name) {
return StudentMap.getStudentGrade(name);
}
@Override
protected boolean onTransact(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags) throws RemoteException {
if (code == REQUEST_CODE) {
String name = data.readString();
int studentGrade = getStudentGrade(name);
if (reply != null)
reply.writeInt(studentGrade);
return true;
}
return super.onTransact(code, data, reply, flags);
}
}
上述代碼將查詢成績的相關(guān)邏輯從Service搬到了GradeBinder中。因此供填,此時Service中只需要在onBind的時候返回GradeBinder的實例即可拐云。代碼如下:
public class GradeService extends Service {
public static final int REQUEST_CODE=1000;
@Nullable
@Override
public IBinder onBind(Intent intent) {
return new GradeBinder();
}
}
2.客戶端代碼優(yōu)化
客戶端優(yōu)化的思路是在連接到遠程服務(wù)時候?qū)嵗粋€代理類,代理類持有Binder近她,讓代理類行使Binder的權(quán)利叉瘩。首先來看代理類的代碼實現(xiàn):
public class BinderProxy implements IGradeInterface {
// 被代理的Binder
private final IBinder mBinder;
// 私有化構(gòu)造方法
private BinderProxy(IBinder binder) {
mBinder = binder;
}
// 通過Binde讀取成績
@Override
public int getStudentGrade(String name) {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
int grade = 0;
data.writeString(name);
try {
if (mBinder == null) {
throw new IllegalStateException("Need Bind Remote Server...");
}
mBinder.transact(REQUEST_CODE, data, reply, 0);
grade = reply.readInt();
} catch (RemoteException e) {
e.printStackTrace();
}
return grade;
}
// 實例化Binder代理類的對象
public static IGradeInterface asInterface(IBinder iBinder) {
if (iBinder == null) {
return null;
}
if (iBinder instanceof IGradeInterface) {
LogUtils.e("當前進程");
// 如果是同一個進程的請求,則直接返回Binder
return (IGradeInterface) iBinder;
} else {
LogUtils.e("遠程進程");
// 如果是跨進程查詢則返回Binder的代理對象
return new BinderProxy(iBinder);
}
}
}
BinderProxy類的構(gòu)造方法被設(shè)置成了private粘捎。同時提供了一個asInterface方法中薇缅,這個方法通過判斷Binder是不是IGradeInterface類型從而確定是不是跨進程的通信危彩。如果不是跨進程通信,則返回當前這個Binder捅暴,否則就返回Binder的這個代理類恬砂。
接下來客戶端連接上遠程服務(wù)的時候使用BinderProxy獲取Binder或者BinderProxy實例。代碼如下:
public class BinderProxyActivity extends BaseViewBindingActivity<ActivityBinderBinding> {
// 此處可能是BinderProxy也可能是GradeBinder
private IGradeInterface mBinderProxy;
private final ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
// 連接服務(wù)成功蓬痒,根據(jù)是否跨進程獲取BinderProxy或者GradeBinder實例
mBinderProxy = BinderProxy.asInterface(iBinder);
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
mBinderProxy = null;
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding.btnBindService.setOnClickListener(view -> bindGradeService());
// 查詢學生成績點擊事件泻骤,通過mBinderProxy查詢成績
binding.btnFindGrade.setOnClickListener(view -> ToastUtils.showShort("Anna grade is " + mBinderProxy.getStudentGrade("Anna")));
}
// 綁定服務(wù)
private void bindGradeService() {
String action = "android.intent.action.server.gradeservice";
Intent intent = new Intent(action);
intent.setPackage(getPackageName());
bindService(intent, mServiceConnection, BIND_AUTO_CREATE);
}
}
可以看到,此時的代碼相比第一章的代碼整潔了不少梧奢。但是狱掂,代碼寫起來似乎還沒有第一章中的方便。主要是因為要我們增加一個IGradeInterface接口亲轨,還要自定義一個GradeBinder趋惨,同時,還需要寫代理類的相關(guān)代碼惦蚊,感覺非常繁瑣器虾。那么有沒有辦法讓代碼簡潔,寫起來還不繁瑣呢蹦锋?答案是肯定的兆沙,使用AIDL就可以實現(xiàn)。
四莉掂、AIDL
AIDL是Android Interface Description Languaged 簡寫葛圃。用于描寫客戶端/服務(wù)端通信接口的一種描述語言。提起AIDL相信很多人都會覺得頭大憎妙,定義一個AIDL接口库正,生成了那么多不知所云的代碼,看起來簡直就是災(zāi)難厘唾。先別擔心褥符,如果你看懂了第三章的內(nèi)容,那么其實你已經(jīng)完全掌握了AIDL抚垃。沒錯属瓣,說白了AIDL生成的那一坨代碼其實就是我們第三章中寫的代碼。即AIDL的原理其實就是使用了代理模式對Binder的使用進行了優(yōu)化讯柔,使用AIDL保證了代碼的整潔,同時也省去了自己編寫繁瑣的代理類相關(guān)代碼护昧。
關(guān)于AIDL的使用就非常簡單了魂迄。
1.創(chuàng)建AIDL接口
首先,在要創(chuàng)建AIDL的目錄上右鍵->New->AIDL->AIDl File 來創(chuàng)建一個AIDL文件惋耙,如下圖所示:
創(chuàng)建一個名為IGradeService的AIDL文件捣炬,并添加一個getStudentGrade的方法熊昌。代碼如下:
// IGradeService.aidl
package com.zhpan.sample.binder.aidl;
// Declare any non-default types here with import statements
interface IGradeService {
/**
* 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);
int getStudentGrade(String name);
}
接著Rebuild一下項目后IDE就會自動生成AIDL的代碼了。
2.AIDL生成的代碼
在項目的build目錄下com.zhpan.sample.binder.aidl包中會看到自動生成的一個名為IGradeService的接口湿酸,代碼如下:
// 這個接口相當于上一章中的IGradeInterface接口
public interface IGradeService extends android.os.IInterface {
...
// Stub是一個Binder婿屹,相當于上一章中的GradeBinder
public static abstract class Stub extends android.os.Binder
implements com.zhpan.sample.binder.aidl.IGradeService {
private static final java.lang.String DESCRIPTOR = "com.zhpan.sample.binder.aidl.IGradeService";
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
public static IGradeService asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.zhpan.sample.binder.aidl.IGradeService))) {
// 如果是當前進程則直接返回當前Binder對象
return ((com.zhpan.sample.binder.aidl.IGradeService) iin);
}
// 跨進程則返回Binder的代理對象
return new com.zhpan.sample.binder.aidl.IGradeService.Stub.Proxy(obj);
}
@Override public android.os.IBinder asBinder() {
return this;
}
@Override
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags)
throws android.os.RemoteException {
java.lang.String descriptor = DESCRIPTOR;
switch (code) {
case INTERFACE_TRANSACTION: {
reply.writeString(descriptor);
return true;
}
case TRANSACTION_basicTypes: {
data.enforceInterface(descriptor);
int _arg0;
_arg0 = data.readInt();
long _arg1;
_arg1 = data.readLong();
boolean _arg2;
_arg2 = (0 != data.readInt());
float _arg3;
_arg3 = data.readFloat();
double _arg4;
_arg4 = data.readDouble();
java.lang.String _arg5;
_arg5 = data.readString();
this.basicTypes(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5);
reply.writeNoException();
return true;
}
case TRANSACTION_getStudentGrade: {
data.enforceInterface(descriptor);
java.lang.String _arg0;
_arg0 = data.readString();
int _result = this.getStudentGrade(_arg0);
reply.writeNoException();
reply.writeInt(_result);
return true;
}
default: {
return super.onTransact(code, data, reply, flags);
}
}
}
// Binder的代理類,相當于上一章中的BinderProxy
private static class Proxy implements com.zhpan.sample.binder.aidl.IGradeService {
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote) {
mRemote = remote;
}
@Override public android.os.IBinder asBinder() {
return mRemote;
}
public java.lang.String getInterfaceDescriptor() {
return DESCRIPTOR;
}
@Override public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
double aDouble, java.lang.String aString) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(anInt);
_data.writeLong(aLong);
_data.writeInt(((aBoolean) ? (1) : (0)));
_data.writeFloat(aFloat);
_data.writeDouble(aDouble);
_data.writeString(aString);
boolean _status = mRemote.transact(Stub.TRANSACTION_basicTypes, _data, _reply, 0);
if (!_status && getDefaultImpl() != null) {
getDefaultImpl().basicTypes(anInt, aLong, aBoolean, aFloat, aDouble, aString);
return;
}
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
@Override public int getStudentGrade(java.lang.String name)
throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
int _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeString(name);
boolean _status = mRemote.transact(Stub.TRANSACTION_getStudentGrade, _data, _reply, 0);
if (!_status && getDefaultImpl() != null) {
return getDefaultImpl().getStudentGrade(name);
}
_reply.readException();
_result = _reply.readInt();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
public static com.zhpan.sample.binder.aidl.IGradeService sDefaultImpl;
}
static final int TRANSACTION_basicTypes = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_getStudentGrade = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
public static boolean setDefaultImpl(com.zhpan.sample.binder.aidl.IGradeService impl) {
if (Stub.Proxy.sDefaultImpl == null && impl != null) {
Stub.Proxy.sDefaultImpl = impl;
return true;
}
return false;
}
public static com.zhpan.sample.binder.aidl.IGradeService getDefaultImpl() {
return Stub.Proxy.sDefaultImpl;
}
}
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble,
java.lang.String aString) throws android.os.RemoteException;
public int getStudentGrade(java.lang.String name) throws android.os.RemoteException;
}
瞥一眼代碼會發(fā)現(xiàn)IGradeService接口中有一個名為Stub的內(nèi)部類推溃,它繼承了Binder昂利,并實現(xiàn)了IGradeService接口,并且它的內(nèi)部有一個asInterface的方法铁坎,這個方法與我們上一章BinderProxy中的asInterface一致蜂奸,只是寫的位置不同而已。另外在Stub的onTranscation方法的TRANSACTION_getStudentGrade條件中的代碼與GradeBinder的onTranscation方法代碼是一樣的硬萍。
接著扩所,Stub類中還有一個名為Proxy的內(nèi)部類。Proxy類與上一章的BinderProxy相對應(yīng)朴乖∽嫫粒可以看到Proxy類的構(gòu)造方法并沒有修飾符,而BinderProxy的構(gòu)造方法被聲明成了private买羞,都可以防止外部通過構(gòu)造方法區(qū)實例化代理類的對象袁勺。Proxy的getStudentGrade方法與BinderProxy中的getStudentGrade一樣,通過Binder去讀取服務(wù)端的寫入數(shù)據(jù)哩都。
3.AIDL客戶端
使用AIDL的客戶端實現(xiàn)幾乎與第三章中的代碼一致魁兼。只不過是在連接到服務(wù)端后通過IGradeService.Stub下的asInterface方法來獲取Binder或者Binder的代理對象焕妙。代碼如下:
public class AidlActivity extends BaseViewBindingActivity<ActivityBinderBinding> {
private IGradeService mBinderProxy;
private final ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
// 連接服務(wù)后寄啼,根據(jù)是否跨進程獲取Binder或者Binder的代理對象
mBinderProxy = IGradeService.Stub.asInterface(iBinder);
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
mBinderProxy = null;
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding.btnBindService.setOnClickListener(view -> bindGradeService());
// 查詢學生成績
binding.btnFindGrade.setOnClickListener(view -> getStudentGrade("Anna"));
}
// 綁定服務(wù)
private void bindGradeService() {
String action = "android.intent.action.server.aidl.gradeservice";
Intent intent = new Intent(action);
intent.setPackage(getPackageName());
bindService(intent, mServiceConnection, BIND_AUTO_CREATE);
}
// 查詢成績
private void getStudentGrade(String name) {
int grade = 0;
try {
grade = mBinderProxy.getStudentGrade(name);
} catch (RemoteException e) {
e.printStackTrace();
}
ToastUtils.showShort("Anna grade is " + grade);
}
}
到這里,關(guān)于AIDL的介紹就結(jié)束了蠢挡。有沒有驚奇的發(fā)現(xiàn)AIDL原來這么簡單儒鹿!
五化撕、總結(jié)
本篇文章主要帶大家認識了進程間通信和Binder與AIDL的使用。通過本篇文章的學習可以發(fā)現(xiàn)Binder與AIDL其實是非常簡單的约炎。了解了Binder之后植阴,我們就可以去更加深入的學習Android Framework層的知識了。