Android—Binder+AIDL
Binder?
Binder機(jī)制優(yōu)點(diǎn):
- 只需要進(jìn)行一次數(shù)據(jù)拷貝,性能上僅次于共享內(nèi)存。
- 基于C/S架構(gòu),職責(zé)明確,架構(gòu)清晰,穩(wěn)定性較好。
- 安全性好,為每個(gè)App分配UID,UID是鑒別進(jìn)程身份的標(biāo)志。
內(nèi)存映射:(一次copy的原因)
Binder IPC 機(jī)制中涉及到的內(nèi)存映射通過(guò) mmap() 來(lái)實(shí)現(xiàn),mmap() 是操作系統(tǒng)中一種內(nèi)存映射的方法。內(nèi)存映射簡(jiǎn)單的講就是將用戶空間的一塊內(nèi)存區(qū)域映射到內(nèi)核空間。映射關(guān)系建立后,用戶對(duì)這塊內(nèi)存區(qū)域的修改可以直接反應(yīng)到內(nèi)核空間;反之內(nèi)核空間對(duì)這段區(qū)域的修改也能直接反應(yīng)到用戶空間。
Binder IPC 通信過(guò)程:
- 首先 Binder 驅(qū)動(dòng)在內(nèi)核空間創(chuàng)建一個(gè)數(shù)據(jù)接收緩存區(qū);
- 接著在內(nèi)核空間開(kāi)辟一塊內(nèi)核緩存區(qū),建立內(nèi)核緩存區(qū)和內(nèi)核中數(shù)據(jù)接收緩存區(qū)之間的映射關(guān)系,以及內(nèi)核中數(shù)據(jù)接收緩存區(qū)和接收進(jìn)程用戶空間地址的映射關(guān)系;
- 發(fā)送方進(jìn)程通過(guò)系統(tǒng)調(diào)用 copy_from_user() 將數(shù)據(jù) copy 到內(nèi)核中的內(nèi)核緩存區(qū),內(nèi)核緩存區(qū)映射到接收區(qū),接收區(qū)又映射到用戶空間地址,因此也就相當(dāng)于把數(shù)據(jù)發(fā)送到了接收進(jìn)程的用戶空間,這樣便完成了一次進(jìn)程間的通信。
Binder通訊模型:
Binder是基于C/S架構(gòu)的,包含4個(gè)角色:Client、Server、Binder驅(qū)動(dòng)和ServiceManager。
- Binder驅(qū)動(dòng):類似網(wǎng)絡(luò)通信中的路由器,負(fù)責(zé)將Client的請(qǐng)求轉(zhuǎn)發(fā)到具體的Server中執(zhí)行,并將Server返回的數(shù)據(jù)傳回給Client。
- ServiceManager:類似網(wǎng)絡(luò)通信中的DNS服務(wù)器,負(fù)責(zé)將Client請(qǐng)求的Binder描述符轉(zhuǎn)化為具體的Server地址,以便Binder驅(qū)動(dòng)能夠轉(zhuǎn)發(fā)給具體的Server。Server如需提供Binder服務(wù),需要向ServiceManager注冊(cè)。
通信過(guò)程:
Client和Server通信:
Client要和Server通信,它就是通過(guò)保存一個(gè)Server對(duì)象的Binder引用,再通過(guò)該Binder引用在內(nèi)核中找到對(duì)應(yīng)的Binder實(shí)體,進(jìn)而找到Server對(duì)象,然后將通信內(nèi)容發(fā)送給Server對(duì)象。
Client進(jìn)程將需要傳送的數(shù)據(jù)寫入到Parcel對(duì)象中調(diào)用BinderProxy的transact()將上述數(shù)據(jù)發(fā)送到Binder驅(qū)動(dòng)(通過(guò)BpBinder)Binder驅(qū)動(dòng)找到Binder引用對(duì)應(yīng)的Binder實(shí)體,通過(guò)Binder實(shí)體找到用戶空間的Server對(duì)象,Server收到Binder驅(qū)動(dòng)通知后,Server 進(jìn)程通過(guò)回調(diào)Binder對(duì)象onTransact()進(jìn)行數(shù)據(jù)解包和調(diào)用目標(biāo)方法,Binder驅(qū)動(dòng)根據(jù)代理對(duì)象沿原路將結(jié)果返回并通知Client進(jìn)程獲取返回結(jié)果,喚醒Client線程,接收結(jié)果。
AIDL? Android Interface Definition Language(Android接口定義語(yǔ)言)
AIDL是基于Binder的,作用是實(shí)現(xiàn)進(jìn)程間的通信。如果需要操作非基礎(chǔ)類型的數(shù)據(jù),需要序列化。
首先是定義一個(gè)Person類繼承Parcelable。
package com.example.mylibraryimport android.os.Parcel import android.os.Parcelableclass Person():Parcelable {var name:String = ""var age:Int = 0constructor(parcel: Parcel):this(){name = parcel.readString().toString()age = parcel.readInt()}constructor(name: String,age: Int):this(){this.name = namethis.age = age}override fun writeToParcel(dest: Parcel?, flags: Int) {dest?.writeString(name)dest?.writeInt(age)}fun readFromParcel(parcel: Parcel): Person? {name = parcel.readString().toString()age = parcel.readInt()return this}override fun describeContents(): Int {return 0}companion object CREATOR : Parcelable.Creator<Person> {override fun createFromParcel(parcel: Parcel): Person {return Person(parcel)}override fun newArray(size: Int): Array<Person?> {return arrayOfNulls(size)}} }要能操作Person類還需要定義一個(gè)AIDL文件
// Person.aidl package com.example.mylibrary;// Declare any non-default types here with import statementsparcelable Person;接下來(lái)創(chuàng)建自己的AIDL文件,然后聲明自己需要的方法。?
// IMyAidlInterface.aidl package com.example.mylibrary; import com.example.mylibrary.Person;// Declare any non-default types here with import statementsinterface IMyAidlInterface {/*** Demonstrates some basic types that you can use as parameters* and return values in AIDL.*/List<Person> getPeople();void addPerson(in Person person);Person updatePerson(inout Person person);Person updatePerson2(inout Person person); }關(guān)于參數(shù)前的in、out和inout,跨進(jìn)程時(shí),in參數(shù)會(huì)把參數(shù)的內(nèi)容傳給aidl,但其改動(dòng)不會(huì)同步到調(diào)用進(jìn)程;out參數(shù)不會(huì)把參數(shù)的屬性傳給aidl(aidl獲取的參數(shù)對(duì)象屬性為空),但其改動(dòng)會(huì)同步到調(diào)用進(jìn)程;inout參數(shù)則是in和out的綜合。不跨進(jìn)程時(shí),三者則是擺設(shè)。
上面一定要導(dǎo)入Person類的正確地址,不然aidl生成對(duì)應(yīng)的java找不到。同步一下。
3、有了接口文件,我們需要定義一個(gè)服務(wù)類,實(shí)現(xiàn)接口方法,在onBind返回實(shí)例。
package com.example.mylibraryimport android.app.Service import android.content.Intent import android.os.IBinder import android.util.Log import kotlin.random.Randomclass PersonService: Service() {var personList:ArrayList<Person> = ArrayList()var random = Random(1)init {val p = Person("AAAA",20)personList.add(p)}private var personManager = object : IMyAidlInterface.Stub(){override fun addPerson(person: Person?) {val isNull = person == null // 參數(shù)為inLog.e("aaa","in person is null--$isNull")if (person != null) {personList.add(person)}}override fun updatePerson(person: Person): Person {personList.set(0,person)return person}override fun getPeople(): MutableList<Person> {return personList}override fun updatePerson2(person: Person): Person {val p1 = Person()p1.age = random.nextInt() % 40p1.name = "mike"personList[1] = p1return p1}}override fun onBind(intent: Intent?): IBinder? {Log.e("bbbbb","有連接請(qǐng)求");Log.e("cccc",intent.toString());return personManager;} }4、在manifest中聲明
<service android:name="com.example.mylibrary.PersonService"android:exported="false"android:process=":remote"><intent-filter><action android:name="com.example.text" /><category android:name="android.intent.category.DEFAULT"/></intent-filter></service>android:exported 該屬性用來(lái)標(biāo)示,其它應(yīng)用的組件是否可以喚醒service或者和這個(gè)service進(jìn)行交互:true可以,false不可以。如果為false,只有同一個(gè)應(yīng)用的組件或者有著同樣user ID的應(yīng)用可以啟動(dòng)這個(gè)service或者綁定這個(gè)service。
android:process=":remote"? 讓服務(wù)在指定進(jìn)程名中啟動(dòng),這里選擇”remote”這個(gè)名字是隨意主觀的,你能用其他名字來(lái)讓這個(gè)服務(wù)在另外的進(jìn)程中運(yùn)行。冒號(hào)’:’這個(gè)前綴將把這個(gè)名字附加到你的包所運(yùn)行的標(biāo)準(zhǔn)進(jìn)程名字的后面作為新的進(jìn)程名稱。
現(xiàn)在的結(jié)構(gòu)是這樣的。?
?5、在主app中使用這個(gè)服務(wù)
前提:app已經(jīng)依賴了mylibrary這個(gè)module
package com.example.textimport 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.util.Log import androidx.appcompat.app.AppCompatActivity import com.example.mylibrary.IMyAidlInterface import com.example.mylibrary.Person import com.example.mylibrary.PersonServiceclass MainActivity : AppCompatActivity() {private var isConnected = falselateinit var peopleManager:IMyAidlInterfaceprivate val p = Person("Dustin", 27)override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_main)}private val connection: ServiceConnection = object : ServiceConnection {override fun onServiceConnected(name: ComponentName, service: IBinder) {peopleManager = IMyAidlInterface.Stub.asInterface(service) // 此處的service,就是Service的onBind()方法返回的Stub,必須經(jīng)過(guò)這個(gè)方法才能還原成Stub類對(duì)象isConnected = trueshow()logger("before add")logger(p.name+"aaaaaaaaaaaaaaaaa")logger("=================")peopleManager.addPerson(p)show()logger("=================")peopleManager.updatePerson(p)logger(p.name+"aaaaaaaaaaaaaaaaa")show()logger("=================") peopleManager.updatePerson2(p)show()}override fun onServiceDisconnected(name: ComponentName) {logger(name.toString() + "已經(jīng)斷開(kāi)連接")isConnected = false}}fun show(){for (i:Int in 0 until peopleManager.people.size){logger(peopleManager.people[i].name)logger(peopleManager.people[i].age.toString())}}private fun logger(info: String) {Log.e("FragmentActivity.TAG", info)}override fun onStop() {super.onStop()tryDisconnectService()}override fun onStart() {super.onStart()tryConnectService()}private fun tryConnectService() {logger("try to connect service")if (!isConnected) {val intent = Intent(this, PersonService::class.java)bindService(intent, connection, Context.BIND_AUTO_CREATE)}}private fun tryDisconnectService() {logger("try to disconnect service")if (isConnected) {unbindService(connection)isConnected = false}}}結(jié)果:
服務(wù)進(jìn)程:
app進(jìn)程:
我們還沒(méi)測(cè)試過(guò)out,我們把AIDL文件的
Person updatePerson(inout Person person); 改為Person updatePerson(out Person person);同步,再運(yùn)行。
看到updatePerson(out Person person);改變不了服務(wù)類的數(shù)據(jù)了。
但是傳到服務(wù)進(jìn)程的值并不為空。
分析一下? mAidlInterface = IMyAidlInterface.Stub.asInterface(service)
public static IMyAidlInterface asInterface(IBinder obj){if ((obj==null)) {return null;}IInterface iin = obj.queryLocalInterface(DESCRIPTOR);if (((iin!=null)&&(iin instanceof IMyAidlInterface))) {return ((IMyAidlInterface)iin);}return new Stub.Proxy(obj); } private static class Proxy implements IMyAidlInterface{private IBinder mRemote;Proxy(IBinder remote){mRemote = remote;}@Override public IBinder asBinder(){return mRemote;}public String getInterfaceDescriptor(){return DESCRIPTOR;}@Override public void myMethod() throws RemoteException{android.os.Parcel _data = android.os.Parcel.obtain();android.os.Parcel _reply = android.os.Parcel.obtain();try {_data.writeInterfaceToken(DESCRIPTOR);mRemote.transact(Stub.TRANSACTION_myMethod, _data, _reply, 0);_reply.readException();}finally {_reply.recycle();_data.recycle();}} }Stub.Proxy同樣實(shí)現(xiàn)了我們定義的功能接口,而且包含一個(gè)BinderProxy對(duì)象,當(dāng)我們?cè)贑lient進(jìn)程中調(diào)用我們所定義的功能方法時(shí),其實(shí)就是調(diào)用Stub.Proxy中實(shí)現(xiàn)的方法。?在實(shí)現(xiàn)該功能方法時(shí),它首先將參數(shù)序列化,然后調(diào)用BinderProxy的transact()方法,調(diào)用該方法以后,Binder驅(qū)動(dòng)會(huì)喚醒Server進(jìn)程中的本地Binder對(duì)象, 并調(diào)用它的onTransact()方法。
@Override public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException{switch (code){case INTERFACE_TRANSACTION:{reply.writeString(DESCRIPTOR);return true;}case TRANSACTION_myMethod:{data.enforceInterface(DESCRIPTOR);this.myMethod();reply.writeNoException();return true;}} return super.onTransact(code, data, reply, flags);}TRANSACTION_myMethod,是一個(gè)整型,也就是說(shuō)在Binder中對(duì)每一個(gè)方法都進(jìn)行了編號(hào),在transact()方法中傳入編號(hào),然后在onTransact()方法中,根據(jù)請(qǐng)求的變化調(diào)用相應(yīng)的方法。這里我們看到data接收參數(shù),然后調(diào)用本地Binder中定義的功能方法,這里是抽象方法,留有子類實(shí)現(xiàn),最后將結(jié)果寫入到_reply中,由Binder驅(qū)動(dòng)負(fù)責(zé)將返回值傳遞到BinderProxy的transact()方法中的_reply。
Service接口方法調(diào)用流程小結(jié)
文章推薦:
https://blog.csdn.net/weixin_44339238/article/details/110942282
總結(jié)
以上是生活随笔為你收集整理的Android—Binder+AIDL的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 怎样洗头使头发变黑变多
- 下一篇: Android—Window、Windo