上一篇文章已經(jīng)講述了如何在Android studio中搭建基于aidl的cs模型框架,只是用起來(lái)了,這次對(duì)aidl及cs端如何調(diào)用的原理進(jìn)行簡(jiǎn)單分析
1 創(chuàng)建AIDL文件
AIDL 文件可以分為兩類。
一類是用來(lái)定義接口方法,聲明要暴露哪些接口給客戶端調(diào)用;
一類用來(lái)聲明實(shí)現(xiàn)了 Parcelable 接口的數(shù)據(jù)類型,以供其他 AIDL 文件使用那些非默認(rèn)支持的數(shù)據(jù)類型。
在 AIDL 文件中需要明確標(biāo)明引用到的數(shù)據(jù)類型所在的包名,即使兩個(gè)文件處在同個(gè)包名下。
默認(rèn)情況下,AIDL 支持下列數(shù)據(jù)類型:
八種基本數(shù)據(jù)類型:byte、char、short、int、long、float、double、boolean
String,CharSequence
List類型。List承載的數(shù)據(jù)必須是AIDL支持的類型,或者是其它聲明的AIDL對(duì)象
Map類型。Map承載的數(shù)據(jù)必須是AIDL支持的類型,或者是其它聲明的AIDL對(duì)象
客戶端和服務(wù)端都需要?jiǎng)?chuàng)建,我們先在server端中創(chuàng)建,然后復(fù)制到client端即可。在 Android Studio 中右鍵點(diǎn)擊新建一個(gè) AIDL 文件,如圖所示:
創(chuàng)建完成后,系統(tǒng)就會(huì)默認(rèn)創(chuàng)建一個(gè) aidl 文件夾,文件夾下的目錄結(jié)構(gòu)即是工程的包名,AIDL 文件就在其中。如圖所示:
文件中會(huì)有一個(gè)默認(rèn)方法,可以刪除掉,也可以新增其他方法。
2 實(shí)現(xiàn)接口
創(chuàng)建或修改過(guò) AIDL 文件后需要 build 下工程,Android SDK 工具會(huì)生成以 .aidl 文件命名的 .java 接口文件(例如,IRemoteService.aidl 生成的文件名是 IRemoteService.java),在進(jìn)程間通信中真正起作用的就是該文件。生成的接口包含一個(gè)名為 Stub 的子類(例如,IRemoteService.Stub),該子類是其父接口的抽象實(shí)現(xiàn),并且會(huì)聲明 AIDL 文件中的所有方法。
如要實(shí)現(xiàn) AIDL 生成的接口,請(qǐng)實(shí)例化生成的 Binder 子類(例如,IRemoteService.Stub),并實(shí)現(xiàn)繼承自 AIDL 文件的方法。
以下是使用匿名內(nèi)部類實(shí)現(xiàn) IRemoteService 接口的示例:
private final IRemoteService.Stub binder = new IRemoteService.Stub() {
public int getPid(){
return Process.myPid();
}
public void basicTypes(int anInt, long aLong, boolean aBoolean,
float aFloat, double aDouble, String aString) {
// Does nothing
}
};
現(xiàn)在,binder 是 Stub 類的一個(gè)實(shí)例(一個(gè) Binder),其定義了服務(wù)端的 RPC 接口。
3 server端公開(kāi)接口
在為服務(wù)端實(shí)現(xiàn)接口后,需要向客戶端公開(kāi)該接口,以便客戶端進(jìn)行綁定。創(chuàng)建 Service 并實(shí)現(xiàn) onBind(),從而返回生成的 Stub 的類實(shí)例。以下是服務(wù)端的示例代碼:
public class RemoteService extends Service {
private final String TAG = "RemoteService";
@Override
public void onCreate() {
super.onCreate();
}
@Override
public IBinder onBind(Intent intent) {
// Return the interface
Log.d(TAG, "onBind");
return binder;
}
private final IRemoteService.Stub binder = new IRemoteService.Stub() {
public int getPid() {
return Process.myPid();
}
public void basicTypes(int anInt, long aLong, boolean aBoolean,
float aFloat, double aDouble, String aString) {
Log.d(TAG, "basicTypes anInt:" + anInt + ";aLong:" + aLong + ";aBoolean:" + aBoolean + ";aFloat:" + aFloat + ";aDouble:" + aDouble + ";aString:" + aString);
}
};
}
我們還需要在 Manefest 文件中注冊(cè)我們創(chuàng)建的這個(gè) Service,否則客戶端無(wú)法綁定服務(wù)。
<service
android:name=".RemoteService"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="com.example.aidl"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</service>
4 client 端調(diào)用IPC方法
當(dāng)客戶端(如 Activity)調(diào)用 bindService() 以連接此服務(wù)時(shí),客戶端的 onServiceConnected() 回調(diào)會(huì)接收服務(wù)端的 onBind() 方法所返回的 binder 實(shí)例。
客戶端還必須擁有接口類的訪問(wèn)權(quán)限,因此如果客戶端和服務(wù)端在不同應(yīng)用內(nèi),則客戶端應(yīng)用的 src/ 目錄內(nèi)必須包含 .aidl 文件(該文件會(huì)生成 android.os.Binder 接口,進(jìn)而為客戶端提供 AIDL 方法的訪問(wèn)權(quán)限)的副本。所以我們需要把服務(wù)端的 aidl 文件夾整個(gè)復(fù)制到客戶端的 java 文件夾同個(gè)層級(jí)下,不需要改動(dòng)任何代碼。
當(dāng)客戶端在 onServiceConnected() 回調(diào)中收到 IBinder 時(shí),它必須調(diào)用 IRemoteService.Stub.asInterface(service),以將返回的參數(shù)轉(zhuǎn)換成 IRemoteService 類型。例如:
IRemoteService iRemoteService;
private ServiceConnection mConnection = new ServiceConnection() {
// Called when the connection with the service is established
public void onServiceConnected(ComponentName className, IBinder service) {
// Following the example above for an AIDL interface,
// this gets an instance of the IRemoteInterface, which we can use to call on the service
iRemoteService = IRemoteService.Stub.asInterface(service);
}
// Called when the connection with the service disconnects unexpectedly
public void onServiceDisconnected(ComponentName className) {
Log.e(TAG, "Service has unexpectedly disconnected");
iRemoteService = null;
}
};
獲得了 iRemoteService 對(duì)象,我們就可以調(diào)用 AIDL 中定義的方法了。如要斷開(kāi)連接,可以調(diào)用unbindService() 方法。以下是客戶端的示例代碼:
public class MainActivity extends AppCompatActivity {
private final String TAG = "ClientActivity";
private IRemoteService iRemoteService;
private Button mBindServiceButton;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mBindServiceButton = findViewById(R.id.btn_bind_service);
mBindServiceButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String text = mBindServiceButton.getText().toString();
if ("Bind Service".equals(text)) {
Intent intent = new Intent();
intent.setAction("com.example.aidl");
intent.setPackage("com.example.aidl.server");
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
} else {
unbindService(mConnection);
mBindServiceButton.setText("Bind Service");
}
}
});
}
ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceDisconnected(ComponentName name) {
Log.d(TAG, "onServiceDisconnected");
iRemoteService = null;
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.d(TAG, "onServiceConnected");
iRemoteService = IRemoteService.Stub.asInterface(service);
try {
int pid = iRemoteService.getPid();
int currentPid = Process.myPid();
Log.d(TAG, "currentPID: " + currentPid + ", remotePID: " + pid);
iRemoteService.basicTypes(12, 123, true, 123.4f, 123.45,
"服務(wù)端你好,我是客戶端");
} catch (RemoteException e) {
e.printStackTrace();
}
mBindServiceButton.setText("Unbind Service");
}
};
}
5 通過(guò)IPC傳遞對(duì)象
除了上面默認(rèn)支持的數(shù)據(jù)類型,AIDL 還可以傳遞對(duì)象,但是該類必須實(shí)現(xiàn) Parcelable 接口。而該類是兩個(gè)應(yīng)用間都需要使用到的,所以也需要在 AIDL 文件中聲明該類,為了避免出現(xiàn)類名重復(fù)導(dǎo)致無(wú)法創(chuàng)建 AIDL 文件的錯(cuò)誤,這里需要先創(chuàng)建 AIDL 文件,之后再創(chuàng)建類。
先在服務(wù)端新建一個(gè) AIDL 文件,比如 Rect.aidl,示例如下:
// Rect.aidl
package com.example.aidl.server;
// Declare Rect so AIDL can find it and knows that it implements
// the parcelable protocol.
parcelable Rect;
然后就可以創(chuàng)建 Rect 類了,并使之實(shí)現(xiàn) Parcelable 接口。示例代碼如下:
public class Rect implements Parcelable {
private int left;
private int top;
private int right;
private int bottom;
public Rect(int left, int top, int right, int bottom) {
this.left = left;
this.top = top;
this.right = right;
this.bottom = bottom;
}
public static final Parcelable.Creator<Rect> CREATOR = new Parcelable.Creator<Rect>() {
public Rect createFromParcel(Parcel in) {
return new Rect(in);
}
public Rect[] newArray(int size) {
return new Rect[size];
}
};
private Rect(Parcel in) {
readFromParcel(in);
}
@Override
public void writeToParcel(Parcel out, int flags) {
out.writeInt(left);
out.writeInt(top);
out.writeInt(right);
out.writeInt(bottom);
}
public void readFromParcel(Parcel in) {
left = in.readInt();
top = in.readInt();
right = in.readInt();
bottom = in.readInt();
}
@Override
public int describeContents() {
return 0;
}
@NonNull
@Override
public String toString() {
return "Rect[left:" + left + ",top:" + top + ",right:" + right + ",bottom:" + bottom + "]";
}
}
這樣我們就可以在之前創(chuàng)建的 IRemoteService.aidl 中新增一個(gè)方法來(lái)傳遞 Rect 對(duì)象了,示例代碼如下:
// IRemoteService.aidl
package com.example.aidl.server;
import com.example.aidl.server.Rect;
// Declare any non-default types here with import statements
interface IRemoteService {
/**
* 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 getPid();
void addRectInOut(inout Rect rect);
}
注意這里需要明確導(dǎo)包:文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-683619.html
import com.example.aidl.server.Rect;
然后將新增的 Rect.aidl 文件和 Rect.java 文件還有修改的 IRemoteService.aidl 文件同步到客戶端相同路徑下,如圖所示:
build 下工程,就可以在客戶端調(diào)用到該 addRectInOut 方法了。示例代碼如下:文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-683619.html
ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceDisconnected(ComponentName name) {
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
iRemoteService = IRemoteService.Stub.asInterface(service);
try {
iRemoteService.addRectInOut(new Rect(1, 2, 3, 4));
} catch (RemoteException e) {
e.printStackTrace();
}
}
};
到了這里,關(guān)于[Android AIDL] --- AIDL原理簡(jiǎn)析的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!