AIDL是什么
AIDL,即Android Interface Definition Language(Android接口定義語音),提到AIDL,不得不先引入Android系統中的Binder,Binder是Android跨進程通信的一種方式,眾所周知,Android系統是基于linux內核,系統會為每一個應用分配一個單獨的虛擬機,每個應用擁有自己獨立的進程,當然,一個應用也可以有多個進程,如果我們的應用需要實現進程間通信,Android并沒有采用linux已有的通信方式,如Socket、管道等,而是采用一種了新的通信方式Binder,Binder在平時應用層開發可能用到的不多,但是如果查看Android系統源碼就會發現,Android系統中很多地方用到了Binder,它是ServiceManager連接各種Manager(ActivityManager,WindowManager等)與其相對應的ManagerService的橋梁,對于應用層來說,通過Binder,客戶端與服務端可以進行跨進程通信,客戶端可以訪問服務端提供的方法或數據,AIDL正是為了定義客戶端與服務端進程間通信時相互都認可的接口,當我們創建AIDL接口時,Android SDK會為我們自動生成對應的binder類,通過這個binder可以實現進程間通信。
如何使用AIDL
AIDL基本使用有三個步驟:
- 創建一個Service和AIDL文件
- 實現接口
- 向客戶端公開接口
需要注意的是,AIDL支持下列數據類型:
- 基本數據類型(int long double char 等等)
- CharSequence
- String
- List
- Map
- Parcelable
這里選擇用自定義對象來作為數據傳遞,首先我們新建一個Person類并且實現Parcelable接口。需要注意的是,如果需要通過ADIL進行復雜對象傳遞的話,該對象必須實現Parcelable接口
package com.zx.aidl.demo;
import android.os.Parcel;
import android.os.Parcelable;
public class Person implements Parcelable {
private int id;
private String name;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override public int describeContents() {
return 0;
}
@Override public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(this.id);
dest.writeString(this.name);
}
public Person() {
}
protected Person(Parcel in) {
this.id = in.readInt();
this.name = in.readString();
}
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];
}
};
}
接下來分別創建Person.aidl、IRemoteService.aidl和Service
Person.aidl
package com.zx.aidl.demo;
parcelable Person;
IRemoteService.aidl
package com.zx.aidl.demo;
import com.zx.aidl.demo.Person;
interface IRemoteService {
void addPerson(in Person person);
List<Person> getPersons();
}
IRemoteService 這個接口中定義了倆個方法,需要注意的是AIDL接口中,只能定義方法,并不支持定義靜態常量,當我們在AIDL文件中引用到其他自定義類型時,需要創建一個與該類同名的AIDL文件,如上面的Person.aidl ,并在文件中聲明該類為parcelable,這里的parcelable首字母是小寫,同時需要在引用該類的AIDL接口中import它的包名,接下來創建服務端的Service
package com.zx.aidl.demo;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.annotation.Nullable;
import android.util.Log;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
public class RemoteService extends Service {
private static final String TAG = RemoteService.class.getSimpleName();
private CopyOnWriteArrayList<Person> persons = new CopyOnWriteArrayList<>();
@Override public void onCreate() {
super.onCreate();
Log.i(TAG, "onCreate() executed");
}
@Override public int onStartCommand(Intent intent, int flags, int startId) {
Log.i(TAG, "onStartCommand() executed");
return super.onStartCommand(intent, flags, startId);
}
@Override public void onDestroy() {
super.onDestroy();
Log.i(TAG, "onDestroy() executed");
}
@Override public boolean onUnbind(Intent intent) {
Log.i(TAG, "onUnbind() executed");
return super.onUnbind(intent);
}
private IRemoteService.Stub binder = new IRemoteService.Stub() {
@Override public void addPerson(Person person) throws RemoteException {
persons.add(person);
}
@Override public List<Person> getPersons() throws RemoteException {
return persons;
}
};
@Nullable @Override public IBinder onBind(Intent intent) {
Log.i(TAG, "onBind() executed");
return binder;
}
}
到這里,已經完成了上面所述的三個步驟;
1.創建AIDL和Service
2.實現接口
private IRemoteService.Stub binder = new IRemoteService.Stub() {
@Override public void addPerson(Person person) throws RemoteException {
persons.add(person);
}
@Override public List<Person> getPersons() throws RemoteException {
return persons;
}
};
3.向客戶端公開接口
@Nullable @Override public IBinder onBind(Intent intent) {
Log.i(TAG, "onBind() executed");
return binder;
}
第二步實現接口的時候發現這么一個類IRemoteService.Stub,我們之前并沒有創建過這個類,那這個類到底從哪來的?
IRemoteService其實是編譯器根據IRemoteService.aidl這個文件為我們自動生成的一個Java類,Stub是IRemoteService的一個內部抽象類
<service android:name=".RemoteService"
android:process=":remote"
>
</service>
</application>
服務端工作已完成,接下來直接上客戶端布局以及Activity代碼
package com.zx.aidl.demo;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.v7.app.AppCompatActivity;
import android.text.TextUtils;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import java.util.List;
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private Button btnBind;
private Button btnUnbind;
private EditText etId;
private EditText etName;
private Button btnAdd;
private Button btnGetPersons;
private TextView tvContent;
private IRemoteService iRemoteService;
private ServiceConnection serviceConnection = new ServiceConnection() {
@Override public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
iRemoteService = IRemoteService.Stub.asInterface(iBinder);
}
@Override public void onServiceDisconnected(ComponentName componentName) {
}
};
@Override protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btnBind = (Button) findViewById(R.id.btn_bind);
btnUnbind = (Button) findViewById(R.id.btn_unbind);
btnAdd = (Button) findViewById(R.id.btn_add);
etId = (EditText) findViewById(R.id.et_id);
etName = (EditText) findViewById(R.id.et_name);
btnGetPersons = (Button) findViewById(R.id.btn_get);
tvContent = (TextView) findViewById(R.id.tv_content);
btnBind.setOnClickListener(this);
btnUnbind.setOnClickListener(this);
btnGetPersons.setOnClickListener(this);
btnAdd.setOnClickListener(this);
}
@Override public void onClick(View view) {
switch (view.getId()) {
case R.id.btn_bind :
bindService(new Intent(this,RemoteService.class),serviceConnection, Context.BIND_AUTO_CREATE);
break;
case R.id.btn_unbind:
unbindService(serviceConnection);
break;
case R.id.btn_add:
addPerson();
break;
case R.id.btn_get :
getPersons();
break;
}
}
private void getPersons() {
try {
List<Person> personList = iRemoteService.getPersons();
if (personList != null) {
StringBuilder sb = new StringBuilder();
for (Person person : personList) {
sb.append("id :").append(person.getId()).append(" ").append("姓名:").append(person.getName()).append("\n");
}
tvContent.setText(sb.toString());
}
} catch (RemoteException e) {
e.printStackTrace();
}
}
private void addPerson() {
String id = etId.getText().toString();
String name = etName.getText().toString();
if (! TextUtils.isEmpty(id) && ! TextUtils.isEmpty(name)) {
try {
Person person = new Person();
person.setId(Integer.parseInt(id));
person.setName(name);
iRemoteService.addPerson(person);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
}
客戶端實現其實很簡單,首先通過bindService方法綁定服務端Service并建立連接,在ServiceConnection 的onServiceConnected方法回調內接受服務端Service onBind方法返回的binder對象并把它轉換為我們需要的
IRemoteService 實例,然后通過IRemoteService實例去調用它本身的addPerson()與getPersons()方法,這樣就達到了客戶端調用服務端的方法功能,其實也就是客戶端與服務端通信,接下來首先看下客戶端bindService()與unBindService()方法時,Service會執行那些生命周期方法。
與startService()方法不同的是,bindService時,Service只執行了onCreate()與onBind()方法,同理,執行unBindService()時,Service執行了與之對應的onUnbind()與onDestroy()方法,所以當我們綁定解綁一個Service時,它的生命周期為:onCreate()--->onBind()--->onUnbind()--->onDestory()
回到正題,看下客戶端與服務端數據傳遞,首先我們在之前已經在服務端RemoteService實現的接口的回調方法里加幾行log日志打印
private IRemoteService.Stub binder = new IRemoteService.Stub() {
@Override public void addPerson(Person person) throws RemoteException {
Log.e(TAG, " addPerson() executed ");
Log.e(TAG,person.getId()+"");
Log.e(TAG,person.getName());
persons.add(person);
}
@Override public List<Person> getPersons() throws RemoteException {
Log.e(TAG, " getPersons() executed ");
if (persons.size() > 0) {
for (Person person : persons) {
Log.e(TAG,person.getId()+"");
Log.e(TAG,person.getName());
}
}
return persons;
}
};
AIDL原理分析