我最初接觸aidl
的時候焙糟,就感覺這個好難,一下一大堆的代碼就出來了样屠〈┐椋總覺得這種東西會用就行,懂它什么的因該是大神們的事痪欲,但是知其然悦穿,不知其所以然,用起來總是覺得怪怪的业踢,所以就決定慢慢理一下它咧党。
其實一步一步看懂,也很簡單的陨亡。
aidl的使用
最常見的aidl
的使用就是Service
的跨進程通信了,那么我們就寫一個Activity
和Service
的跨進程通信吧深员。
首先负蠕,我們就在AS里面新建一個aidl
文件(ps:現(xiàn)在AS建aidl
不要求和java包名相同了):
package aidl;
interface IMyInterface {
String getInfor(String s);
}
可以看到,在這里面我們就一個方法getInfor(String s)
,接受一個字符串參數(shù)倦畅,然后返回一個字符串遮糖,恩,相當(dāng)?shù)暮唵巍?/p>
接著你sync project
一下就可以在app/generated/source/aidl/debug/aidl
里面發(fā)現(xiàn)由aidl
文件生成的java
文件了叠赐。點進去一看欲账,可能你也被這個貌似'龐大'的類給嚇住了,那現(xiàn)在我們就先不管它了芭概。
然后就看看Service
:
public class MyService extends Service {
public final static String TAG = "MyService";
private IBinder binder = new IMyInterface.Stub() {
@Override
public String getInfor(String s) throws RemoteException {
Log.i(TAG, s);
return "我是 Service 返回的字符串";
}
};
@Overrid
public void onCreate() {
super.onCreate();
Log.i(TAG, "onCreat");
}
@Override
public IBinder onBind(Intent intent) {
return binder;
}
}
這里我們寫了一個Service
赛不,看一下也比較簡單。先new
了一IMyInterface.Stub()
并把它向上轉(zhuǎn)型成了IBinder
罢洲,最后在onBind
方法中返回回去踢故。可能你注意到了,在IMyInterface.Stub()
的內(nèi)部我們重寫getInfor(String s)
方法殿较,沒錯這就是我們 aidl
文件中定義的接口耸峭。
對了,因為我們希望看到的是跨進程通信淋纲,所以我們把MyService
定義成新啟動一個進程:
<service
android:name=".server.MyService"
android:process="com.mathiasluo.remote" />
定義為啟動在新進程中劳闹,只需要在AndroidMainfest.xml
中聲明是加上一個process
屬性即可,不過這里有兩個地方值得注意:1.組件默認(rèn)的進程名就是包名;2.定義新的進程名的時候需要以包的形式(eg: com.luo.aidl)洽瞬。
接著本涕,我們繼續(xù)向下看:
public class MainActivity extends AppCompatActivity {
public final static String TAG = "MainActivity";
private IMyInterface myInterface;
private ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
myInterface = IMyInterface.Stub.asInterface(service);
Log.i(TAG, "連接Service 成功");
try {
String s = myInterface.getInfor("我是Activity傳來的字符串");
Log.i(TAG, "從Service得到的字符串:" + s);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.e(TAG, "連接Service失敗");
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
startAndBindService();
}
private void startAndBindService() {
Intent service = new Intent(MainActivity.this, MyService.class);
//startService(service);
bindService(service, serviceConnection, Context.BIND_AUTO_CREATE);
}
}
由于主要是Service
和Activity
間的通信,所以為了讓代碼整潔就沒有寫UI
了片任。
在onCreate(Bundle savedInstanceState)
中偏友,我們調(diào)用了自己定義的一個方法startAndBindService()
,這個方法里面我們生成了一個Intent
对供,然后 bindService
了這個Intent
傳入了三個參數(shù)分別是Intent
位他、ServiceConnection
、Flag
产场。
Intent
我們就不用說了鹅髓,我們看看后面兩個參數(shù):
在Activity
中,我們new
了一個ServiceConnection
并實現(xiàn)了他的兩個方法onServiceConnected
京景、onServiceDisconnected
窿冯。在onServiceConnected
中我們通過IMyInterface.Stub.asInterface(service)
把傳來的IBinder
轉(zhuǎn)換成了我們定義的IMyInterface
。然后我們調(diào)用了getInfor
方法确徙,傳遞了個字符串和獲取從MyService
傳來的字符串醒串,并且打印了Log
。
對于我們傳的Context.BIND_AUTO_CREATE
的意思就是說:如果綁定Service的時候鄙皇,Service還沒有被創(chuàng)建芜赌,就創(chuàng)建它。當(dāng)然伴逸,這里還有很多Flag
缠沈,我也不一一說了。
然后错蝴,我們的編碼就完成了洲愤,開始運行測試一下把!
顷锰、
看一下運行結(jié)果柬赐,在這兩個不同的進程
中都得到了我們想要的結(jié)果,所以官紫,一個用aidl
實現(xiàn)的跨進程通信就這樣完成了躺率。當(dāng)然我們的demo(這次不能拼錯了)中玛界,getInfor
沒有任何邏輯,你也可以加一些邏輯去執(zhí)行一些復(fù)雜的操作悼吱。
aidl的理解
現(xiàn)在我們會使用aidl
了慎框,那我們還不去掀開它神秘的面紗。
Service中的IBinder
還記得我們在MyService
中利用new IMyInterface.Stub()
向上轉(zhuǎn)型成了IBinder
然后在onBind
方法中返回的后添。那我們就看看IMyInterface.Stub
吧:
public static abstract class Stub extends android.os.Binder implements aidl.IMyInterface {
..........
}
可以看到笨枯,Stub
是IMyInterface
中的一個靜態(tài)抽象類,繼承了Binder
遇西,并且實現(xiàn)了IMyInterface
接口馅精。這也就解釋了我們定義IMyInterface.Stub
的時候為什么需要實現(xiàn)IMyInterface
中的方法了,也說明了為什么我們可以把IMyInterface.Stub
向上轉(zhuǎn)型成IBinder
了粱檀。
Activity中的IMyInterface
在Activity
中洲敢,通過ServiceConnection
連接MyService
并成功回調(diào)onServiceConnected
中我們把傳回來的IBinder
通過IMyInterface.Stub.asInterface(service)
轉(zhuǎn)換成為IMyInterface
,那就來看看這里是如何轉(zhuǎn)換的吧:
public static abstract class Stub extends android.os.Binder implements aidl.IMyInterface {
..........
public static aidl.IMyInterface asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
//檢查Binder是不是在當(dāng)前進程
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof aidl.IMyInterface))) {
return ((aidl.IMyInterface) iin);
}
return new aidl.IMyInterface.Stub.Proxy(obj);
}
}
首先茄蚯,我們因該明白的是压彭,傳回來的IBinder
就是我們在Service
的onBind( )
方法所return
的IBinder
,然后我們調(diào)用Stub
中的靜態(tài)方法asInterface
并把返回來的IBinder
當(dāng)參數(shù)傳進去渗常。
在asInterface
方法中壮不,首先判斷了傳進來的IBinder
是不是null
,如果為null
就返回一個null
;接著就判斷傳進來的IBinder
是不是就在當(dāng)前進程里面皱碘,如果是的話就直接返回IMyInterface
询一,不是的話就返回IMyInterface.Stub.Proxy(obj)
。這里我覺得需要明白的是:直接返回的IMyInterface
是實現(xiàn)了定義的接口方法getInfor
的癌椿。因為在IMyInterface.Stub
中所實現(xiàn)的健蕊。當(dāng)然如果是在同一進程中,那么我們調(diào)用IMyInterface
的方法時就是在本地調(diào)用方法踢俄,直接調(diào)用就可以了绊诲。
如果沒在同一進程,就會返回IMyInterface.Stub.Proxy(obj)
:
private static class Proxy implements aidl.IMyInterface {
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 java.lang.String getInfor(java.lang.String s) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.lang.String _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeString(s);
//傳送數(shù)據(jù)到遠(yuǎn)程的
mRemote.transact(Stub.TRANSACTION_getInfor, _data, _reply, 0);
_reply.readException();
//接受從遠(yuǎn)端傳回的數(shù)據(jù)
_result = _reply.readString();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
static final int TRANSACTION_getInfor = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
}
在Proxy
中褪贵,我們首先把Service
連接成功返回的IBinder
它的內(nèi)部變量mRemote
,這里在提一下,這里得IBinder
還是是MyService
中onBind
所返回的抗俄。然后脆丁,當(dāng)我們調(diào)用IMyInterface
的方法的時候,其實就是調(diào)用的Proxy
的方法了动雹,這也是為什么這個類叫做Porxy
的原因了槽卫。
當(dāng)調(diào)用IMyInterface.getInfor(String s)
,我們就看Proxy
中的getInfor
,先獲取了兩個Parcel
對象 _data
胰蝠、_data
歼培,從變量名就可以看出震蒋,一個是傳送數(shù)據(jù)的,另一個則是接受返回數(shù)據(jù)的躲庄。接著查剖,向_data
中寫入了DESCRIPTOR
(也就是這個類的全名),再寫入了方法參數(shù)噪窘。然后就到了最重要的一步了笋庄,
mRemote.transact(Stub.TRANSACTION_getInfor, _data, _reply, 0);
這里我們調(diào)用了IBinder
的transact
方法,來把數(shù)據(jù)傳給遠(yuǎn)端的服務(wù)器倔监。然后在我們遠(yuǎn)程的MyService
中直砂,里面的Stub
中就會回調(diào)onTransact()
(因為你把數(shù)據(jù)傳個遠(yuǎn)程的服務(wù),遠(yuǎn)端的服務(wù)收到數(shù)據(jù)也就回調(diào)了)
注意:這里是在遠(yuǎn)程的服務(wù)里調(diào)用的浩习。
@Overridepublic boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
switch (code) {
case INTERFACE_TRANSACTION: {
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_getInfor: {
data.enforceInterface(DESCRIPTOR);
java.lang.String _arg0;
//取出參數(shù)
_arg0 = data.readString();
// 遠(yuǎn)程服務(wù)調(diào)用自己本地實現(xiàn)的方法獲取返回值
java.lang.String _result = this.getInfor(_arg0);
reply.writeNoException();
//寫入返回值
reply.writeString(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
onTransact
方法是在Stub
的內(nèi)部實現(xiàn)的静暂。
先看一下它的四個參數(shù):
code
:每個方法都有一個int
類型的數(shù)字用來區(qū)分(后面中的swicth),在我們例子中也就是我們Proxy中的Stub.TRANSACTION_getInfor
谱秽。
data
:傳過來的數(shù)據(jù)洽蛀,其中包含我們的參數(shù),以及類的描述弯院。
reply
:傳回的數(shù)據(jù)辱士,我們要寫入是否發(fā)生了Exception
,以及返回值
flags
:該方法是否有返回值 ,0
表示有返回值听绳。
調(diào)用onTransact
就表示有數(shù)據(jù)傳來颂碘,首先就會通過swicth
判斷是哪個方法,然后取出方法參數(shù)椅挣,調(diào)用本地實現(xiàn)的方法獲取返回值头岔,寫入返回值到reply
。最后鼠证,返回true
峡竣,才會把數(shù)據(jù)發(fā)送出去诬垂,發(fā)揮false
就不會把結(jié)果返回給Activity
了由捎。這里也就是說,只有返回true
,我們Proxy
中才能接受從遠(yuǎn)端傳回的數(shù)據(jù)
塑煎。
//傳送數(shù)據(jù)到遠(yuǎn)程的
mRemote.transact(Stub.TRANSACTION_getInfor, _data, _reply, 0);
_reply.readException();
//接受從遠(yuǎn)端傳回的數(shù)據(jù)
_result = _reply.readString();
注意:Service
也是把數(shù)據(jù)發(fā)送出來荠列,讓客戶端接受的类浪。
Service
發(fā)出了數(shù)據(jù),客戶端接收到了肌似,就會一層一層返回去费就。所以,當(dāng)我們簡單的調(diào)用IMyInterface
的getInfor
時候川队,先是Proxy
的transact
發(fā)送出數(shù)據(jù)力细,然后服務(wù)端的onTransact
接受并處理傳來的數(shù)據(jù)睬澡,再把處理得到的數(shù)據(jù)寫入返回值并發(fā)送給客戶端,客戶端讀取值后就成為調(diào)用方法的返回值返回了眠蚂。
然后aidl
的基本原理就是這樣了煞聪,看明白了aidl
,才發(fā)現(xiàn)原來aidl
不過就是幫我們生成了那些數(shù)據(jù)寫入河狐,傳送米绕,讀取的方法而已。所以馋艺,我們自己寫一個不要aidl
的跨進程通信也是很容易的栅干。