preface
Service not only needs to be associated with the current App, but also needs to control the internal business logic of Service within the App. Service and Binder are enough. However, as a novice, I have to learn how to use Binder on the Internet. Bind all binders and Service code together. Bind all binders and Service code together to return Service instances. Bind all binders and Service code together to return Service instances. Binder for yourself?
Tools used
IDE | language | Simulator version |
---|---|---|
The AS 3.5 | kotlin | Android 10 Api 29 |
Binder’s official explanation
Base class for a remotable object, the core part of a lightweight remote procedure call mechanism defined by {@link IBinder}. A base class for remote objects, the lightweight core of which is the remote procedure call mechanism defined by {@link IBinder}. This class is an implementation of IBinder that provides standard local implementation of such an object. This class is a native implementation provided by IBinder that implements such objects.
The rest of the comments are numerous, but to sum it up:
Binder
Class inheritanceIBinder
Class.- A lot of developers use it
aidl
To customize the interface they want. Binder
Is a basicIPC (Interprocess Communication)
Unit, does not affect the application life cycle. Should be used in the highest level of application components, such asService
.Activity
.ContentProvider
.- You must remember the state of the process when it was terminated. Attached is the official explanation link: Binder Class.
The body of the
Binder and Service scripts that are common on the web will not be posted here. I will just write what I think is a better way to write calls.
Bind bind bind bind bind bind bind bind bind
Service returns Binder when onBinde() :
override fun onBind(p0: Intent?).: IBinder? {
/* * attachInterface(@nullable IInterface owner, @Nullable String descriptor) */
binder.attachInterface(object : MyBinderInterface {
override fun asBinder(a): IBinder {
return binder
}
override fun start(s: String) {
log("catch string $s")
//do your work
}
}, binderInterfaceDescriptor)
return binder
}
Copy the code
2. Attach interface with Binder?
A handy attachInterface() method is used to initialize the Binder within the Service, and queryLocalInterface() can be used at Service time to obtain the IInterface associated with the Binder. IInterface is the base class for interfaces used by binders.
Source: IInterface. Java/** * Base class for Binder interfaces. When defining a new interface, * you must derive it from IInterface. */
public interface IInterface
{
/** * Retrieve the Binder object associated with this interface. * You must use this instead of a plain cast, so that proxy objects * can return the correct result. */
public IBinder asBinder(a);
}
Copy the code
Interfaces associated with Binder should also inherit the IInterface class. The code tested looks like this:
interface MyBinderInterface: IInterface {
fun start(s:String)
}
Copy the code
3. Enable bindingActivity
theServiceConnection
How to write?
If your Service is running in the main process, the queryLocalInterface method returns the interface associated with Binder using attachInterface, then you can use myBinderInterface directly to handle the Service.
private val serviceConnection = object : ServiceConnection {
override fun onServiceConnected(name: ComponentName, service: IBinder) {
val myBinderInterface =
service.queryLocalInterface(MyService.binderInterfaceDescriptor) asMyBinderInterface myBinderInterface? .start("hello world")}/** * note: this method */ is not called in the same process
override fun onServiceDisconnected(name: ComponentName){}}Copy the code
However, if the Android: Process =”:reomte” attribute of the Service is configured in androidmanifest.xml, the queryLocalInterface method will return null. OnServiceConnected ected service becomes BinderProxy, which is a final class and inherits IBinder. The queryLocalInterface method in this class is as follows:
Source: BinderProxy. Java/** * Retrieve a local interface - always null in case of a proxy */
public IInterface queryLocalInterface(String descriptor) {
return null;
}
Copy the code
This is why queryLocalInterface returns NULL when a Service is in a separate process. So how do you interact with Binder? In ibinder.java, quecryLocalInterface is commented as follows:
Source: IBinder. Java/** * Attempt to retrieve a local implementation of an interface * for this Binder object. If null is returned, you will need * to instantiate a proxy class to marshall calls through * the transact() method. */
public @Nullable IInterface quecryLocalInterface(@NonNull String descriptor);
Copy the code
So we’re going to use transact() to interact with Binder. Transact () is defined as follows:
Source: IBinder. Java/**
* Perform a generic operation with the object.
*
* @param code The action to perform. This should
* be a number between {@link #FIRST_CALL_TRANSACTION} and
* {@link #LAST_CALL_TRANSACTION}.
* @paramdata Marshalled data to send to the target. Must not be null. * If you are not sending any data, You must create an empty Parcel * that is given here@paramreply Marshalled data to be received from the target. May be * null if you are not interested in the return value. * This parameter is used to receive data *@param flags Additional operation flags. Either 0 for a normal
* RPC, or {@link#FLAG_ONEWAY} for a one-way RPC. * FLAG_ONEWAY is null even if the reply is executed successfully. * Use 0 in both directions * *@return Returns the result from {@link Binder#onTransact}. A successful call
* generally returns true; false generally means the transaction code was not
* understood.
*/
public boolean transact(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags)
throws RemoteException;
Copy the code
Here I create a new MyProxy class and implement the Parcelable interface:
class MyProxy(varaction: String? .var result: Boolean): Parcelable {
constructor(parcel: Parcel) : this( parcel.readString(), parcel.readByte() ! =0.toByte()
)
override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeString(action)
parcel.writeByte(if (result) 1 else 0)}override fun describeContents(a): Int {
return 0
}
companion object CREATOR : Parcelable.Creator<MyProxy> {
override fun createFromParcel(parcel: Parcel): MyProxy {
return MyProxy(parcel)
}
override fun newArray(size: Int): Array<MyProxy? > {return arrayOfNulls(size)
}
}
}
Copy the code
So in ServiceConnection, onServiceConnected() is written like this:
override fun onServiceConnected(name: ComponentName, service: IBinder) {
val parcelData: Parcel = Parcel.obtain()
val parcelResult: Parcel = Parcel.obtain()
MyProxy("action".false).writeToParcel(parcelData, 0)
MyProxy("action".true).writeToParcel(parcelResult, 0)
//change as you logic
val code = 1001
//maybe throw RemoteException use try catch
val proxyResult = service.transact(code, parcelData, parcelResult, 0)
//you can get this parcelResult data with createFromParcel() like
//val result = MyProxy.createFromParcel(parcelResult)
parcelData.recycle()
parcelResult.recycle()
}
Copy the code
If you think about it carefully enough, the Transact method works between two processes, with no delay? There is a comment at the beginning of ibinder.java:
Source: IBinder. Java/* The key IBinder API is {@link #transact transact()} matched by * {@link Binder#onTransact Binder.onTransact()}. These * methods allow you to send a call to an IBinder object and receive a * call coming in to a Binder object, respectively. This transaction API * is synchronous, such that a call to {@link #transact transact()} does not * return until the target has returned from * {@link Binder#onTransact Binder.onTransact()}; this is the * expected behavior when calling an object that exists in the local * process, and the underlying inter-process communication (IPC) mechanism * ensures that these same semantics apply when going across processes. */
Copy the code
It is clear that transact() is a synchronous method, and its return will wait until binder.ontransact () returns. Kotlin takes advantage of this by using the following code instead:
var result: MyProxy= MyProxy("example".false)
GlobalScope.launch {
// You can extract the following code as the suspend modified function
withContext(Dispatchers.IO) {
val parcelData: Parcel = Parcel.obtain()
val parcelResult: Parcel = Parcel.obtain()
result.writeToParcel(parcelData, 0)
MyProxy("action".true).writeToParcel(parcelResult, 0)
//change as you logic
val code = 1001
//maybe throw RemoteException use try catch
val proxyResult = service.transact(code, parcelData, parcelResult, 0)
// This is the result returned
result = MyProxy.createFromParcel(parcelResult)
}
}
Copy the code
4.Binder
Binder classes are empty when the Service is running in the main process:
class MyBinder : Binder() {}Copy the code
What if the Service is running in a separate process? We know above that the transact method is handled and returned by binder.onTransact (), so we override this method:
class MyBinder : Binder() {
override fun onTransact(code: Int.data: Parcel, reply: Parcel? , flags:Int): Boolean {
// The class returned is simply written here. You can judge by code, which is passed in by the transat() method
MyProxy("reply".true).writeToParcel(reply, 0)
// Use the quecryLocalInterface method to get the interface and control the Service
//return super.onTransact(code, data, reply, flags)
//base your logic
return true}}Copy the code
5. The result of the sample code above
The Service is running in a separate process.
The following figure shows the Activity Log when binding and starting the Service and passing parameters to the Service using the Transact method
Service
Binder
The Log Log
Many say
Binder
Service
Service
OnBinde
Binder
Service
conclusion
- The above notation is in use
Service
Very useful for individual processes, but cumbersome - Compared to custom
AIDL
This method is not difficult - The enclosed
Android Developers
In theService
A detailed explanation:link
This article has been written on and off for two weeks, there is nothing on the Internet, mainly rely on their own a little bit of verification, debugging, if there is a mistake, please do tell ah, I am still a cute Android new. Thanks for reading my article!