深入解讀Android的內(nèi)部進(jìn)程通信接口AIDL
意義:
由于每個(gè)應(yīng)用進(jìn)程都有自己的獨(dú)立進(jìn)程空間,在android平臺(tái)上,一個(gè)進(jìn)程通常不能訪問另一個(gè)進(jìn)程的內(nèi)存空間,而我們經(jīng)常需要夸進(jìn)程傳遞對(duì)象,就需要把對(duì)象分解成操作對(duì)象可以理解的基本單元,并且有序的通過進(jìn)程邊界。
定義:
AIDL(Android Interface Definition Language)是一種IDL語言,用于生成可以在Android設(shè)備上兩個(gè)進(jìn)程之間進(jìn)行進(jìn)程間通信(interprocess communication, IPC)的代碼。如果在一個(gè)進(jìn)程中(例如Activity)要調(diào)用另一個(gè)進(jìn)程中(例如Service)對(duì)象的操作,就可以使用AIDL生成可序列化的參數(shù)。
說明以及實(shí)現(xiàn)流程:
AIDL接口和普通的java接口沒有什么區(qū)別,只是擴(kuò)展名為.aidl,保存在src目錄下,如果其他應(yīng)用程序需要IPC,則也需要在src目錄下創(chuàng)建同樣的AIDL文件,創(chuàng)建完畢之后,通過ADT工具,會(huì)在工程的gen目錄下生成相對(duì)應(yīng)的.java文件。
一般實(shí)現(xiàn)兩個(gè)進(jìn)程之間的通信需要實(shí)現(xiàn)下面幾個(gè)步驟
(1)在Eclipse的android工程目錄下面創(chuàng)建一個(gè).aidl擴(kuò)展名的文件,語法和java定義接口的語法差不多,不過需要自己手動(dòng)import對(duì)應(yīng)的包名。(比如需要用到list集合,則需要import java.util.List;)
(2)如果aidl文件符合規(guī)范,ADT工具會(huì)幫助編譯器在gen目錄下生成相對(duì)應(yīng)的.java文件。
(3)需要繼承實(shí)現(xiàn)一個(gè)服務(wù)類,跨進(jìn)程調(diào)用的基礎(chǔ)。
(4)在service端實(shí)現(xiàn)AIDL接口,如果有回調(diào)則在client端實(shí)現(xiàn)callback的AIDL接口。
(5)在AndroidManifest.xml注冊(cè)service。
注意:
實(shí)現(xiàn)AIDL,我們需要注意以下五點(diǎn)
(1)AIDL只支持接口方法,不能公開static變量。
(2)AIDL接口方法如果有參數(shù),則需要注意in、out、inout的使用規(guī)則,對(duì)于基本數(shù)據(jù)類型,默認(rèn)是in類型,可以不需要添加聲明,非基本可變對(duì)象需要在變量名之前添加方法類型
in表示輸入?yún)?shù),調(diào)用者把值傳遞給使用者使用。
out表示輸出參數(shù),調(diào)用者把容器傳遞給使用者填充,然后自己使用處理。
inout標(biāo)書輸入輸出參數(shù),傳送相應(yīng)的值并接收返回。
列舉一個(gè)out的使用例子:
服務(wù)端傳參數(shù)給客戶端,客戶端填充,服務(wù)端調(diào)用完之后,可以讀取到客戶端填寫的內(nèi)容,具體的例子后面將給出。
(3)AIDL定義的接口名必須和文件名一致。
(4)oneway表示用戶請(qǐng)求相應(yīng)功能時(shí)不需要等待響應(yīng)可直接調(diào)用返回,非阻塞效果,該關(guān)鍵字可以用來聲明接口或者聲明方法,如果接口聲明中用到了oneway關(guān)鍵字,則該接口聲明的所有方法都采用oneway方式。
(5)AIDL傳遞非基本可變長(zhǎng)度變量(非final對(duì)象),需要實(shí)現(xiàn)parcelable接口。
parcel一般都用在Binder通信,通過read和write方法進(jìn)行客戶端與服務(wù)端的數(shù)據(jù)傳遞(通信)。
比如:frameworks層服務(wù)端與hardware客戶端的Binder通信
reply->writeInt32(getCardReaderSize()); int mid = data.readInt32();
用來存放parcel數(shù)據(jù)的是內(nèi)存(RAM),而不是永遠(yuǎn)介質(zhì)(Nand等)。
parcelable定義了把數(shù)據(jù)寫入parcel和從parcel讀出數(shù)據(jù)的接口,一個(gè)類的實(shí)例,如果需要封裝到消息中去,就必須實(shí)現(xiàn)這一接口,如果實(shí)現(xiàn)了這個(gè)接口,該類的實(shí)例就是可以“被打包”。
Parcelabel 的實(shí)現(xiàn),需要在類中添加一個(gè)靜態(tài)成員變量 CREATOR,這個(gè)變量需要繼承 Parcelable.Creator 接口。
package com.zlc.provider;
import android.os.Parcel;
import android.os.Parcelable;
public class Students implements Parcelable{
private int stu_id;
private String stu_name;
public Students(Parcel source){
stu_id = source.readInt();
stu_name = source.readString();
}
public int getStu_id() {
return stu_id;
}
public void setStu_id(int stu_id) {
this.stu_id = stu_id;
}
public String getStu_name() {
return stu_name;
}
public void setStu_name(String stu_name) {
this.stu_name = stu_name;
}
@Override
public int describeContents() {
// TODO Auto-generated method stub
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
// TODO Auto-generated method stub
dest.writeInt(stu_id);
dest.writeString(stu_name);
}
//Interface that must be implemented and provided as a public CREATOR field that generates instances of your Parcelable class from a Parcel.
public final static Parcelable.Creator<Students> CREATOR = new Parcelable.Creator<Students>() {
@Override
public Students createFromParcel(Parcel source) {
// TODO Auto-generated method stub
return new Students(source);
}
@Override
public Students[] newArray(int size) {
// TODO Auto-generated method stub
return new Students[size];
}
};
}
實(shí)例:
下面列舉一個(gè)例子,主要實(shí)現(xiàn)客戶端調(diào)用服務(wù)端然后回調(diào)回來,具體實(shí)現(xiàn)功能改變客戶端的文字和圖片顯示,這個(gè)例子暫時(shí)效果是圖片的更改直接使用客戶端已經(jīng)準(zhǔn)備好的圖片,接下來幾篇博客會(huì)基于這個(gè)功能完善,到達(dá)服務(wù)端可以發(fā)送文字、圖片、文件句柄(I/O流),并且直接由服務(wù)端通過方法名稱直接調(diào)用客戶端方法,客戶端只需要注冊(cè)對(duì)應(yīng)的view并且提供相應(yīng)的方法給服務(wù)端使用,后面的兩部的完善主要用到反射和重寫MemoryFile(達(dá)到parcelable序列化效果)來實(shí)現(xiàn)。
(1)首先按照我們上面的步驟需要?jiǎng)?chuàng)建aidl文件,分別創(chuàng)建調(diào)用和回調(diào)的aidl文件,為了闡述更詳細(xì)一些,小編把parcelable對(duì)象也添加進(jìn)去,僅僅作為測(cè)試。
IMyAidlService.aidl主要由服務(wù)端實(shí)現(xiàn)客戶端調(diào)用
package com.zlc.aidl;
import com.zlc.aidl.DemoParcelable;
import com.zlc.aidl.AIDLCallback;
interface IMyAidlService{
void registerClient(AIDLCallback cb);//注冊(cè)回調(diào)
void saveDemoInfo(in DemoParcelable demo);//實(shí)際調(diào)用方法
}
AIDLCallback.aidl主要由客戶端實(shí)現(xiàn),服務(wù)端調(diào)用
package com.zlc.aidl;
import com.zlc.aidl.DemoParcelable;
import java.util.List;
interface AIDLCallback {
int returnResult(out List<DemoParcelable> list,int a);//回調(diào)給客戶端
void testMethod(out Bundle params);//用來測(cè)試參數(shù)in/out的使用
}
DemoParcelable.aidl聲明傳遞對(duì)象:
package com.zlc.aidl; parcelable DemoParcelable;
補(bǔ)充一點(diǎn):out和in參數(shù)區(qū)別其實(shí)很明顯我們直接查看adt生成在gen目錄下對(duì)應(yīng)的java文件就可以看出區(qū)別:當(dāng)是out參數(shù)的時(shí)候是執(zhí)行完之后從parcel對(duì)象讀取值,而in參數(shù)時(shí)是寫到parcel對(duì)象里面?zhèn)鬟^去。
我們看下當(dāng)testMethod分別是out和in修飾時(shí)生成的文件
當(dāng)時(shí)out的時(shí)候是從parcel對(duì)象里面讀數(shù)據(jù)
mRemote.transact(Stub.TRANSACTION_testMethod, _data, _reply, 0);
_reply.readException();
if ((0!=_reply.readInt())) {
params.readFromParcel(_reply);
}
當(dāng)時(shí)in的時(shí)候是從parcel對(duì)象里面取數(shù)據(jù)
if ((params!=null)) {
_data.writeInt(1);
params.writeToParcel(_data, 0);
}
else {
_data.writeInt(0);
}
mRemote.transact(Stub.TRANSACTION_testMethod, _data, _reply, 0);
_reply.readException();
(2)實(shí)現(xiàn)一個(gè)服務(wù)類用來實(shí)現(xiàn)進(jìn)程之間通信MyAidlService.java,貼出部分代碼,詳細(xì)代碼會(huì)在后面上傳。
@Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
Log.d(TAG, "MyAidlService onBind");
return mBinder;
}
private final IMyAidlService.Stub mBinder = new IMyAidlService.Stub() {
private AIDLCallback cb;
@Override
public void saveDemoInfo(DemoParcelable demo) throws RemoteException {
if (demo != null) {
if ("meinv1".equals(demo.getDemo_name())) {
demo.setDemo_name("meinv2");
}
list.add(demo);
Log.d(TAG, "saveDemoInfo list.size = " + list.size() + " list = " + list);
cb.returnResult(list, 5);
Bundle params = new Bundle();
cb.testMethod(params);
int width = params.getInt("width", 0);
int height = params.getInt("height", 0);
Log.d(TAG, "width = " + width + " height = "+height);
}
}
@Override
public void registerClient(AIDLCallback cb) throws RemoteException {
cb.asBinder().linkToDeath(new DeathRecipient() {
@Override
public void binderDied() {
try {
Log.i(TAG, "[ServiceAIDLImpl]binderDied.");
} catch (Throwable e) {
}
}
}, 0);
}
};
(3)實(shí)現(xiàn)客戶端連接并且實(shí)現(xiàn)callback方法
private ServiceConnection mRemoteConnection = new ServiceConnection() {
@Override
public void onServiceDisconnected(ComponentName name) {
// TODO Auto-generated method stub
Log.d(TAG, "onServiceDisconnected");
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
// TODO Auto-generated method stub
Log.d(TAG, "onServiceConnected");
mRemoteService = (IMyAidlService) IMyAidlService.Stub
.asInterface(service);
if(mRemoteService != null)
Log.d(TAG, "onServiceConnected success");
}
};
……
btn.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
String actionName = "com.zlc.aidl.server.MyAidlService";
Intent intent = new Intent(actionName);
boolean ret = bindService(intent, mRemoteConnection,
Context.BIND_AUTO_CREATE);
Log.d(TAG, " ret ?=" + ret);
if (ret) {
new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
try {
DemoParcelable demo = new DemoParcelable();
List<String> list = new ArrayList<String>();
list.add("like dance");
demo.setDemo_id((Integer) img.getTag());
demo.setDemo_name("meinv1");
demo.setDemo_list(list);
mRemoteService.registerClient(callback);
mRemoteService.saveDemoInfo(demo);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}).start();
}
}
});
}
……
private final AIDLCallback callback = new AIDLCallback.Stub() {
@Override
public int returnResult(List<DemoParcelable> list, int a)
throws RemoteException {
if (list != null)
Log.d(TAG, "list.size = " + list.size()+" a="+a);
for (DemoParcelable demoParcelable : list) {
doFresh(demoParcelable);
}
return 0;
}
@Override
public void testMethod(Bundle outParams) throws RemoteException {
// TODO Auto-generated method stub
if (outParams != null) {
outParams.putInt("width", 11);
outParams.putInt("height", 12);
}
}
};
(4)在androidManifest.xml里面注冊(cè)service服務(wù)。
注意一點(diǎn):android:process=":remote",代表在應(yīng)用程序里,當(dāng)需要該service時(shí),會(huì)自動(dòng)創(chuàng)建新的進(jìn)程。而如果是android:process="remote",沒有“:”分號(hào)的,則創(chuàng)建全局進(jìn)程,不同的應(yīng)用程序共享該進(jìn)程。
通過ps直接看pid進(jìn)程號(hào)就可以看出。
讓應(yīng)用的組件在一個(gè)單獨(dú)的進(jìn)程中運(yùn)行,如果帶冒號(hào): ,則創(chuàng)建一個(gè)專屬于當(dāng)前進(jìn)程的進(jìn)程,如果不帶冒號(hào),需要使用標(biāo)準(zhǔn)的命名規(guī)范命名進(jìn)程名,例如com.xxx.xxx.xxx,而且該進(jìn)程是全局共享的進(jìn)程,即不同應(yīng)用的組件都可以運(yùn)行于該進(jìn)程。
這可以突破應(yīng)用程序的24M(或16M)內(nèi)存限制。
總之,使用帶:remote的屬性的進(jìn)程id pid不同,父進(jìn)程ID PPID是一樣的。而使用不帶冒號(hào)的remote則會(huì)創(chuàng)建兩個(gè)完全獨(dú)立的進(jìn)程。
- Android使用AIDL實(shí)現(xiàn)兩個(gè)App間通信
- Android進(jìn)程通信之Messenger和AIDL使用詳解
- Android AIDL——進(jìn)程通信機(jī)制詳解
- Android AIDL實(shí)現(xiàn)進(jìn)程間通信探索
- Android IPC進(jìn)程間通信詳解最新AndroidStudio的AIDL操作)
- Android編程實(shí)現(xiàn)AIDL(跨進(jìn)程通信)的方法詳解
- 實(shí)例講解Android中的AIDL內(nèi)部進(jìn)程通信接口使用
- 基于Android AIDL進(jìn)程間通信接口使用介紹
- Android AIDL實(shí)現(xiàn)兩個(gè)APP間的跨進(jìn)程通信實(shí)例
相關(guān)文章
Android組件Activity的啟動(dòng)過程深入分析
這篇文章主要介紹了Android組件Activity的啟動(dòng)過程,Activity作為Android四大組件之一,他的啟動(dòng)沒有那么簡(jiǎn)單。這里涉及到了系統(tǒng)服務(wù)進(jìn)程,啟動(dòng)過程細(xì)節(jié)很多,這里我只展示主體流程。activity的啟動(dòng)流程隨著版本的更替,代碼細(xì)節(jié)一直在進(jìn)行更改,每次都會(huì)有很大的修改2023-04-04
Android EditText默認(rèn)不彈出輸入法的實(shí)現(xiàn)方法
下面小編就為大家分享一篇Android EditText默認(rèn)不彈出輸入法的實(shí)現(xiàn)方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2018-01-01
Android對(duì)EditTex的圖片實(shí)現(xiàn)監(jiān)聽
這篇文章主要為大家詳細(xì)介紹了Android如何對(duì)EditTex的圖片實(shí)現(xiàn)監(jiān)聽,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-10-10
解決Android TabLayout 在寬屏幕上tab不能平均分配的問題
這篇文章主要介紹了解決Android TabLayout 在寬屏幕上tab不能平均分配的問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2020-08-08
詳解Android studio實(shí)現(xiàn)語音轉(zhuǎn)文字功能
這篇文章主要介紹了如何通過Android studio調(diào)用科大訊飛的語音轉(zhuǎn)文字功能,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以了解一下2022-03-03
Android實(shí)現(xiàn)水波紋擴(kuò)散效果的實(shí)例代碼
這篇文章主要介紹了Android實(shí)現(xiàn)水波紋擴(kuò)散效果的實(shí)例代碼,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-05-05
學(xué)習(xí)使用Material Design控件(二)使用DrawerLayout實(shí)現(xiàn)側(cè)滑菜單欄效果
這篇文章主要為大家介紹了學(xué)習(xí)使用Material Design控件的詳細(xì)教程,使用DrawerLayout和NavigationView實(shí)現(xiàn)側(cè)滑菜單欄效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-07-07

