最近有空就在看IPC,正好路過AIDL這裏,發現基本忘乾淨了…正好藉此機會回顧並記錄下來,那麼接下來我就先擼爲敬了.
AIDL用來做什麼
AIDL(Android 接口定義語言)與您可能使用過的其他 IDL 類似。 您可以利用它定義客戶端與服務使用進程間通信 (IPC) 進行相互通信時都認可的編程接口。 在 Android 上,一個進程通常無法訪問另一個進程的內存。 儘管如此,進程需要將其對象分解成操作系統能夠識別的原語,並將對象編組成跨越邊界的對象。 編寫執行這一編組操作的代碼是一項繁瑣的工作,因此 Android 會使用 AIDL 來處理。
上面是文檔中的描述,通俗的說法:AIDL的作用是讓你可以在自己的APP裏綁定一個其他APP的service並可以拿到它暴露給你的方法獲取數據,這樣你的APP可以和其他APP交互。
AIDL使用
上面已經說過AIDL是用來將對象分解成操作系統能夠識別的原語,並將對象編組成跨越邊界的對象,那麼這裏就按AIDL中可以使用的數據類型來分別說明如何使用.
基本類型
因爲AIDL是兩個APP交互啦,所以當然要兩個APP啦,我們在第一個工程目錄右鍵
輸入名字AS就幫我們新建了AIDL文件了
// IMyName.aidl
package com.zly.www.test1;
// Declare any non-default types here with import statements
interface IMyName {
/**
* 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);
}
這裏basicTypes這個方法其實沒啥用可以刪掉,但從他註釋裏面可以看出,AIDL默認支持int,long,boolean,float,double,String基本類型的數據.因爲這裏是要跨進程通訊的,所以不是隨便你自己定義的一個類型就可以在AIDL使用的,自定義類型我們在後面會講.我們在AIDL文件中定義一個我們要提供給第二個APP使用的接口.
那麼我們把這個類稍微修改下
// IMyName.aidl
package com.zly.www.test1;
// Declare any non-default types here with import statements
interface IMyName {
String getName();
}
然後點下sycn project讓AS幫我們生成下aidl代碼.
接下來我們新建一個service並且在清單註冊,然後在service中新建一個內部類MyBinder繼承剛剛寫的AIDL接口IMyName裏的Stub類並實現方法,在onBind()返回該實例,你可能會問我沒在IMyName裏寫Stub類呀,其實這裏Stub是AS根據剛剛AIDL文件生成的代碼中的.
public class MyService extends Service {
private MyBinder mBinder = new MyBinder();
@Nullable
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
class MyBinder extends IMyName.Stub{
@Override
public String getName() throws RemoteException {
return "帥也是錯嗎";
}
}
}
接下來新建一個項目,將前面寫的AIDL文件拷貝過來,要注意的是包名必須完全一致.
syncProject一下項目,然後在MainActivity中綁定service
public class MainActivity extends AppCompatActivity {
private IMyName iMyName;
private ServiceConnection serviceConnection;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent intent = new Intent("aaaa");
intent.setPackage("com.zly.www.test1");
bindService(intent, serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
iMyName = IMyName.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
iMyName = null;
}
}, BIND_AUTO_CREATE);
}
public void get(View v) {
try {
Toast.makeText(this, iMyName.getName(), Toast.LENGTH_SHORT).show();
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
protected void onDestroy() {
super.onDestroy();
unbindService(serviceConnection);
}
}
這邊我們通過隱式意圖來綁定service,在onServiceConnected方法中通過IMyName.Stub.asInterface(service)獲取IMyName對象,然後在get()中調用IMyName.getName()。(這裏有點需要注意Android 5.0出後,其中有個特性就是Service Intent must be explitict,也就是說從Lollipop開始,service服務必須採用顯示方式啓動,所以這裏intent我指定了package)
自定義類型
如果要在AIDL中使用自定義類型的數據,第一自定義類型要實現Parcelable接口,第二需要寫一個ADIL聲明文件.
下面創建一個Student類實現Parcelable接口
public class Student implements Parcelable {
public Student(String name) {
this.name = name;
}
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
protected Student(Parcel in) {
name = in.readString();
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(name);
}
@Override
public int describeContents() {
return 0;
}
public static final Creator<Student> CREATOR = new Creator<Student>() {
@Override
public Student createFromParcel(Parcel in) {
return new Student(in);
}
@Override
public Student[] newArray(int size) {
return new Student[size];
}
};
}
接下來新建一個aidl文件,名稱爲我們自定義類型的名稱,這邊是Student.aidl。在Student.aidl申明我們的自定義類型和它的完整包名,注意這邊parcelable是小寫的,不是Parcelable接口,一個自定類型需要一個這樣同名的AIDL文件來聲明。
package com.zly.www.test1;
parcelable Student;
然後在我們的aidl接口中導入自定義數據類型
syncProjcet,然後在service中實現接口方法
class MyBinder extends IMyName.Stub{
@Override
public String getName() throws RemoteException {
return "帥也是錯嗎";
}
@Override
public Student getStudent() throws RemoteException {
return new Student("朱利源");
}
}
然後把AIDL文件與自定義類型文件拷到第二個項目,注意這裏還是包名一致
接下來就可以在該activity中使用自定義類型的數據了
public class MainActivity extends AppCompatActivity {
//.....
public void get(View v) {
try {
Toast.makeText(this, iMyName.getStudent().getName(), Toast.LENGTH_SHORT).show();
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
效果如下