In the past month and a half, I have been busy with job handover and interview review due to job changes. I don’t have much time to write blogs. I haven’t even done the three-times-a-week fitness for a few weeks. Many colleagues asked me if I had gained weight or become stronger (I quickly changed the subject, smart boy). I resigned last week. This week I mainly dealt with some personal matters, did some preparations for joining the company, read some books, etc. Next week I will join YY (I only found out last week that the great Luo Shengyang is also in YY). Okay, having said so much, I’m going to get into the main topic of Messenger. Preface Before reading this article, you need to have some understanding of Android's inter-process communication method, otherwise you may be confused. Let’s start with using Messenger There are several ways to implement IPC (inter-process communication) on Android, one of which is to use AIDL. For those who don't know how to use AIDL, you can read the official document below (a ladder is required). https://developer.android.com... For communication using AIDL, the key is to create an aidl file. The system will automatically generate a corresponding Java class for the aidl file, and the key implementation lies in the generated Java class. The system provides a class that makes it easier for us to perform IPC - Messenger. Let's first take a look at how to use Messenger (those who are familiar with it can skip this part).
At this point, after initializing the Sender in the bound service process, you can do the interaction between multiple processes. Using Messenger to implement multi-process interaction is much more convenient than using aidl, but the internal implementation of Messenger is also implemented with aidl, but it is encapsulated for the convenience of developers to call, so that developers can ignore the implementation details of aidl. After a brief understanding of the basic use of Messenger, let's take a look at the source code of Messenger to understand some internal implementation details.
The Messenger class is located in the android.os package. It does not contain a lot of code, so it does not seem difficult to use. There are only a few methods as shown below. In the sample code above, you can see that the client process has two Messengers, Sender and Receiver. If you don't need to implement the service process to call back the client process, then the Receiver can be completely omitted. When the service process needs to call back the client process, the Receiver needs to be passed in. Since the Messenger object needs to be passed between processes, the Messenger class must inherit the Serializable or Parcelable interface. According to the traditional style of the Android system, it is recommended to inherit Parcelable to achieve this. Therefore, the describeContents, writeToParcel methods and CREATOR objects seen above actually inherit the implementation of Parcelable. If you don't understand, you can refer to the official documentation of Parcelable (need a ladder) https://developer.android.com... The equals and hashCode methods are self-explanatory, as we are all familiar with them. The static methods writeMessengerOrNullToParcel and readMessengerOrNullFromParcel are mainly used to implement the read and write operations of the Messenger in the Parcel. The implementation is relatively simple, and you can understand it by referring to the following code. Of course, in addition to inheriting Parcelable, Messenger also needs to declare a Messenger.aidl file with the same name. You can find the Messenger.aidl file in the android.os package under the framework layer source code. For those who have written aidl, it must be familiar to you. Excluding the methods mentioned above, the remaining ones are mainly the two constructors of Messenger and the getBinder and send methods. When the client calls the service process method, it is through the send method in Messenger, so we first look directly at the internal implementation of the send method. Inside it, the send method of mTarget is called. So what is mTarget? From the above, we can see that mTarget is an IMessenger instance, which is the only member variable of Messenger. mTarget is initialized in the public Messenger(Handler target) constructor, using the Handler's getIMessenger method to obtain an IMessenger instance. From the above figure, we can see that there is no modifier in front of getIMessenger, which controls the scope of this method to be limited to other classes in the android.os package. We cannot use this method in daily development, so there is no corresponding interface document in the API document. In the second to last line, we can see new MessengerImpl() and return to the caller at the end. So in fact, mTarget is implemented in MessengerImpl. MessengerImpl is actually a private inner class of Handler, which inherits IMessenger.Stub and implements the send method. Those who have used aidl must have understood the IMessenger.Stub. In fact, IMessenger is the IMessenger.aidl file provided by the system, and IMessenger.Stub is the class generated by IMessenger.aidl. IMessenger.aidl can be found in the android.os package of the framework layer code, and for the Java implementation of IMessenger, you can see the link below. http://grepcode.com/file/repo… The send implementation of MessengerImpl is relatively simple, with only two lines of code
First, the Linux Uid of the client process that initiates the call is stored in the Message object we passed in. When the service process receives the Message, it can get the Linux Uid of the process that initiates the call through msg.sendingUid. Then it is sent to the service process through the sendMessage method of the Handler. This means that the operations between the Messenger and the service process are serial. Therefore, the Messenger is not applicable in scenarios with parallel requirements. After understanding the send method, the only thing left is getBinder, which has a simple internal implementation. Referring to the previous sample code, the main thing here is to return the Binder object to the client call in the Service's onBind method, and the implementation is also in the Java implementation of IMessenger. So far, I have basically read the entire internal code of Messenger. From the above analysis, the internal implementation is indeed very simple. It is an encapsulation implementation based on aidl, and the implementation details of the underlying aidl are shielded from developers. Of course, I personally think there are two shortcomings:
Blocking and non-blocking remote calls The underlying implementation of cross-process communication in the Android system is implemented through Binder. Under normal circumstances, the process of a client process initiating a remote method call is roughly as follows:
This means that we cannot initiate a remote method call in the UI thread of the client process, otherwise if the remote method performs a time-consuming operation, the client's UI thread will be blocked, causing the ANR problem. Readers can try to customize aidl and initiate a time-consuming remote method call for verification. However, if you use the Messenger provided by the system, this problem will not occur. No matter how time-consuming your remote method execution is, the client Messenger will continue to execute the next code after initiating the call and will not block and wait. This puzzles me, why? Earlier we can see that the send method of Messenger is implemented in MessengerImpl Moreover, the operation of sending Messeage uses the Handler of the main thread, and there is no other asynchronous operation. Why is it not blocked during the execution? I didn't understand this at all. Finally, after a long time of tossing and turning, I asked Mr. Ren Yugang for help and got the answer. It turns out that by default, the remote method calls initiated are blocking, but they can also be non-blocking. Messenger uses non-blocking communication, and the key lies in the implementation of IMessenger.aidl Compared with the usual custom aidl, there is an additional oneway keyword. The difference between declaring or not declaring the oneway keyword is to generate a parameter in the Java class. When oneway is not declared, the last parameter passed to mRemote.transact is 0; when oneway is declared, the last parameter passed to mRemote.transact is android.os.IBinder.FLAG_ONEWAY. Looking at the API documentation, you can see that the role of FLAG_ONEWAY is to allow the client to call remote methods non-blockingly. Now the truth is clear. If our custom aidl also wants to implement non-blocking calls, we only need to declare the oneway keyword. Summarize Because oneway is not often used, and there is little mention of it in the documentation, the only description is the following paragraph. I didn't know about it until I read the Messenger code. It's also a great experience. If you are interested in my blog posts, you can follow my blog: http://www.jianshu.com/users/... |
>>: Three popular command line file conversion tools under Linux
Do you often feel at a loss because you are not f...
Phishing and scams When phishing occurs, cybercri...
Download Marvel Universe Movie Collection Marvel ...
On November 25, more and more Apple users complai...
How much does Baidu AiPurchase charge? Is the eff...
Based on his own work practice, the author of thi...
Copywriting with texture can give people enough r...
2022 NetEase video brick moving to make money, da...
A while ago, an Airdesk video by the B station UP...
From a technical point of view, Microsoft's H...
Zhou Wenqiang's "Top Financial Thinking ...
Behind the "paid silence of IPOs" is a ...
The recent popularity of Ruimao has given coffee ...
1. Spending money ≠ growth, data-driven is a capa...
[September 9 news] Microsoft has sent out invitat...