当前位置:网站首页>IPC: multi process binder Aidl user guide, including cases
IPC: multi process binder Aidl user guide, including cases
2022-07-28 04:07:00 【Pumpkin's Metaphysics】
Guidelines for related articles
Android Multiple processes write the same file at the same time , What will happen? ?
Android: Multi process startup mode 、 Pay attention to points and how to solve .
Binder
brief introduction
Simply speaking ,Binder yes Android.os A class under the package , It inherited IBinder Interface . from IPC From the perspective of Binder yes Android A unique way of cross process communication . stay FrameWork in ,Binder yes ServiceManager Connect various Manager(ActivityManager、WindowManager wait ) The bridge .
Develop and use in multi process applications Binder To communicate ,Binder It is the communication medium between the client and the server . When bindService when , The server will return the Binder object , Through this Binder object , The client can call a series of services provided by the server , Complete interprocess communication .
AIDL
The above is a brief understanding of Binder, that AIDL What is it? ?
AIDL For Android Interface definition language . A general template , For fast cross process communication , adopt AIDL It can be realized quickly Binder Cross process communication .
We will define .aidl file ,build when ,Android SDK Tools will be based on .aidl The file generates a IBinder Interface , And save it in the gen/ Catalog in . Services must be implemented appropriately IBinder Interface . Then the client application can bind to the service and start from IBinder Call methods to execute IPC.
So let's build a AIDL Use example of , First, get familiar with using AIDL Cross process communication .
AIDL Use
instructions
Realization AIDL The following steps are required :
establish
.aidlfileThis file defines the programming interface with method signature .
Implementation interface
Android SDKThe tool will be based on your.aidlFile generation interface . This interface has a nameStubThe inner abstract class of , It inheritedBinderAnd implementAIDLMethods in interfaces . You must inheritStubClass and implement methods .Expose interfaces for clients
Realization
ServiceAnd rewriteonBind()To return toStubThe realization of the class .
By default ,AIDL The following data types are supported :
Java All basic types in programming languages ( Such as
int、long、char、booleanetc. )Array of primitive types , for example
int[]Self defined
ParcelableObjects andAIDLThe interface itselfString、CharSequenceList、MapFor all elements, it must be one of the supported data types in this list , Or other things you declare
AIDLGenerated interface orparcelablesOne of . In addition, for the receiverlistAlways beArrayList,MapAlways beHashMap.
Be careful :
aidlMethod input parameters , All non basic types need to point to a data flow ,in、outperhapsinout.- Basic types 、
String、IBinderandAIDLThe interface itself defaults toin, It can't be anything else . - It can be for Null Parameters and return types of must use
@nullableAnnotate .
IN OUT INOUT
About in、out、inout These are a few tag The use effect of , Here is a table .
Definition process 1 call process 2 Incoming Book object Method , and tag Namely in、out、inout. The specific calling effect is shown in the table below :
addBook(in Book book)
addBook(out Book book)
addBook(inout Book book)
| TAG | Method | effect |
|---|---|---|
| in | addBook(in Book book) | process 2 Will receive the process 1 add Come in Book object . |
| out | addBook(out Book book) | process 2 Will receive the process 1 Called add Method , But it was delivered Book The object is empty , There's no value in it . But the process 2 Make any changes to the passed object , Sync to process 1 Of Book object . |
| inout | addBook(inout Book book) | process 2 Will receive the process 1 add Come in Book object , At the same time 2 Make any changes to the passed object , Sync to process 1 Of Book object . |
Case a
AIDL Simple communication case .
establish
.aidlfile .IRemoteService.
package com.howie.multiple_process; // Declare any non-default types here with import statements interface IRemoteService { /** * Get service process id */ int getPid(); }Realization
IRemoteService.StubInterface , Implementation interface method .object RemoteServiceImpl : IRemoteService.Stub() { /** * Get server process ID */ override fun getPid(): Int = Process.myPid() }adopt
ServiceimageClientexposeIRemoteServiceDefine a
RemoteService, stayonBindReturn in methodRemoteServiceImpl.class RemoteService : Service() { override fun onCreate() { super.onCreate() } override fun onBind(intent: Intent?): IBinder = RemoteServiceImpl }stay
AndroidManifestRegister in the fileRemoteService<service android:name=".aidl_service.RemoteService"/>Build the client , We use a
ActivityCallbackbindServiceTo bind the service . In addition, we need to buildServiceConnectionstayonServiceConnectedadoptYouService.Stub.asInterface(service)Get remote services ,onServiceDisconnectedThe callback will be performed after the connection is accidentally disconnected .var iRemoteService: IRemoteService? = null private val mConnection = object : ServiceConnection { override fun onServiceConnected(name: ComponentName?, service: IBinder?) { // Get remote services iRemoteService = IRemoteService.Stub.asInterface(service) } override fun onServiceDisconnected(name: ComponentName?) { unBind() } override fun onBindingDied(name: ComponentName?) { super.onBindingDied(name) unBind() } } private fun unBind() { iRemoteService = null unbindService(mConnection) } private fun bindRemoteService() { // Binding remote services , Pass in mConnection bindService(Intent(this, RemoteService::class.java), mConnection, Context.BIND_AUTO_CREATE) } private fun doEvent() { // TODO: Get remote process PID binding.btObtainRemotePid.setOnClickListener { ToastUtil.toastShort("Remote pid is ${ iRemoteService?.pid ?: ""}.") } } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivityClientBinding.inflate(layoutInflater) setContentView(binding.root) binding.btDescribe.text = this::class.java.simpleName // TODO: bind long-range aidl service bindRemoteService() doEvent() }Be careful
RemoteServiceIn the main process , The client is in another new process .Case 2It's the same thing .
Case 2
Case a Describes a simple case , On how to use AIDL. Then it is definitely not enough for daily development , Let's write a more complicated case , utilize AIDL Pass the object and callback interface .
establish
aidlfile , Because it contains objects 、 Callback notice , So we need to create threeaidlfile , They are as follows .Bookobject// Book.aidl package com.howie.multiple_process.bean; parcelable Book;package com.howie.multiple_process.bean import android.os.Parcel import android.os.Parcelable class Book @JvmOverloads constructor(var bookId: Int = 0, var bookName: String? = "", var bookDescribe: String? = "") : Parcelable { constructor(parcel: Parcel) : this() { bookId = parcel.readInt() bookName = parcel.readString() bookDescribe = parcel.readString() } override fun writeToParcel(parcel: Parcel, flags: Int) { parcel.writeInt(bookId) parcel.writeString(bookName) parcel.writeString(bookDescribe) } override fun describeContents(): Int { return 0 } override fun toString(): String { return "Book(bookId=$bookId, bookName=$bookName, bookDescribe=$bookDescribe)" } companion object CREATOR : Parcelable.Creator<Book> { override fun createFromParcel(parcel: Parcel): Book { return Book(parcel) } override fun newArray(size: Int): Array<Book?> { return arrayOfNulls(size) } } }callbackInterface callback object// IBookSizeChangeListener.aidl package com.howie.multiple_process.listener; import com.howie.multiple_process.bean.Book; // Declare any non-default types here with import statements interface IBookSizeChangeListener { void bookSizeChange(in List<Book> books); }BookServiceThe method provided by the// IBookManagerService.aidl package com.howie.multiple_process; import com.howie.multiple_process.bean.Book; import com.howie.multiple_process.listener.IBookSizeChangeListener; interface IBookManagerService { // obtain book list List<Book> getBookList(); // Add books void addBook(in Book book); // Register and unregister listening void registerListener(IBookSizeChangeListener listener); void unRegisterListener(IBookSizeChangeListener listener); }Realization
IBookManagerService.StubInterface , Implementation interface method ./** * Function in binder Execution in thread pool , Pay attention to concurrency */ object BookManagerServiceImpl : IBookManagerService.Stub() { private const val TAG = "BookManagerServiceImpl" /** * Dedicated to AIDL The server saves the callback interface , For internal use binder As key. Ensure that the client passes in the same object , The object can be found smoothly on the server . */ private val callbacks: RemoteCallbackList<IBookSizeChangeListener> = RemoteCallbackList() private val books = ArrayList<Book>() override fun getBookList(): MutableList<Book> { //mock time consuming Thread.sleep(3000) return books } override fun addBook(book: Book?) { // stay binder Thread pool operation , Therefore, synchronous processing is required synchronized(books) { books.add(book ?: Book()) } // Callback callback synchronized(callbacks) { try { val num = callbacks.beginBroadcast() if (AppUtil.isDebug) { Log.d(TAG, "callback num is $num") } for (i in 0 until num) { callbacks.getBroadcastItem(i)?.bookSizeChange(books) } } finally { callbacks.finishBroadcast() } } } override fun registerListener(listener: IBookSizeChangeListener?) { //RemoteCallbackList Thread safety is ensured internally , So there is no need to lock if (listener != null) { callbacks.register(listener) } } override fun unRegisterListener(listener: IBookSizeChangeListener?) { //RemoteCallbackList Thread safety is ensured internally , So there is no need to lock if (listener != null) { callbacks.unregister(listener) } } }The client needs
bind, Like the first case , The code is not listed , It is mainly necessary to declare aIBookSizeChangeListenerRegister to the server , Used on the serverBookAfter the change , Based on Monitoring .private val callback = object : IBookSizeChangeListener.Stub() { override fun bookSizeChange(books: MutableList<Book>?) { //binder Execution in thread pool Need to schedule to the main thread Log.d(TAG, "callback book size is ${ books?.size}") safeLaunch { binding.tvDisplayBook.text = books?.joinToString("\t\n") } } }Registration and de registration functions
iBookManagerService?.registerListener(callback) iBookManagerService?.unRegisterListener(callback) override fun onDestroy() { super.onDestroy() iBookManagerService?.unRegisterListener(callback) unBindBookManagerService() }
Be careful :
Let's talk about the precautions of the above code in detail .
When the client passes objects to the server , It is essentially a process of serialization and deserialization , So it's not the same object ( You can even imagine that both processes are definitely not the same object ). So even if the client registration and de registration are the same object , Different objects will also be generated on the server . So how to ensure the anti registration , Remove the objects that really need to be removed ? First, you can do a test , Same object , It is passed from the client to the server many times , Be willing to meet someone 、
hashcodeas well asbinderobject .register D/BookManagerServiceImpl: Client IBookSizeChangeListener hash code is 86046646 D/BookManagerServiceImpl: Client IBookSizeChangeListener binder is com.howie.multiple_[email protected]520f7b6 D/BookManagerServiceImpl: Service IBookSizeChangeListener hash code is 69562410 D/BookManagerServiceImpl: Service IBookSizeChangeListener binder is [email protected] unregister D/BookManagerServiceImpl: Client IBookSizeChangeListener hash code is 86046646 D/BookManagerServiceImpl: Client IBookSizeChangeListener binder is com.howie.multiple_[email protected]520f7b6 D/BookManagerServiceImpl: Service IBookSizeChangeListener hash code is 110738971 D/BookManagerServiceImpl: Service IBookSizeChangeListener binder is [email protected]You can see that every time you arrive at the server , Corresponding
hashcodeIt's all changing , But responsible for the current object transferBinderObject is unique91b10ffIs the only one. , So we can useBinderAskeyTo de register .The above code does the same , Use the official collection
RemoteCallbackList, It is specially used to store the data transmitted by the clientCallBackobject . It is internally used for searchingkeyThat is to sayBinderobject .ArrayMap<IBinder, Callback> mCallbacks = new ArrayMap<IBinder, Callback>();The methods of the server are
BinderExecution in thread pool , So you need to synchronizeadopt
callbackThe callback is bound on the clientBinderExecution in thread pool , If necessaryUIOperation requires switching toUIThreads . In the above codesafeLaunchIt's rightlifecycleScope.launchIt's a simple package , Exception handling .val coroutineExceptionHandler = CoroutineExceptionHandler { _, throwable -> // TODO: exception handling throwable.printStackTrace() } fun LifecycleOwner.safeLaunch( context: CoroutineContext = EmptyCoroutineContext, start: CoroutineStart = CoroutineStart.DEFAULT, block: suspend CoroutineScope.() -> Unit ): Job { return lifecycleScope.launch(context + coroutineExceptionHandler, start, block) }
Code of this chapter
Next issue , elementary analysis Binder principle .
It's not easy to create , If there is help, one button three times *️. Welcome to technical discussion !
边栏推荐
- [untitled]
- RT thread changes the print serial port (add other functions on the basis of BSP)
- Summary of static blog building tools
- conda虚拟环境总结与解读
- Appnium--APP自动化测试工具
- [MySQL database] index and transaction (often used in interview)
- Selenium -- Web automated testing tool
- ftp服务器、nfs服务器的搭建和使用
- Kingbasees security guide of Jincang database -- 6.2. Configuration files related to authentication
- Error no matching function for call to 'std:: exception:: exception (const char [15])' problem solving
猜你喜欢

Advanced Mathematics (Seventh Edition) Tongji University exercises 3-5 personal solutions

Monotonous stack -- 42. Receiving rain -- a difficult problem that big factories must know
![[untitled]](/img/6c/df2ebb3e39d1e47b8dd74cfdddbb06.gif)
[untitled]

Advanced Mathematics (Seventh Edition) Tongji University exercises 3-6 personal solutions

Combination of Oracle and Premier League statistics and presentation

How does MySQL ensure high availability

Embedded development: tips and techniques -- the best practice of defensive programming with C

Appnium--APP自动化测试工具

Machine learning 06: Decision Tree Learning

xml文件使用及解析
随机推荐
Kingbasees security guide of Jincang database -- 6.2. Configuration files related to authentication
【无标题】
Classification cluster analysis
Servlet usage
7/27(板子)染色法判定二分图+求组合数(递推公式)
Summary of static blog building tools
Monotonic stack - 739. Daily temperature
un7.27:如何在idea中成功搭建若依框架项目?
[untitled]
Common interface testing tools
Detailed explanation of string + memory function (C language)
Dynamic programming - 474. One and zero
Recursion and non recursion are used to calculate the nth Fibonacci number respectively
Experience sharing of automatic test for students with monthly salary of 28K
金仓数据库KingbaseES安全指南--6.1. 强身份验证简介
Move notice!
C language: find the number of 1 in binary stored in memory as an integer
H265/HEVC名词解释-- CTU,CTB,CU,CB,TU,PU,TB,PB,LCU,Slice,Tile,Chroma,Luma,I帧,B帧,P帧
xml文件使用及解析
Data mining-02