当前位置:网站首页>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 !
边栏推荐
- 【day03】流程控制语句
- Greed 122. The best time to buy and sell stocks II
- CANopen learning notes
- Common weak network testing tools
- Un7.27: how to successfully build a ruoyi framework project in idea?
- 21 days, fat brother personally takes you to play oauth2
- 一名合格的软件测试工程师,应该具备哪些技术能力?
- RT-Thread改变打印串口(在BSP的基础上添加其他功能)
- RT thread changes the print serial port (add other functions on the basis of BSP)
- Adding DSP library to STM32F103 and its solution
猜你喜欢

Build an "industrial brain" and improve the park's operation, management and service capabilities with "digitalization"!

Leetcode58. Length of the last word
![[image classification] 2021 MLP mixer nips](/img/fb/ca25da210c8da2b6b55f372a325a6c.png)
[image classification] 2021 MLP mixer nips

ServletContext、request、response

Machine learning 07: Bayesian learning

RT thread changes the print serial port (add other functions on the basis of BSP)

Do Netease and Baidu have their own tricks for seizing the beach AI learning machine?

Construction and use of FTP server and NFS server
![[untitled]](/img/e9/4b00244b67af5ddaa3f35baa1ac968.png)
[untitled]

Basic knowledge of day08 redis
随机推荐
【day03】流程控制语句
Which stock exchange has the lowest commission? Is it safe to open an account on your mobile phone
金仓数据库KingbaseES安全指南--4 数据访问保护
Linux - MySQL advanced (day19)
Slice切片
Crowdfunding platform system based on JSP & Servlet
C language: realize the exchange of two numbers without creating temporary variables
CV2. Threshold(), CV2. Findcontours(), CV2. Findcontours image contour processing
Advanced Mathematics (Seventh Edition) Tongji University exercises 3-5 personal solutions
[leetcode] 34. Find the first and last positions of elements in the sorted array
What is interface testing and its testing process
金仓数据库KingbaseES安全指南--6.1. 强身份验证简介
《关于我写自定义cell这件事》
Recursion and non recursion are used to calculate the nth Fibonacci number respectively
[MySQL database] index and transaction (often used in interview)
[prototype and prototype chain] get to know prototype and prototype chain~
Convert py file to exe executable file
7/27(板子)染色法判定二分图+求组合数(递推公式)
A 404 page source code imitating win10 blue screen
Ch340 RTS DTR pin programming drives OLED