directory
- preface
- Why use multiple processes?
- Why cross-process communication?
- What are the ways to communicate across processes?
- Implement a multi-process message push using AIDL
- Implementation approach
- Example concrete implementation
- Know what it is and why.
- Cross-process callback interface
- DeathRecipient
- Permission to verify
- Do different initialization tasks for different processes
- conclusion
- conclusion
Why use multiple processes
- For the concept of process, came here is the programming immortal, no longer worded, I believe that you can recite backwards, jumping, lying down, all kinds of posture.
- Believe that many students in the actual development, basic wouldn’t give the app division process, moreover, the use of multiple processes in Android, may also need to write additional code process communication, also may lead to additional bugs, no doubt increased the development workload, time limit for a project in a number of startups does not allow, this led to the entire app in a process.
What are the disadvantages of running the entire app in one process?
- In Android, there is a limit on the memory allocated to each process by the VIRTUAL machine (the value can be 32M, 48M, 64M, etc., depending on the model). Imagine adding a commonly used image selection module in the app for uploading pictures or profile pictures. Loading a large number of bitmaps will increase the memory usage of the app rapidly. If you also cache the images you have viewed in the memory, then the risk of OOM will greatly increase. If you need to use WebView to load a wave of web pages at this time, I ask you are afraid!
How do mainstream apps such as wechat and Weibo solve these problems?
- Wechat mobile development team said in the article “Android Memory optimization talk” : “For Webview, gallery, etc., because there are memory system leaks or occupy too much memory, we can use a separate process. Wechat also currently places them in a separate Tools process.”
Use ADB to check the process information of wechat and Weibo (for Android version 5.0 or lower, you can directly go to Settings -> Applications) :
After entering the adb shell, use the “ps | grep entry name” can filter out the process of want to see.
-
It can be seen that wechat does have a Tools process, and Sina Weibo also has image-related processes, among which there are many other processes, such as wechat push process, Weibo Remote process, etc. It can be seen that they not only put the WebView and image library mentioned above into a separate process. Push services, etc., also run in separate processes.
-
To ensure stability, a message push service may need to be separated from the UI process. After separation, the message push service will not be affected even if the UI process exits, crashes, or has high memory consumption.
Therefore, the rational use of multiple processes is not only a matter of great benefit, I personally think it is very necessary.
Therefore, it is best to consider whether we need to split the process based on our circumstances. That’s the purpose of this article: to give you a multi-process approach to solve the problems and situations mentioned above, or to talk to the interviewer about this knowledge during the interview.
Why cross-process communication is needed
-
For example, if the selected image module is placed in a separate process, we can still use the startActivityForResult method to place the selected image in the Bundle and deliver it with the Intent. (See here, aren’t you planning on making your project’s image selection a separate process?)
-
However, the business of putting the “push notification Service” into a separate process is a little more complicated. In this case, a series of complex operations may occur, such as passing objects to the Service and calling Service methods.
-
Each process runs in a relatively independent memory space, so they cannot communicate directly, because variables and objects in the program have memory addresses after initialization. For example, reading the value of a variable is essentially to find the memory address of the variable and take out the stored value.
-
Different processes, running in independent memory (in fact, it can be understood as two different applications), obviously cannot directly know the memory address of each other’s variables, objects, and so naturally cannot access each other’s variables, objects, and so on. When the two processes interact, they need to use cross-process communication to achieve it. Simply put, cross-process communication is a technology that allows processes to interact with each other.
What are the means of communication across processes
- Transfer bundles between the four components;
- In file sharing mode, multiple processes read and write the same file and obtain the file content for interaction.
- Messenger, a lightweight cross-process communication solution, is implemented at the bottom using AIDL (the implementation is relatively simple, and the blogger thought about it before starting this article, but finally decided that it was not necessary, so let’s not talk about it at Google);
- Android Interface Definition Language (AIDL) is used to define the Interface for cross-process communication.
- Use ContentProvider, often used for multi-process sharing data, such as system albums, music, etc., we can also access through ContentProvider;
- Use sockets to transmit data.
- This article will focus on using AIDL for multi-process communication, because AIDL is the standard cross-process communication API provided by Android. It is very flexible and powerful. . Messenger is also a cross-process approach using AIDL. As the name implies, Messenger is a serial messaging mechanism. It is a lightweight IPC scheme that can pass Message objects between different processes. We can easily communicate between processes by putting data that needs to be passed in Message.
- But when we need to invoke server-side methods, or when there are concurrent requests, Messenger is not appropriate. The four major components that deliver bundles need not be explained. Data that needs to be delivered can be wrapped in intEnts. Other methods are beyond the scope of this article.
Let’s start with AIDL. Are you ready to go?
Use a cross-process message push with AIDL
For multi-process requirements such as image selection, we may not need to write additional process communication code and use the four components to transfer the Bundle. However, for requirements such as push service, which requires a high degree of interaction between processes, process communication can not be bypstepped. Here we use instant chat software as an example to manually implement a multi-process push example, the specific requirements are as follows:
- The UI and notification push services are divided into two processes;
- The UI process displays the specific message data, passes the message sent by the user to the message Service, and then sends it to the remote server.
- The Service sends and receives messages and maintains a long connection with the remote server. The UI process sends messages to the remote server through the Service. The Service receives messages from the remote server and notifies the UI process.
- Even if the UI process exits, the Service needs to stay running and receive messages from the server.
Implementation approach
Let’s sort out the implementation idea:
- Create UI processes (collectively referred to as clients);
- Create a message Service (hereinafter referred to as the server);
- Configure the server to a separate process (specify the process tag in androidmanifest.xml);
- Bind client and server (bindService);
- Let the client and server have the ability to interact. (used by AIDL);
Example concrete implementation
For easy reading, the code below will omit non-important parts, you can Clone the entire code to the local and read the article again:
github.com/V1sk/AIDL
Step0. AIDL call process overview
Before we begin, let’s summarize the process of making multi-process calls using AIDL:
- The client uses the bindService method to bind the server.
- The server returns the Binder object in the onBind method;
- The client takes the Binder object returned by the server and makes cross-process method calls.
The whole AIDL invocation process is summarized as the above three steps, and we will use the example described above to break these steps down and explain the details.
Step1. The client uses the bindService method to bind the server
1.1 Creating a Client and a server and configuring the server to another process
- Create client -> MainActivity;
- Create a server -> MessageService;
- -> Android :process= “:remote”
The client and server described above, and configuring the server to another process, are shown in androidmanifest.xml, as follows:
Enabling multiple processes is as simple as assigning the Android: Process tag to the four major components.
1.2 Binding MessageService to MainActivity
Create MessageService:
The MessageService will look like it was created, onBind will return null, and next we will return an operable object to the client.
The client MainActivity calls the bindService method to bind MessageService
Starting a Service can be done in one of the following ways:
- Use bindService -> bindService(Intent Service, ServiceConnection conn, int flags);
- Use startService -> startService(Intent Service);
BindService and startService: With bindService, multiple clients can bind a Service at the same time, but when all the clients are unbind, the Service will exit. Usually, if you want to interact with a Service, you can use bindService. You can interact with a Service using an IBinder object in onServiceConnected; if you don’t need to interact with a Service, you can use the startService method.
As mentioned above, we need to interact with the Service, so we need to use the bindService method, but we want the Service to keep running after unbind. In this case, You can call both bindService and startService (such as the message Service in this example, which exits the UI process and still needs to receive the message) as follows:
Stpe2. The server returns Binder objects in onBind
2.1 First, what is a Binder?
Binder is the basic interface for remote objects. IBinder is the core of a lightweight remote procedure call mechanism that describes an abstract protocol for interacting with remote objects. Binder implements the IBinder interface. Binder is the Android SDK’s built-in multiprocess communication implementation class. Binder is not required to implement IBinder.
2.2 Second, where does the Binder object that needs to be returned in onBind come from?
Binder objects for AIDL multiprocess communication are generated automatically through our defined.adil interface file. You can go the other way and manually write a Binder class for cross-process communication, which is essentially an inherited class. Google provides an AIDL interface to help us automatically generate Binder because it is a troublesome and repetitive way to go. In the following paragraphs, we will continue to discuss AIDL as a way to go (we should not lead people astray, right?).
2.3 Defining the AIDL Interface
Binder, which is automatically generated through the AIDL interface, is a good place to start.
Data types supported by AIDL:
- All the basic data types in the Java programming language (such as int, Long, CHAR, Boolean, and so on)
- String and CharSequence
- Parcelable: Objects that implement the Parcelable interface
- List: The elements need to be supported by AIDL. The actual concrete class received on the other end is always an ArrayList, but the generated methods use the List interface
- Map: The elements need to be supported by AIDL, including keys and values. The actual concrete class received on the other end is always HashMap, but the generated method uses the Map interface
Other notes:
- Objects passed in AIDL must implement the Parcelable serialization interface;
- For objects passed in AIDL, you need to create a file with the same name and suffix. AIDL in the same path as the class file and declare the class in the file using the parcelable keyword.
- With common interface differences: can only declare methods, can not declare variables;
- All non-base data type parameters need to be tagged with the direction of the data. It can be in, out, or inout. The default underlying data type must be in, not any other direction.
Let’s continue with our example and start with AIDL
Create an AIDL interface that provides the method to send a message (Android Studio create AIDL: right-click project -> New -> AIDL -> AIDL File)
A more embarrassing things, see a lot of articles, no one can depict all in and out, and inout direction of the meaning of these three parameters, later found on stackoverflow can understand the answer (stackoverflow.com/questions/4… Let me translate the general meaning:
- The parameter marked with “in” is the parameter that receives the actual data, which has the same meaning as our normal parameter passing. In AIDL “out” specifies a parameter is used only for output, in other words, this parameter is not concerned with the caller what data transfer to come over, but the value of this parameter can be in a method is invoked after filling (no matter what the caller has value, at the time of method execution, the parameters of the initial value is always empty). This is what “out” means, only for output.
- And “inout” is obviously a combination of “in” and “out”, input and output parameters. What is the use of distinguishing “in” and “out”? This is important because the contents of each parameter must be marshalled (serialized, transmitted, received, and deserialized). In/Out tags allow binders to skip marshalling steps for better performance.
MessageModel is the entity class of the message. This class is passed in AIDL and implements the Parcelable serialization interface. The code is as follows:
After creating MessageModel AS an entity class, do not forget that there is still one thing to do: For objects passed in AIDL, create a file with the same name but with the suffix. AIDL in the same path as the class file and declare the class in the file using the parcelable keyword. The code is as follows:
For those of you who have not been exposed to AIDL, it is confusing to just say so. Let’s take a look at the structure of the project.
We just added three files:
- Messagesender. aidl -> defines the method for sending messages. Binder class messagesender. Stub is automatically generated and implemented on the server and returned to the client
- Messagemodel.java -> Message entity class, passed from client to server, implements Parcelable serialization
- Messagemodel. aidl -> declares that MessageModel can be passed in aiDL under the same package path as messagemodel.java
OK, BELIEVE at this time meng force has been lifted ~
2.5 Create messagesender. aidl, a Binder object automatically generated by aiDL interface, on the server and return it to the client for invocation. The MessageService code on the server is as follows:
Stub is a Binder object automatically generated by Android Studio from our messagesender.aidl file. We need to return this Binder object to the client.
2.6 Client invokes remote methods after receiving Binder objects
The call steps are as follows:
- In the client’s onServiceConnected method, retrieve the Binder object returned by the server.
- Use the MessageSender. Stub. AsInterface method, obtains the MessageSender. Aidl corresponding operation interface;
- After retrieving the MessageSender object, you can simply call the method like a normal interface.
The client code is as follows:
In the client, we called MessageSender’s sendMessage method, sent a message to the server, and passed the generated MessageModel object as a parameter to the server. The final result printed by the server is as follows:
Here are two things to say:
- The server has received the message from the client and printed it correctly.
- The server and client distinguish two processes, PID is different, process name is different.
At this point, we have completed the most basic cross-process method calls using AIDL, which is the entire elaboration of step.0. We can review step.0 again. Now that we have learned how to use it, let’s move on to… The end of the play…
If I write here to the end of the whole play, what’s the difference with salted fish…
Know what it is and why
Let’s go through the above invocation process to see what happened from the client side to the server side, and see how the upper layers of Binder work. The lower layers of Binder are a very complex topic that this article will not delve into. (Binder if you’re wondering what Binder is, manually rewind the tape and look up…)
Let’s review the call flow from the client first:
- MessageSender messageSender = MessageSender.Stub.asInterface(service);
- messageSender.sendMessage(messageModel);
Aidl messagesender. Java source code (the specific path is: a subdirectory in the build directory, you can find it yourself, if you don’t like it).
Please look at the code and comments below, high energy warning ahead…
If you just look at the code, it may be a little confusing, but it will make sense if you combine the code with the flow chart below:
The whole AIDL call process starts from sendMessage on the client side, as shown in the figure above. The asInterface method will determine whether the Binder returned by onBind is stored in the same process. In the same process, the normal method call will be made. The whole process of data transfer needs to be marshalled (serialized, transmitted, received and deserialized) with Binder sublayers, and the final data is then returned for regular method calls.
Knock on the board: The nature of object transfer across processes is serialization, transfer, reception, and deserialization. Is that why objects transferred across processes must implement the Parcelable interface
Cross-process callback interface
Now that we have implemented the function of sending messages from the client to the cross-process server, we need to pass the remote server messages received by the server to the client. Have students estimate will say: “is this a callback interface”, set the callback interface ideas are right, but the callback interface used here is a little different, passing in the AIDL interface, can’t be a common interface, can only be an AIDL interface, so we need to create a new AIDL interface to the service side, as a callback interface.
Create an AIDL interface for collecting messages messagereceiver. AIDL:
Next we register the callback interface with the server and modify our messagesender.aidl:
This is the final aiDL interface we have modified. Now we need to make corresponding changes:
- The method of adding MessageSender’s register and unregister interface in the server;
- The MessageReceiver interface is implemented in the client and registered to the server through MessageSender.
MainActivity:
There are three major client changes:
- Added the messageReceiver object, which is used to listen to the message notification of the server.
- OnServiceConnected method, register messageReceiver with your Service.
- Unregister messageReceiver when onDestroy.
The following changes are made to the server MessageServie:
Major changes to the server:
- Messagesender. Stub implements methods for registering and unregistering callback interfaces;
- Added RemoteCallbackList to manage the AIDL remote interface.
- FakeTCPTask simulates a long connection to notify a client of the arrival of a new message. (XMPP, Mina, Mars, Netty, etc.)
-
RemoteCallbackList is a common ArrayList. Of course not, otherwise why again a RemoteCallbackList, registerReceiveListener and unregisterReceiveListener transmission to come over on the client objects, through Binder processing, When the server receives the is actually a new object, this leads to when unregisterReceiveListener ordinary ArrayList is unable to find a when registerReceiveListener added to the List of the object, But the underlying Binder objects are the same. RemoteCallbackList uses this feature to find the same object, so we can unregister the interface objects passed by the client.
-
RemoteCallbackList automatically removes the listener registered by the client when the client process terminates. It also implements thread synchronization internally, so we do not need to consider thread synchronization when registering or unregistering. (As for the unwieldy use of ArrayList, you can try it yourself. For space reasons, I won’t demonstrate it here.)
At this point, the server notified the client of the relevant code is also finished, the running result is nothing more than the correct printing is not mapped, you can Run, when printing attention to choose a different process, otherwise there is no log staring bad screen.
DeathRecipient
You think this is the end of it? Too young too simple…
I don’t know if you get the feeling, but the interaction between two processes always feels a little insecure… For example, if the server process crashes and the client process wants to call the server method, it cannot call it. Binder can set up a DeathRecipient object. When Binder unexpectedly dies, we can receive notification in the DeathRecipient interface callback method and perform corresponding operations, such as reconnecting services, etc.
DeathRecipient is used as follows:
- Declare the DeathRecipient object and implement its binderDied method, which will be called back when binder dies.
- Set the DeathRecipient object to Binder objects.
DeathRecipient on the client:
Two important methods in Binder:
- LinkToDeath -> Set the death agent DeathRecipient object;
- UnlinkToDeath -> Binder dies in case of disarming the agent.
In addition, isBinderAlive in Binder can also determine if Binder is dead
Permission to verify
Even if the bus, get on the bus also have to di card is not right, if you want our service process not like a bus who wants to get on, then we can add permission verification.
This section describes two common authentication methods:
- Verify the custom permission in onBind of the server. If it passes our check, Binder objects will be returned normally. If it fails to pass the check, null will be returned.
- Check the client package name in the onTransact method on the server. If the check is not passed, return false.
Add custom permission to androidmanifest.xml:
The server can check permissions by:
Do different initialization tasks for different processes
I believe that a few years ago many friends still use The Android-universal-image-loader to load images, which needs to be initialized in the Application class. For example, if we use it to load images, and we have an image selection process, what if we want to allocate more cache to the image selection process, or some other initialization that doesn’t need to be done in the image selection process?
Here’s a rough and easy way to do it, and bloggers do it… Directly get the process name to judge, make the corresponding operation can be:
The onCreate method of the Application will be called for each process created. This is an important point to note. We can also use the pid of the current process to determine the current process name, and then do some logic we need.
- ** Client process: **com.example.aidl
- Server process: com.example.aidl:remote
conclusion
- Multi-process APP can apply for more than one memory in the system, but it should be used reasonably. It is recommended to put some high-consumption but not commonly used modules into an independent process, and the unused process can be manually closed in time.
- Multiple processes can be implemented in a variety of ways: Bundle, Messenger, AIDL, etc.
- To realize multi-process communication in Android, Binder class provided by the system is recommended, which has realized multi-process communication without the need for us to do the underlying work;
- For multi-process applications, the Application will be created multiple times;
conclusion
This article has been written intermittently for a long time, and I believe that not many students really use it, I choose such a topic is thankless… But I hope to provide a complete solution here. Simple multi-process use, and the effect is significant, such as the image selection and WebView configuration into a separate process, this I hope we can take action. There are a lot of knowledge points in this article, which may not be easy to understand. If you are interested, I suggest you write it manually, and then interrupt the point to see what the operation steps are if you don’t understand.
For those of you who are interviewing, if you talk about multi-process during the interview, you can also gain points by having a good conversation with the interviewer. Or in practical work, where using multi-process can better solve problems, you can bang the table in the meeting and say to the supervisor, “I have a bold idea…” (Of course, getting fired is not my problem…)
The article is not easy, if you like this article, or help you hope that you like to forward attention oh. The article will be updated continuously. Absolutely dry!!