AIDL是什么
Android 接口定義語言 (AIDL) 與您可能使用過的其他接口語言 (IDL) 類似贤牛。您可以利用它定義客戶端與服務均認可的編程接口六水,以便二者使用進程間通信 (IPC) 進行相互通信。在 Android 中盆耽,一個進程通常無法訪問另一個進程的內存轰绵。因此,為進行通信昌执,進程需將其對象分解成可供操作系統(tǒng)理解的原語烛亦,并將其編組為可供您操作的對象。編寫執(zhí)行該編組操作的代碼較為繁瑣懂拾,因此 Android 會使用 AIDL 為您處理此問題煤禽。
AIDL的用處
Android 接口定義語言 (AIDL) 會將對象分解成原語,操作系統(tǒng)可通過識別這些原語并將其編組到各進程中來執(zhí)行 IPC委粉。對于之前采用 Messenger 的方法而言呜师,其實際上是以 AIDL 作為其底層結構。如上所述贾节,Messenger 會在單個線程中創(chuàng)建包含所有客戶端請求的隊列汁汗,以便服務一次接收一個請求。不過栗涂,如果您想讓服務同時處理多個請求知牌,則可直接使用 AIDL。在此情況下斤程,您的服務必須達到線程安全的要求角寸,并且能夠進行多線程處理菩混。
手寫AIDL
1.定義binder服務端所提供的功能接口。定義服務端的功能為提供人員的增加與刪除扁藕,并提供所有人員列表()沮峡。
/**
* 這個類用來定義服務端具有什么樣的能力,繼承自IInterface才就有跨進程傳輸?shù)幕A能力
/**
* Base class for Binder interfaces. When defining a new interface,
* you must derive it from IInterface. Iinterface的說明
*/
*/
public interface PersonManager extends IInterface {
/**
* 添加人數(shù)
*
* @throws RemoteException
*/
void addPerson(PersonBean personBean) throws RemoteException;
/**
* 刪除人數(shù)
*
* @throws RemoteException
*/
void deletePerson(PersonBean personBean) throws RemoteException;
/**
* 獲取人數(shù)
*
* @throws RemoteException
*/
List<PersonBean> getPersons() throws RemoteException;
}
PersonBean 是人員信息的model亿柑,我只寫了一個name屬性邢疙,跨進程傳輸則必須實現(xiàn)Parcelable相關接口。為了后面的刪除功能還需要重寫equle和hashcode方法望薄。
/**
* 實現(xiàn)了parcelable接口的實體類疟游,可用于跨進程傳輸
*/
class PersonBean() : Parcelable {
var name: String? = null;
constructor(parcel: Parcel) : this() {
name = parcel.readString()
}
override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeString(name)
}
override fun describeContents(): Int {
return 0
}
override fun toString(): String {
return "PersonBean(name=$name)"
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as PersonBean
if (name != other.name) return false
return true
}
override fun hashCode(): Int {
return name?.hashCode() ?: 0
}
companion object CREATOR : Parcelable.Creator<PersonBean> {
override fun createFromParcel(parcel: Parcel): PersonBean {
return PersonBean(parcel)
}
override fun newArray(size: Int): Array<PersonBean?> {
return arrayOfNulls(size)
}
}
}
2.添加proxy代理類。我的理解是模擬服務端的所有功能痕支,讓客戶端感受不到調用的是遠程還是本地的服務功能颁虐。可以看見PersonManagerProxy實現(xiàn)了PersonManager
接口卧须,所以就實現(xiàn)了相應的方法另绩,而在構造方法中傳入了IBinder,實際上的跨進程傳輸?shù)膶嶓w故慈。addPerson板熊,deletePerson,getPersons察绷,三個方法中可以看見實際調用的方法是IBinder的transact方法干签。PersonManagerProxy只是進行了一些參數(shù)方法的封裝。
/**
* personmanager/binder在本地的遠程代理類拆撼。
*/
public class PersonManagerProxy implements PersonManager {
/**
* 遠程binder對象
*/
IBinder remote;
private static final String DESCRIPTOR = "com.study.stydyfirst.server.PersonManager";
/**
* 構造方法傳入ibinder
*
* @param remote
*/
public PersonManagerProxy(IBinder remote) {
this.remote = remote;
}
public String getInterfaceDescriptor() {
return DESCRIPTOR;
}
@Override
public void addPerson(PersonBean personBean) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel replay = Parcel.obtain();
try {
data.writeInterfaceToken(DESCRIPTOR);
if (personBean != null) {
data.writeInt(1);
personBean.writeToParcel(data, 0);
} else {
data.writeInt(0);
}
remote.transact(PersonManagerStub.TRANSAVTION_addperson, data, replay, 0);
replay.readException();
} finally {
replay.recycle();
data.recycle();
}
}
@Override
public void deletePerson(PersonBean personBean) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel replay = Parcel.obtain();
try {
data.writeInterfaceToken(DESCRIPTOR);
if (personBean != null) {
data.writeInt(1);
personBean.writeToParcel(data, 0);
} else {
data.writeInt(0);
}
remote.transact(PersonManagerStub.TRANSAVTION_deleteperson, data, replay, 0);
replay.readException();
} finally {
replay.recycle();
data.recycle();
}
}
@Override
public List<PersonBean> getPersons() throws RemoteException {
Parcel data = Parcel.obtain();
Parcel replay = Parcel.obtain();
List<PersonBean> result;
try {
data.writeInterfaceToken(DESCRIPTOR);
remote.transact(PersonManagerStub.TRANSAVTION_getpersons, data, replay, 0);
replay.readException();
result = replay.createTypedArrayList(PersonBean.CREATOR);
} finally {
replay.recycle();
data.recycle();
}
return result;
}
@Override
public IBinder asBinder() {
return remote;
}
}
3.添加PersonManagerStub類容劳。跨進程傳輸?shù)腂inder對象闸度。從代碼中可以看見PersonManagerStub 繼承自Binder竭贩,實現(xiàn)了PersonManager 接口的方法。說明:
- 說明他就是一個binder類莺禁,具有跨進程傳輸?shù)哪芰?/li>
- 他的實現(xiàn)類必須實現(xiàn)我們定義的遠程服務端具備的添加留量,刪除,列表獲取等能力
繼續(xù)分析下PersonManagerStub 的其他代碼哟冬, - 構造方法中 this.attachInterface(this, DESCRIPTOR);的作用就是向BinderService注冊Binder服務楼熄。只有注冊了binder,客戶端才能查詢到有這個binder對象浩峡,并使用它可岂。
- asInterface方法,將一個binder對象轉換為PersonManager 這個我們定義的接口翰灾。通過binder.queryLocalInterface(DESCRIPTOR);方法缕粹,判斷是否返回代理對象稚茅。
- onTransact方法。就是在剛才PersonManagerProxy類中調用的方法就到這了平斩。將客戶端傳輸?shù)臄?shù)據(jù)在這里進行實際處理并通過跨進程傳輸?shù)竭h程service那里
/**
* 繼承自binder實現(xiàn)了personmanager的方法亚享,說明它可以跨進程傳輸,并可進行服務端相關的數(shù)據(jù)操作
*/
public abstract class PersonManagerStub extends Binder implements PersonManager {
private static final String DESCRIPTOR = "com.study.stydyfirst.server.PersonManager";
public PersonManagerStub() {
this.attachInterface(this, DESCRIPTOR);
}
public static PersonManager asInterface(IBinder binder) {
if (binder == null)
return null;
IInterface iin = binder.queryLocalInterface(DESCRIPTOR);//通過DESCRIPTOR查詢本地binder绘面,如果存在則說明調用方和service在同一進程間虹蒋,直接本地調用
if (iin != null && iin instanceof PersonManager)
return (PersonManager) iin;
return new PersonManagerProxy(binder);//本地沒有,返回一個遠程代理對象
}
@Override
public IBinder asBinder() {
return this;
}
@Override
protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
switch (code) {
case INTERFACE_TRANSACTION:
reply.writeString(DESCRIPTOR);
return true;
case TRANSAVTION_getpersons:
data.enforceInterface(DESCRIPTOR);
List<PersonBean> result = this.getPersons();
reply.writeNoException();
reply.writeTypedList(result);
return true;
case TRANSAVTION_addperson:
data.enforceInterface(DESCRIPTOR);
PersonBean arg0 = null;
if (data.readInt() != 0) {
arg0 = PersonBean.CREATOR.createFromParcel(data);
}
this.addPerson(arg0);
reply.writeNoException();
return true;
case TRANSAVTION_deleteperson:
data.enforceInterface(DESCRIPTOR);
PersonBean personBean = null;
if (data.readInt() != 0) {
personBean = PersonBean.CREATOR.createFromParcel(data);
}
this.deletePerson(personBean);
reply.writeNoException();
return true;
}
return super.onTransact(code, data, reply, flags);
}
public static final int TRANSAVTION_getpersons = IBinder.FIRST_CALL_TRANSACTION;
public static final int TRANSAVTION_addperson = IBinder.FIRST_CALL_TRANSACTION + 1;
public static final int TRANSAVTION_deleteperson = IBinder.FIRST_CALL_TRANSACTION + 2;
}
AIDL的相關類已經編寫完畢飒货,下面只需要遠程service實現(xiàn)相應方法,客戶端調用峭竣,就能測試該手寫AIDL的跨進程傳輸能力了
遠程進程service類塘辅,這里使用前臺服務通知來顯示人員信息,便于直觀查看跨進程傳輸結果皆撩,可以看見我們在service中實例化了一個PersonManagerStub對象扣墩,并實現(xiàn)了相關方法。并通過notification顯示personss的數(shù)據(jù)信息扛吞,并通過service的onBind方法將binder對象傳輸給客戶端
/**
* 遠程service
*/
class PersonManagerService : Service() {
/**
* 創(chuàng)建一個數(shù)組用于管理人員 var 聲明可變變量呻惕,val聲明不可變變量 相當于final
*/
private var personss: ArrayList<PersonBean> = ArrayList();
val CHANNEL_ID = "personmanager";
val notificationId = 1;
override fun onCreate() {
super.onCreate()
createNotificationChannel()
//初始化通知欄,
showNotification()
}
/**
* 將當前人數(shù)顯示通過通知欄顯示出來
*/
fun showNotification() {
var builder = NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle("0fdg")
.setStyle(
NotificationCompat.BigTextStyle()
.bigText("當前的人數(shù)是:" + personss.size + "人員信息:" + personss.toString())
)
.setSmallIcon(R.mipmap.ic_launcher)//不設置smallicon滥比,文字信息不生效亚脆,我服了
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
with(NotificationManagerCompat.from(this)) {
// notificationId is a unique int for each notification that you must define
//notify(notificationId, builder.build())
startForeground(notificationId, builder.build())
}
}
/**
* 首先創(chuàng)建通知渠道,再進行通知顯示
*/
private fun createNotificationChannel() {
// Create the NotificationChannel, but only on API 26+ because
// the NotificationChannel class is new and not in the support library
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val name = getString(R.string.channel_name)
val descriptionText = getString(R.string.channel_description)
val importance = NotificationManager.IMPORTANCE_DEFAULT
val channel = NotificationChannel(CHANNEL_ID, name, importance).apply {
description = descriptionText
}
// Register the channel with the system
val notificationManager: NotificationManager =
getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
notificationManager.createNotificationChannel(channel)
}
}
override fun onBind(intent: Intent): IBinder {
// TODO("Return the communication channel to the service.")
return binder
}
private val binder = object : PersonManagerStub() {
override fun addPerson(personBean: PersonBean?) {
if (personBean != null) {
personss.add(personBean)
Log.d("Server", "添加" + personBean.name)
showNotification()
};
}
override fun deletePerson(personBean: PersonBean?) {
personss.remove(personBean)
Log.d("Server", "刪除");
showNotification()
}
override fun getPersons(): List<PersonBean> {
Log.d("Server", "刪除")
return persons;
}
}
}
本地clientActivity,簡單service啟動與遠程建立鏈接盲泛,并傳輸數(shù)據(jù)濒持,本地通過PersonManagerStub.asInterface(service)方法直接將binder對象轉換為PersonManager即可直接調用遠程方法進行數(shù)據(jù)傳輸
/**
* 本地client
*/
class ClientActivity : AppCompatActivity() {
/** The primary interface we will be calling on the service. */
private var mService: PersonManager? = null
/**
* 是否綁定的標志
*/
private var isBound = false
/**
* Class for interacting with the main interface of the service.
*/
private val mConnection = object : ServiceConnection {
override fun onServiceConnected(className: ComponentName, service: IBinder) {
// This is called when the connection with the service has been
// established, giving us the service object we can use to
// interact with the service. We are communicating with our
// service through an IDL interface, so get a client-side
// representation of that from the raw service object.
Log.d("client", "鏈接成功====");
mService = PersonManagerStub.asInterface(service)//將service轉成遠程服務代理對象,并調用他的方法
isBound = true
}
override fun onServiceDisconnected(className: ComponentName) {
// This is called when the connection with the service has been
// unexpectedly disconnected -- that is, its process crashed.
mService = null
isBound = false
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_client)
// Bind to LocalService
addListener()
}
override fun onDestroy() {
super.onDestroy()
if (isBound) {
unbindService(mConnection)
}
}
private fun addListener() {
btn_add.setOnClickListener {
var person = PersonBean()
person.name = "劉德華"
mService?.addPerson(person)
}
btn_delete.setOnClickListener {
var person = PersonBean()
person.name = "劉德華"
mService?.deletePerson(person)
}
btn_bind.setOnClickListener {
Toast.makeText(baseContext, "綁定服務", Toast.LENGTH_SHORT).show();
Intent(this, PersonManagerService::class.java).also { intent ->
bindService(intent, mConnection, Context.BIND_AUTO_CREATE)
//startService(intent)
}
}
}
}
以上就是java層面的client-binder-servi寺滚,跨進程的一個傳輸流程柑营。并通過手寫方式實現(xiàn)了AIDL的所有功能。 項目demo地址https://github.com/92123748/AIDLTest