編輯:關於Android編程
現在總結一下這個用法:
其實很簡單,主要就是以下的幾個步驟:
1.第一步,定義aidl文件(可以參照IWifiManager.java的定義來實現,其實就是定義一個Interface,提供被調用的方法);
例如 (IMyService.aidl):
package com.demo;
import com.demo.Person;
interface IMyService {
void savePersonInfo(in Person person);//注意,這裡的Person類不是基本數據類型哦,要進行處理的,見下文
List<Person> getAllPerson();
}
2.第二步,寫一個service,實現stub類(也就是實現剛才的那些接口),return stub類的對象(也就是binder);
創建一個類實現剛才那個aidl的接口:
public class RemoteService extends Service {
private LinkedList<Person> personList = new LinkedList<Person>();
@Override
public IBinder onBind(Intent intent) { //activity
return mBinder;
}
private final IMyService.Stub mBinder = new IMyService.Stub(){
@Override
public void savePersonInfo(Person person) throws RemoteException {
if (person != null){
personList.add(person);
}
}
@Override
public List<Person> getAllPerson() throws RemoteException {
return personList;
}
};
}
注:
這裡會看到有一個名為IMyService.Stub類,查看aidl文件生成的Java文件源代碼就能發現有這麼一段代碼:
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements com.demo.IMyService
原來Stub類就是繼承於Binder類,也就是說RemoteService類和普通的Service類沒什麼不同,只是所返回的IBinder對象比較特別,是一個實現了AIDL接口的Binder。
3.第三步,在activity裡面啟動service。
通過IMyService.Stub.asInterface(service)來得到IMyService對象:
private IMyService mRemoteService;
private ServiceConnection mRemoteConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
mRemoteService = IMyService.Stub.asInterface(service);
}
public void onServiceDisconnected(ComponentName className) {
mRemoteService = null;
}
};
注:
在生成的IMyService.java裡面會找到這樣的代碼:
/**
* Cast an IBinder object into an com.demo.IMyService interface,
* generating a proxy if needed.
*/
public static com.demo.IMyService asInterface(android.os.IBinder obj) {...}
而service的綁定沒有什麼不同:
if (mIsRemoteBound) {
unbindService(mRemoteConnection);
}else{
bindService(new Intent("com.demo.IMyService"),
mRemoteConnection, Context.BIND_AUTO_CREATE);
}
mIsRemoteBound = !mIsRemoteBound;
通過IPC調用/傳遞數據
客戶端綁定service後就能通過IPC來調用/傳遞數據了,直接調用service對象的接口方法:
addPersonButton.setOnClickListener(
new View.OnClickListener(){
private int index = 0;
@Override
public void onClick(View view) {
Person person = new Person();
index = index + 1;
person.setName("Person" + index);
person.setAge(20);
person.setTelNumber("123456");
try {
mRemoteService.savePersonInfo(person);
} catch (RemoteException e) {
e.printStackTrace();
}
}
});
listPersonButton.setOnClickListener(
new View.OnClickListener(){
@Override
public void onClick(View view) {
List<Person> list = null;
try {
list = mRemoteService.getAllPerson();
} catch (RemoteException e) {
e.printStackTrace();
}
if (list != null){
StringBuilder text = new StringBuilder();
for(Person person : list){
text.append("\nPerson name:");
text.append(person.getName());
text.append("\n age :");
text.append(person.getAge());
text.append("\n tel number:");
text.append(person.getTelNumber());
}
inputPersonEdit.setText(text);
}else {
Toast.makeText(ServiceActivity.this, "get data error",
Toast.LENGTH_SHORT).show();
}
}
});
另外關於第一步,附加的說一下。當aidl中的接口中的返回值或參數不是基本數據類型時,需要做如下的兩步:
1.給一個aidle的實現:
package com.demo;
parcelable Person;
2.給一個序列化(實現java類)
public class Person implements Parcelable {
private String name;
private String telNumber;
private int age;
public Person() {}
public Person(Parcel pl){
name = pl.readString();
telNumber = pl.readString();
age = pl.readInt();
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getTelNumber() {
return telNumber;
}
public void setTelNumber(String telNumber) {
this.telNumber = telNumber;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(name);
dest.writeString(telNumber);
dest.writeInt(age);
}
public static final Parcelable.Creator<Person> CREATOR = new Parcelable.Creator<Person>() {
@Override
public Person createFromParcel(Parcel source) {
return new Person(source);
}
@Override
public Person[] newArray(int size) {
return new Person[size];
}
};
}
Parcelable需要實現三個函數:
1) void writeToParcel(Parcel dest, int flags) 將需要序列化存儲的數據寫入外部提供的Parcel對象dest。而看了網上的代碼例子,個人猜測,讀取Parcel數據的次序要和這裡的write次序一致,否則可能會讀錯數據。具體情況我沒試驗過!
2) describeContents() 沒搞懂有什麼用,反正直接返回0也可以
3) static final Parcelable.Creator對象CREATOR 這個CREATOR命名是固定的,而它對應的接口有兩個方法:
createFromParcel(Parcel source) 實現從source創建出JavaBean實例的功能
newArray(int size) 創建一個類型為T,長度為size的數組,僅一句話(return new T[size])即可。估計本方法是供外部類反序列化本類數組使用。
南周知道 要嚴肅,有知識;要八卦,有內幕。每天一篇,盡享你想知道的和不知道的,我們只想,讓知道成為一種享受。請記住知道,南方周末每日網絡專稿。 1.每天推送一篇你想知道的
這兩天學習了使用Path繪制貝塞爾曲線相關,然後自己動手做了一個類似QQ未讀消息可拖拽的小氣泡,效果圖如下:接下來一步一步的實現整個過程。基本原理其實就是使用Path繪制
Google在2015的IO大會上,給我們帶來了更加詳細的Material Design設計規范,同時,也給我們帶來了全新的Android Design Support
自定義TextView控件TimeTextView代碼:復制代碼 代碼如下:import android.content.Context;import android.c