Android Development Journey: Processes and Threads

introduction

When an application's components are run for the first time, Android starts a Linux process with only one thread of execution. By default, all components of the application run in this process and thread. However, you can schedule components to run in other processes, and you can spawn other threads for processes. This article introduces Android processes and threads from the following points:

1. Process 2. Thread 2.1. Remote procedure calls (Remote procedure calls, RPCs) 2.2. Thread-safe methods

1. Process

The process in which a component runs is controlled by the manifest file. Component elements -,,,, have a process attribute that specifies the process in which the component runs. This property can be set so that each component runs in its own process, or some components share a process and others do not. They can also be set up so that components of different applications run in the same process - assuming the applications share the same Linux user ID and are assigned the same permissions. The element also has a process attribute, which sets a default value for all components.

All components are instantiated in the main thread of a specific process, and system call components are dispatched by the main thread. A separate thread is not created for each instance, so the methods corresponding to these calls - such as View.onKeyDown() reporting user actions and lifecycle notifications - always run in the main thread of the process. This means that no component should perform long or blocking operations (such as network operations or loop calculations) when called by the system, because this will block other components in the process. You can spawn separate threads for long operations.

public boolean onKeyDown(int keyCode,KeyEvent event): Default implementation KeyEvent.Callback.onKeyMultiple(), executed when the view's KEYCODE_DPAD_CENTER or KEYCODE_ENTER is pressed and then released, if the view is available and clickable.

parameter

keyCode - indicates the key code that the button was pressed from KeyEvent event - defines the KeyEvent object of the button action

return value

If you handle the event, return true; if you want the next receiver to handle the event, return false.

When the remaining memory is small and other processes request larger memory and need to be allocated immediately, Android will recycle some processes and the application components in the process will be destroyed. When they run again, a process is restarted.

When deciding which processes to terminate, Android weighs their relative importance to the user. For example, it is easier to shut down a process whose activities are not visible on the screen (a background process) than to run a process whose activities are not visible on the screen (a foreground process). The decision whether to terminate the process depends on the state of the components running in the process. Regarding the state of components, it will be introduced in the next article - component life cycle.

2. Thread

While you may confine your application to a single process, sometimes you will need to spawn a thread to do some background work. Because the user interface must respond quickly to user actions, the thread hosting the activity should not perform time-consuming operations such as network downloads. Any operations that cannot be completed in a short time should be assigned to other threads.

Threads are created in code using standard Java thread objects. Android provides some convenient classes to manage threads - Looper is used to run message loops in threads, Handler users process messages, and HandlerThread users set up a message loop thread.

Looper class

Users of this class run message loops in threads. The thread does not have a message loop by default, you can call prepare() in the thread to create a running loop; then call loop() to process the message until the loop ends. Most of the message loop interaction is through the Handler class. The following is a typical example of executing a Looper thread, using prepare() and loop() to create an initial Handler to interact with Looper:

class LooperThread extends Thread {
    
       
     public Handler mHandler;   
       
     public void run() {
    
       
         Looper.prepare();   
           
         mHandler = new Handler() {
    
       
             public void handleMessage(Message msg) {
    
       
                 // process incoming messages here   
             }   
         };   
           
         Looper.loop();   
     }   
}  

For more information about Looper, Handler, and HandlerThread, please refer to relevant information.

2.1. Remote procedure calls (RPCs)

Android has a lightweight remote procedure call mechanism - the method is called locally but executed remotely (in another process), and the result is returned to the caller. This requires breaking down the method call and its accompanying data into a level that the operating system can understand, transporting it from the local process and address space to the remote process and address space, and reassembling the call. Return values ​​are transferred in the opposite direction. Android provides all the code to do this work, so that we can focus on defining and executing the RPC interface itself.

An RPC interface contains only methods. All methods are executed synchronously (local methods block until remote method execution is completed), even if there is no return value. In short, the mechanism works as follows: First, you declare an RPC interface you want to implement using simple IDL (interface definition language). From this declaration, the aidl tool generates a Java interface definition that is provided to local and remote processes. It contains two inner classes as shown in the figure below:

img

The inner class has all the code needed to manage remote procedure calls to the interface you define using IDL. Both inner classes implement the IBinder interface. One of them is used locally by the system and you can write code to ignore it. The other is Stub, which extends from the Binder class. In addition to the internal code for efficient IPC (interprocess communication) calls, the inner class also contains method declarations in the RPC interface declaration. You can define a subclass of Stub to implement these methods, as shown in the figure.

Typically, remote processes are managed by a service (since a service informs the system about the process and the other processes it is connected to). It has interface files generated by the aidl tool and RPC methods implemented by Stub subclasses. The client of the service only has the interface file generated by the aidl tool.

Here's how a service establishes a connection with its client:

The client of the service (on the local side) should implement the onServiceConnected() and onServiceDisconnected() methods, so it will be notified when the connection to the remote service is successfully established and disconnected. Then call bindService() to establish the connection. The service's onBind() method will be implemented to accept or reject the connection, depending on the intent it receives (which is passed to binServive()). If the connection is accepted, it returns an instance of the Stub subclass. If the service accepts the connection, Android calls the client's onServiceConnected() method and passes it an IBinder object, returning a proxy for the Stub subclass managed by the service. Through the proxy, the client can call the remote service. Here is just a brief description, omitting some details of the RPC mechanism. You can check relevant information or continue to follow the Android development journey, which will be provided to you later.

2.2. Thread safety method

In some cases, the method you implement may be called by more than one thread, so it must be written to be thread-safe. This is true for remote call methods - the RPC mechanism discussed in the previous section. When a method implemented in an IBinder object is called from within the IBinder process, the method is executed in the caller's thread. However, when called from another process, the method will select a thread pool in the IBinder process maintained by Android for execution, and it will not execute in the main thread of the process. For example, a service's onBind() method is called in the main thread of the service process, and methods executed in the object returned by onBind() (for example, a Stub subclass that implements RPC methods) will be called in the thread pool. Since a service can have more than one client, there can be more than one thread executing the same IBinder method at the same time. Therefore, IBinder's methods must be thread-safe.

Likewise, a content provider can accept data requests generated by other processes. Although the ContentResolver and ContentProvider classes hide how inter-process communication is managed, which ContentResolver methods correspond to which requests - query(), insert(), delete(), update(), getType() - are in the thread pool of the content provider's process. is called instead of in the main thread of this process. Because these methods can be called from any number of threads simultaneously, they must also be implemented to be thread-safe.

Guess you like

Origin blog.csdn.net/huahaiyi/article/details/132096605