編輯:關於Android編程
AIDL是Android Interface Definition Language, 顧名思義,它主要就是用來定義接口的一種語言。Android提供AIDL主要用來進程間通訊。
從AIDL的功能來看,它主要的應用場景就是IPC。雖然同一個進程中的client-service也能夠通過AIDL定義接口來進行通信,但這並沒有發揮AIDL的主要功能。 概括來說:
如果不需要IPC,那就直接實現通過繼承Binder類來實現客戶端和服務端之間的通信。 如果確實需要IPC,但是無需處理多線程,那麼就應該通過Messenger來實現。Messenger保證了消息是串行處理的,其內部其實也是通過AIDL來實現。 在有IPC需求,同時服務端需要並發處理多個請求的時候,使用AIDL才是必要的AIDL的簡單使用步驟如下:
編寫.AIDL文件,定義需要的接口 實現定義的接口 將接口暴露給客戶端調用下面在AS上創建一個工程來使用一下:
創建Aidl文件:
創建完後,可以看到aidl接口文件裡面已經為我們提供了案例:
// IMyAidlInterface.aidl package com.example.aidltest; // Declare any non-default types here with import statements interface IMyAidlInterface { /** * 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); }這裡先說下AIDL支持下列所述的數據類型:
所有的基本類型(int、float等)StringCharSequenceListMap
如果要使用自定義的類型,必須實現Parcelable接口才能進行進程間通訊。
下面自定義HelloMsg類:
public class HelloMsg implements Parcelable {
private String name;
private int age;
public HelloMsg(String name, int age) {
this.name = name;
this.age = age;
}
protected HelloMsg(Parcel in) {
name = in.readString();
age = in.readInt();
}
public static final Creator CREATOR = new Creator() {
@Override
public HelloMsg createFromParcel(Parcel in) {
return new HelloMsg(in);
}
@Override
public HelloMsg[] newArray(int size) {
return new HelloMsg[size];
}
};
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(name);
dest.writeInt(age);
}
@Override
public String toString() {
return "HelloMsg{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
定義好
HelloMsg.java之後,還需要新增一個與其對應的AIDL文件。那麼同樣按照剛才的步驟右鍵src文件夾,添加一個名為IHelloMsgInterface的AIDL文件。
// IHelloMsgInterface.aidl
package com.example.aidltest;
parcelable HelloMsg;
注意到
parcelable的首字母是小寫的,這算是AIDL一個特殊的地方。
接下來還需要修改IMyAidlInterface.aidl文件,如下:
// IMyAidlInterface.aidl
package com.example.aidltest;
import com.example.aidltest.HelloMsg;
interface IMyAidlInterface {
HelloMsg sayHello();
}
即便
IMyAidlInterface.aidl和IHelloMsgInterface
.aidl位於同一個包下,這裡的
import是必須要有的。這也是AIDL一個特殊的地方。
注意:build之後發現會報錯,將IHelloMsgInterfece.aidl重命名為HelloMsg.aidl即可。
build成功之後會在build/generated/souce/aidl/debug目錄下生成IMyAidlInterface文件。可以大致熟悉下該文件的內容,對掌握android binder機制很有幫助
public interface IMyAidlInterface extends android.os.IInterface
{
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements com.example.aidltest.IMyAidlInterface
{
private static final java.lang.String DESCRIPTOR = "com.example.aidltest.IMyAidlInterface";
/** Construct the stub at attach it to the interface. */
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.example.aidltest.IMyAidlInterface interface,
* generating a proxy if needed.
*/
public static com.example.aidltest.IMyAidlInterface asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.example.aidltest.IMyAidlInterface))) {
return ((com.example.aidltest.IMyAidlInterface)iin);
}
return new com.example.aidltest.IMyAidlInterface.Stub.Proxy(obj);
}
這裡有個內部類Stub,它繼承系統Binder類和實現IMyAidlInterface接口。另外還提供了asInterface()接口,這個方法接受一個遠端
Binder對象,並將其轉化成
Stub對應的接口對象並返回。在構造方法調用Binder中的attachInterface方法把當前服務對象和描述符進行關聯。在asInterface方法中會調用queryLocalInterface查詢,如果不在同一進程就返回null,這個時候就返回Proxy對象。
上面看完了Stub類之後,發現他其實是遠端服務Binder對象的一個中間者,用來和客戶端進行交互的,下面再來看一下Proxy類:
private static class Proxy implements com.example.aidltest.IMyAidlInterface
{
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;
}
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
@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);
mRemote.transact(Stub.TRANSACTION_basicTypes, _data, _reply, 0);
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
@Override public com.example.aidltest.HelloMsg sayHello() throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
com.example.aidltest.HelloMsg _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_sayHello, _data, _reply, 0);
_reply.readException();
if ((0!=_reply.readInt())) {
_result = com.example.aidltest.HelloMsg.CREATOR.createFromParcel(_reply);
}
可以看到裡面有個mRemote對象,它是服務端傳遞過來的binder對象。調用transact方法後會調用上面Stub中的onTransact方法。這裡其實用了靜態代理模式,Proxy就是遠端傳遞過來的binder的本地代理。可以理解為客戶端的中間者。
Stub類是服務端的中間者,一般是實現了AIDL接口類型和繼承了Binder類,具備將Binder對象轉化成原生對象的能力
Proxy類是客戶端的中間者,一般是實現了AIDL接口類型
下面實現服務端的接口,定義RemoteService.java
public class RemoteService extends Service {
@Nullable
@Override
public IBinder onBind(Intent intent) {
return new IMyAidlInterface.Stub() {
@Override
public HelloMsg sayHello() throws RemoteException {
return new HelloMsg("wuliqing", 28);
}
};
}
}
客戶端調用服務端接口代碼如下:
public class MainActivity extends AppCompatActivity {
private IMyAidlInterface iMyAidlInterface = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
@Override
protected void onStart() {
super.onStart();
Intent intent = new Intent(this, RemoteService.class);
bindService(intent, serviceConnection, BIND_AUTO_CREATE);
}
@Override
protected void onStop() {
super.onStop();
unbindService(serviceConnection);
}
private ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
iMyAidlInterface = IMyAidlInterface.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
iMyAidlInterface = null;
}
};
public void onClickToSayHello(View view) {
if (iMyAidlInterface != null) {
try {
HelloMsg helloMsg = iMyAidlInterface.sayHello();
Toast.makeText(this, helloMsg.toString(), Toast.LENGTH_SHORT).show();
} catch (RemoteException e) {
e.printStackTrace();
Toast.makeText(this, e.toString(), Toast.LENGTH_SHORT).show();
}
}
}
}
在
onServiceConnected()回調中,我們使用
IMyAidlInterface.Stub.asInterface(service)方法返回我們的接口的引用。接著客戶端就可以通過它來對服務端發送請求了。
在這裡我為RemoteService設置了process屬性,讓它運行在與默認進程不同的進程中。
從上圖可看出客戶端和服務運行在兩個進程當中。
然後點擊按鈕,成功返回結果。
最後給出一張流程圖,加深印象:
版本:1.0日期:2014.11.10 2014.11.11版權:© 2014 kince 轉載注明出處一、概述 桌面抽屜之間的切換時Android用戶經
老早就使用了,但是現在才寫,惰性太大,現在改現在做產品的話相信大家基本都做分享吧,一個是項目的需求需要,還有一個是可以很好的宣傳自己的產品,其他的好處根據情況而論其實每個
本文實例講述了Android編程之繪制文本(FontMetrics)實現方法。分享給大家供大家參考,具體如下:Canvas 作為繪制文本時,使用FontMetrics對象
很多開發者一聽說Android終端的屏幕尺寸五花八門,屏幕分辨率千奇百怪,就覺得Android開發在屏幕適配方面是必定是一件頭疼的事情。因為在Android問世之前,廣大