Android—Binder+AIDL
Binder?
Binder機制優點:
- 只需要進行一次數據拷貝,性能上僅次于共享內存。
- 基于C/S架構,職責明確,架構清晰,穩定性較好。
- 安全性好,為每個App分配UID,UID是鑒別進程身份的標志。
內存映射:(一次copy的原因)
Binder IPC 機制中涉及到的內存映射通過 mmap() 來實現,mmap() 是操作系統中一種內存映射的方法。內存映射簡單的講就是將用戶空間的一塊內存區域映射到內核空間。映射關系建立后,用戶對這塊內存區域的修改可以直接反應到內核空間;反之內核空間對這段區域的修改也能直接反應到用戶空間。
Binder IPC 通信過程:
- 首先 Binder 驅動在內核空間創建一個數據接收緩存區;
- 接著在內核空間開辟一塊內核緩存區,建立內核緩存區和內核中數據接收緩存區之間的映射關系,以及內核中數據接收緩存區和接收進程用戶空間地址的映射關系;
- 發送方進程通過系統調用 copy_from_user() 將數據 copy 到內核中的內核緩存區,內核緩存區映射到接收區,接收區又映射到用戶空間地址,因此也就相當于把數據發送到了接收進程的用戶空間,這樣便完成了一次進程間的通信。
Binder通訊模型:
Binder是基于C/S架構的,包含4個角色:Client、Server、Binder驅動和ServiceManager。
- Binder驅動:類似網絡通信中的路由器,負責將Client的請求轉發到具體的Server中執行,并將Server返回的數據傳回給Client。
- ServiceManager:類似網絡通信中的DNS服務器,負責將Client請求的Binder描述符轉化為具體的Server地址,以便Binder驅動能夠轉發給具體的Server。Server如需提供Binder服務,需要向ServiceManager注冊。
通信過程:
Client和Server通信:
Client要和Server通信,它就是通過保存一個Server對象的Binder引用,再通過該Binder引用在內核中找到對應的Binder實體,進而找到Server對象,然后將通信內容發送給Server對象。
Client進程將需要傳送的數據寫入到Parcel對象中調用BinderProxy的transact()將上述數據發送到Binder驅動(通過BpBinder)Binder驅動找到Binder引用對應的Binder實體,通過Binder實體找到用戶空間的Server對象,Server收到Binder驅動通知后,Server 進程通過回調Binder對象onTransact()進行數據解包和調用目標方法,Binder驅動根據代理對象沿原路將結果返回并通知Client進程獲取返回結果,喚醒Client線程,接收結果。
AIDL? Android Interface Definition Language(Android接口定義語言)
AIDL是基于Binder的,作用是實現進程間的通信。如果需要操作非基礎類型的數據,需要序列化。
首先是定義一個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類還需要定義一個AIDL文件
// Person.aidl package com.example.mylibrary;// Declare any non-default types here with import statementsparcelable Person;接下來創建自己的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); }關于參數前的in、out和inout,跨進程時,in參數會把參數的內容傳給aidl,但其改動不會同步到調用進程;out參數不會把參數的屬性傳給aidl(aidl獲取的參數對象屬性為空),但其改動會同步到調用進程;inout參數則是in和out的綜合。不跨進程時,三者則是擺設。
上面一定要導入Person類的正確地址,不然aidl生成對應的java找不到。同步一下。
3、有了接口文件,我們需要定義一個服務類,實現接口方法,在onBind返回實例。
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 // 參數為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","有連接請求");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 該屬性用來標示,其它應用的組件是否可以喚醒service或者和這個service進行交互:true可以,false不可以。如果為false,只有同一個應用的組件或者有著同樣user ID的應用可以啟動這個service或者綁定這個service。
android:process=":remote"? 讓服務在指定進程名中啟動,這里選擇”remote”這個名字是隨意主觀的,你能用其他名字來讓這個服務在另外的進程中運行。冒號’:’這個前綴將把這個名字附加到你的包所運行的標準進程名字的后面作為新的進程名稱。
現在的結構是這樣的。?
?5、在主app中使用這個服務
前提:app已經依賴了mylibrary這個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,必須經過這個方法才能還原成Stub類對象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() + "已經斷開連接")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}}}結果:
服務進程:
app進程:
我們還沒測試過out,我們把AIDL文件的
Person updatePerson(inout Person person); 改為Person updatePerson(out Person person);同步,再運行。
看到updatePerson(out Person person);改變不了服務類的數據了。
但是傳到服務進程的值并不為空。
分析一下? 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同樣實現了我們定義的功能接口,而且包含一個BinderProxy對象,當我們在Client進程中調用我們所定義的功能方法時,其實就是調用Stub.Proxy中實現的方法。?在實現該功能方法時,它首先將參數序列化,然后調用BinderProxy的transact()方法,調用該方法以后,Binder驅動會喚醒Server進程中的本地Binder對象, 并調用它的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,是一個整型,也就是說在Binder中對每一個方法都進行了編號,在transact()方法中傳入編號,然后在onTransact()方法中,根據請求的變化調用相應的方法。這里我們看到data接收參數,然后調用本地Binder中定義的功能方法,這里是抽象方法,留有子類實現,最后將結果寫入到_reply中,由Binder驅動負責將返回值傳遞到BinderProxy的transact()方法中的_reply。
Service接口方法調用流程小結
文章推薦:
https://blog.csdn.net/weixin_44339238/article/details/110942282
總結
以上是生活随笔為你收集整理的Android—Binder+AIDL的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 怎样洗头使头发变黑变多
- 下一篇: MPEG-4 AVC/H.264 信息