網(wǎng)站首頁 編程語言 正文
前言
眾所周知,Android進程間通信采用的是Binder機制。Binder是Android系統(tǒng) 獨有的進程間通信方式,它是采用mmp函數(shù)將進程的用戶空間與內(nèi)核空間的一塊內(nèi)存區(qū)域進行映射,免去了一次數(shù)據(jù)拷貝,相比Linux上的傳統(tǒng)IPC具有高效、安全的優(yōu)點。本文結(jié)合AIDL與bindService函數(shù),在Android體系的應(yīng)用層和Framework層,對Binder通信進行深入剖析,以加深對Binder的了解。
AIDL
AIDL是Android接口描述語言,它也是一個工具,能幫助我們自動生成進程間通信的代碼,省去了很多工作。既然它是一個工具,其實也不是必需的。筆者結(jié)合AIDL生成的代碼進行剖析,分析它生成的代碼是如何進程IPC通信的。
AIDL示例
服務(wù)端
創(chuàng)建PersonController.aidl
文件:
在src目錄包名com.devnn.libservice
上右鍵點擊創(chuàng)建AIDL文件并全名為PersonController
,就會在aidl目錄下創(chuàng)建同名的aidl文件。
// PersonController.aidl package com.devnn.libservice; import com.devnn.libservice.Person; // Declare any non-default types here with import statements interface PersonController { /** * Demonstrates some basic types that you can use as parameters * and return values in AIDL. */ List<Person> getPersons(); void addPerson(inout Person person); }
形參上的inout修飾表示數(shù)據(jù)可以發(fā)送到服務(wù)端進程,而且服務(wù)端進程對數(shù)據(jù)的修改,也會同步到客戶端進程。還有另外兩個方式in和out,表示單向的傳輸。當使用in時,服務(wù)端對person對象的修改,客戶端是無法感知的。當使用out時,客戶端發(fā)送過去的字段是空的,返回的數(shù)據(jù)是服務(wù)端的。
創(chuàng)建Person.aidl
文件:
// Person.aidl package com.devnn.libservice; // Declare any non-default types here with import statements parcelable Person;
創(chuàng)建Person.java
文件,Person類需要實現(xiàn)Parcelable
接口:
package com.devnn.libservice import android.os.Parcel import android.os.Parcelable class Person(var name: String?, var age: Int) : Parcelable { constructor(parcel: Parcel) : this(parcel.readString(), parcel.readInt()) { } /** * 字段寫入順序和讀取順序要保持一致 */ override fun writeToParcel(parcel: Parcel, flags: Int) { parcel.writeString(name) parcel.writeInt(age) } /** * readFromParcel不是必需的,在aidl文件中函數(shù)參數(shù)類型是inout時需要。 */ fun readFromParcel(parcel: Parcel) { name = parcel.readString() age = parcel.readInt() } /** * 默認即可 */ 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) } } }
創(chuàng)建MyService類繼承Service類:
package com.devnn.libservice import android.app.Application import android.app.Service import android.content.Intent import android.os.Binder import android.os.IBinder import android.os.Build import android.util.Log import java.util.ArrayList class MyService : Service() { override fun onCreate() { super.onCreate() Log.d("MyService", "onCreate") /** * 為了證明是在新進程中運行,將進程名打印出來 * 在android 33設(shè)備上運行的。 */ if (Build.VERSION.SDK_INT >= 28) { val processName = Application.getProcessName() Log.d("MyService", "processName=$processName") } } override fun onBind(intent: Intent): IBinder? { Log.d("MyService", "onBind") return binder } private val binder: Binder = object : PersonController.Stub() { override fun getPersons(): List<Person> { Log.d("MyService", "getPersons") val list: MutableList<Person> = ArrayList() list.add(Person("張三", 20)) list.add(Person("李四", 21)) return list } override fun addPerson(person: Person) { Log.d("MyService", "addPerson") Log.d("MyService", "name=${person.name},age=${person.age}") } } }
為了讓MyService
在獨立進程運行,在Manifest聲明時需要注明是新進程:
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.devnn.libservice"> <application> <service android:name=".MyService" android:process="com.devnn.libservice.MyService"></service> </application> </manifest>
android:process=“com.devnn.libservice.MyService” 表示是獨立進程,用冒號開頭就示是子進程或叫私有進程,不用冒號開頭表示是獨立進程。注意進程名中間不能用冒號,比如這種就不行:com.devnn.xxx:MyService。
以上代碼是在單獨的libservice module
中編寫的,結(jié)構(gòu)如下:
客戶端
在app
module中創(chuàng)建客戶端代碼,命名為ClientActivity
,只有三個按鈕,id分別btn1
、btn2
、btn3
,功能分別對應(yīng)bindService
、getPersons
、addPersons
,代碼如下:
package com.devnn.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.util.Log import androidx.appcompat.app.AppCompatActivity import com.devnn.libservice.Person import com.devnn.libservice.PersonController import com.devnn.demo.databinding.ActivityClientBinding class ClientActivity : AppCompatActivity() { private val binding by lazy { ActivityClientBinding.inflate(this.layoutInflater) } private lateinit var personController: PersonController override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(binding.root) //bindService binding.btn1.setOnClickListener { val intent = Intent().apply { component = ComponentName(this@ClientActivity.packageName, "com.devnn.libservice.MyService") } bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE) } //getPersons binding.btn2.setOnClickListener { personController?.let { val list = it.persons; list?.map { Log.i("ClientActivity", "person:name=${it.name},age=${it.age}") } } } //addPerson binding.btn3.setOnClickListener { personController?.let { val person = Person("王五", 22) it.addPerson(person) } } } private val serviceConnection = object : ServiceConnection { override fun onServiceConnected(name: ComponentName?, service: IBinder?) { Log.i("ClientActivity", "onServiceConnected") personController = PersonController.Stub.asInterface(service) } override fun onServiceDisconnected(name: ComponentName?) { Log.i("ClientActivity", "onServiceDisconnected") } } }
運行界面如下:
客戶端與服務(wù)端進程的aidl文件要保持一致,可以將服務(wù)端的aidl拷貝到客戶端。如果客戶端在單獨module,并且依賴了service所在的module,也可以不用拷貝aidl。
運行日志
運行后,點擊bindService按鈕輸出日志如下:
在Logcat查看進程列表也出現(xiàn)了2個:
點擊getPersons按鈕,輸出日志如下:
可以看到,客戶端進程可以正常獲取服務(wù)端進程的數(shù)據(jù)。
點擊addPerson按鈕,輸出日志如下:
可以看到,服務(wù)端進程可以正常接收客戶端進程的發(fā)來的數(shù)據(jù)。
以上是AIDL使用的一個例子,下面分析AIDI通信的過程。
AIDL通信過程分析
項目build后就會自動在build目錄下生成對應(yīng)的Java代碼:
PersonController.java代碼如下:
/* * This file is auto-generated. DO NOT MODIFY. */ package com.devnn.libservice; // Declare any non-default types here with import statements public interface PersonController extends android.os.IInterface { /** Default implementation for PersonController. */ public static class Default implements com.devnn.libservice.PersonController { /** * Demonstrates some basic types that you can use as parameters * and return values in AIDL. */ @Override public java.util.List<com.devnn.libservice.Person> getPersons() throws android.os.RemoteException { return null; } @Override public void addPerson(com.devnn.libservice.Person person) throws android.os.RemoteException { } @Override public android.os.IBinder asBinder() { return null; } } /** Local-side IPC implementation stub class. */ public static abstract class Stub extends android.os.Binder implements com.devnn.libservice.PersonController { private static final java.lang.String DESCRIPTOR = "com.devnn.libservice.PersonController"; /** Construct the stub at attach it to the interface. */ public Stub() { this.attachInterface(this, DESCRIPTOR); } /** * Cast an IBinder object into an com.devnn.libservice.PersonController interface, * generating a proxy if needed. */ public static com.devnn.libservice.PersonController asInterface(android.os.IBinder obj) { if ((obj==null)) { return null; } android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR); if (((iin!=null)&&(iin instanceof com.devnn.libservice.PersonController))) { return ((com.devnn.libservice.PersonController)iin); } return new com.devnn.libservice.PersonController.Stub.Proxy(obj); } @Override public android.os.IBinder asBinder() { return this; } @Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException { java.lang.String descriptor = DESCRIPTOR; switch (code) { case INTERFACE_TRANSACTION: { reply.writeString(descriptor); return true; } case TRANSACTION_getPersons: { data.enforceInterface(descriptor); java.util.List<com.devnn.libservice.Person> _result = this.getPersons(); reply.writeNoException(); reply.writeTypedList(_result); return true; } case TRANSACTION_addPerson: { data.enforceInterface(descriptor); com.devnn.libservice.Person _arg0; if ((0!=data.readInt())) { _arg0 = com.devnn.libservice.Person.CREATOR.createFromParcel(data); } else { _arg0 = null; } this.addPerson(_arg0); reply.writeNoException(); if ((_arg0!=null)) { reply.writeInt(1); _arg0.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE); } else { reply.writeInt(0); } return true; } default: { return super.onTransact(code, data, reply, flags); } } } private static class Proxy implements com.devnn.libservice.PersonController { private android.os.IBinder mRemote; Proxy(android.os.IBinder remote) { mRemote = remote; } @Override public android.os.IBinder asBinder() { return mRemote; } public java.lang.String getInterfaceDescriptor() { return DESCRIPTOR; } /** * Demonstrates some basic types that you can use as parameters * and return values in AIDL. */ @Override public java.util.List<com.devnn.libservice.Person> getPersons() throws android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); java.util.List<com.devnn.libservice.Person> _result; try { _data.writeInterfaceToken(DESCRIPTOR); boolean _status = mRemote.transact(Stub.TRANSACTION_getPersons, _data, _reply, 0); if (!_status && getDefaultImpl() != null) { return getDefaultImpl().getPersons(); } _reply.readException(); _result = _reply.createTypedArrayList(com.devnn.libservice.Person.CREATOR); } finally { _reply.recycle(); _data.recycle(); } return _result; } @Override public void addPerson(com.devnn.libservice.Person person) throws android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); try { _data.writeInterfaceToken(DESCRIPTOR); if ((person!=null)) { _data.writeInt(1); person.writeToParcel(_data, 0); } else { _data.writeInt(0); } boolean _status = mRemote.transact(Stub.TRANSACTION_addPerson, _data, _reply, 0); if (!_status && getDefaultImpl() != null) { getDefaultImpl().addPerson(person); return; } _reply.readException(); if ((0!=_reply.readInt())) { person.readFromParcel(_reply); } } finally { _reply.recycle(); _data.recycle(); } } public static com.devnn.libservice.PersonController sDefaultImpl; } static final int TRANSACTION_getPersons = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0); static final int TRANSACTION_addPerson = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1); public static boolean setDefaultImpl(com.devnn.libservice.PersonController impl) { // Only one user of this interface can use this function // at a time. This is a heuristic to detect if two different // users in the same process use this function. if (Stub.Proxy.sDefaultImpl != null) { throw new IllegalStateException("setDefaultImpl() called twice"); } if (impl != null) { Stub.Proxy.sDefaultImpl = impl; return true; } return false; } public static com.devnn.libservice.PersonController getDefaultImpl() { return Stub.Proxy.sDefaultImpl; } } /** * Demonstrates some basic types that you can use as parameters * and return values in AIDL. */ public java.util.List<com.devnn.libservice.Person> getPersons() throws android.os.RemoteException; public void addPerson(com.devnn.libservice.Person person) throws android.os.RemoteException; }
仔細觀察會發(fā)現(xiàn),這個接口有個Stub靜態(tài)抽象內(nèi)部類,里面有一個asInterface
方法、onTransact
方法,Proxy
靜態(tài)內(nèi)部類是我們要關(guān)注的。
客戶端進程調(diào)用 PersonController.Stub.asInterface(service)
這個代碼實際上就返回了Proxy這個代理對象,當調(diào)用getPersons和addPerson方法時,也相當于調(diào)用Proxy代理類里的對應(yīng)的方法。
拿addPerson
方法舉例,這個方法實際上把JavaBean對象轉(zhuǎn)成了Pacel類型的對象,然后調(diào)用IBinder的transact方法開始進程間通信。
addPerson方法進程間通信調(diào)用的方法如下:
mRemote.transact(Stub.TRANSACTION_getPersons, _data, _reply, 0);
mRemote
就是通過binderService獲取到的服務(wù)端進程的IBinder對象。
第一個參數(shù)Stub.TRANSACTION_getPersons
表示要調(diào)用的方法ID。
第二個參數(shù)_data
就是要發(fā)送給服務(wù)端的數(shù)據(jù)
第三個參數(shù)_reply
就是服務(wù)端返給客戶端的數(shù)據(jù)。
第四個參數(shù)0
表示是同步的,需要等待服務(wù)端返回數(shù)據(jù),1表示異步不需要等待服務(wù)端返回數(shù)據(jù)。
整體來說Proxy
這個類就是給客戶端使用的代碼。
Stub
這個類是給服務(wù)端使用的代碼。
注意:誰主動發(fā)起通信誰就是客戶端,接收通信是服務(wù)端。有時候一個進程同時充當客戶端和服務(wù)端,比如app與ams的通信,app既充當客戶端也充當服務(wù)端。
Stub
類的onTransact
方法就是服務(wù)端被調(diào)用時的回調(diào)函數(shù)。
boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags)
第一個參數(shù)code
表示調(diào)用的方法ID
第二個參數(shù)data
表示調(diào)用方發(fā)來的數(shù)據(jù)
第三個參數(shù)reply
表示服務(wù)端需要回傳的數(shù)據(jù)
第四個參數(shù)flags表示同步還是異步。
注意這個onTransact
方法是在binder服務(wù)端的binder線程池被調(diào)用的,也就是在子線程調(diào)用的。所以上面的MyService
里的getPersons
和addPersons
方法是在子線程里運行的,如果需要與主線程通信還得使用Handler。
經(jīng)過以上分析,可以發(fā)現(xiàn)進程間通信實際上是拿到對方的IBinder引用后,通過調(diào)用IBinder的transact方法發(fā)送和接收Parcel類型數(shù)據(jù)進行通信。AIDL實際上是簡化了Binder調(diào)用的過程,幫助我們自動生成了通信代碼。我們也可以根據(jù)需要,自己編寫相關(guān)代碼通信。
那么客戶端調(diào)用bindServcie
方法是如何拿到服務(wù)端進程的IBinder對象呢?這部分涉及到android framewok層,這里把它大致流程分析一下。
bindService流程分析
調(diào)用bindService方法時,實際是調(diào)用ContextWrapper的bindService方法,Activity是繼承于ContextWrapper。下面基于Android 10的源碼,用流程圖表示這個調(diào)用鏈。
整體來說,客戶端進程需要與服務(wù)端進程通信,先要獲取服務(wù)端的binder對象,這中間需要經(jīng)過AMS(Activity Manager Service)服務(wù)作為中介。先向AMS發(fā)起請求(bindService,攜帶ServiceConnection對象),AMS再跟服務(wù)端進程通信,服務(wù)端進程把binder給到AMS,AMS再通過ServiceConnection的onServiceConnected回調(diào)把binder發(fā)送給客戶端進程。客戶端獲取到binder就可以調(diào)用transact方法發(fā)送數(shù)據(jù)。
OK,關(guān)于AIDL和Binder進程間通信就介紹到這了。
原文鏈接:https://blog.csdn.net/devnn/article/details/127028386
相關(guān)推薦
- 2022-12-10 C++中如何將數(shù)據(jù)保存為CSV文件_C 語言
- 2022-11-12 python中validators庫的使用方法詳解_python
- 2022-07-21 IC設(shè)計中g(shù)litch free時鐘選擇器的設(shè)計過程
- 2022-07-30 Python實現(xiàn)多腳本處理定時運行_python
- 2022-04-25 ASP.NET?Core?MVC中使用Tag?Helper組件_實用技巧
- 2022-09-08 詳解Dijkstra算法原理及其C++實現(xiàn)_C 語言
- 2022-09-13 教你一招完美解決vscode安裝go插件失敗問題_Golang
- 2022-05-17 docker停止某個容器
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細win安裝深度學習環(huán)境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎(chǔ)操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權(quán)
- redisson分布式鎖中waittime的設(shè)
- maven:解決release錯誤:Artif
- restTemplate使用總結(jié)
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務(wù)發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結(jié)構(gòu)-簡單動態(tài)字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應(yīng)用詳解
- 聊聊消息隊列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支