Words written in the front
An Android application is a Linux process. Each application runs in its own process, does not interfere with each other, and is relatively safe.
An application corresponds to a main thread, which is commonly referred to as UI threads. Android adheres to a single-threaded model, so Ui operations are not thread-safe and these operations must be executed in UI threads.
This article is a translation of the official document, with the original link:/guide/components/
Overview
When an application component starts and the application does not run any other components, the Android system uses a single execution thread to start a new Linux process for the application. By default, all components of the same application run in the same process and thread (called the "main" thread). If an application component starts and the application already has a process (because other components of the application exist), the component starts within this process and uses the same execution thread. However, you can schedule other components in your application to run in separate processes and create additional threads for any process.
This document describes how processes and threads work in Android applications.
1. Process
By default, all components of the same application run in the same process, and most applications do not change this. However, if you find that you need to control the process to which a component belongs, you can do this in the manifest file.
The manifest file entries of various component elements—,, and—support the android:process attribute, which specifies which process the component should run. You can set this property so that each component runs in its own process, or that some components share one process while others do not. Additionally, you can set up android:process so that components of different applications run in the same process, provided that the applications share the same Linux user ID and sign with the same certificate.
Additionally, the element supports the android:process attribute to set the default value that applies to all components.
If memory is insufficient and other processes that provide users with more urgent services need memory, Android may decide to close a process at a certain moment. Application components running in the terminated process will also be destroyed. When these components need to be run again, the system will restart the process for them.
When deciding which process to terminate, the Android system will weigh the relative importance of them to the user. For example, it is more likely to close the Activity process that is no longer visible on the hosting screen than a process that hosts a visible activity. Therefore, the decision to terminate a process depends on the status of the components running in that process. Below, we introduce the rules used to decide to terminate the process.
Process life cycle
The Android system will keep the application process as long as possible, but in order to create a new process or run more important processes, it is ultimately necessary to clear the old process to recycle memory. To determine which processes are retained or terminated, the system places each process into the "importance hierarchy" based on the components running in the process and the status of those components. If necessary, the system first eliminates the least important process, then the slightly less important process, and so on, to recycle system resources.
There are 5 levels of importance hierarchy. The following list lists various processes according to their importance (the first process is most important and will be the last process terminated):
1. Front desk process
The process required for the user's current operation. If a process meets any of the following conditions, it is considered a foreground process:
Hosting the activity the user is interacting with (the onResume() method of the Activity has been called)
Host a Service, which is bound to the Activity the user is interacting with
Host the Service running in the foreground (the service has called startForeground())
Hosting a Service (onCreate(), onStart(), or onDestroy()) that is executing a lifecycle callback
Hosting a BroadcastReceiver that is executing its onReceive() method
Usually, there are few foreground processes at any given time. The system will terminate them only if it is inherently insufficient to support them to continue running simultaneously. At this time, the device often has reached the memory paging state, so some foreground processes need to be terminated to ensure the user interface responds normally.
2. Visible process
There is no foreground component, but it still affects the process of what the user sees on the screen. A process is considered to be a visible process if any of the following conditions:
Hosted is not in the foreground but is still visible to the user (its onPause() method has been called). For example, this may happen if the foreground Activity launches a dialog box that allows the previous Activity to be displayed after it
Hosting Services bound to visible (or foreground) Activity
Visible processes are considered extremely important processes and the system will not terminate unless they must be terminated in order to keep all foreground processes running simultaneously.
3. Service process
A process that is running a service that has been started using the startService() method and does not belong to the two higher-class processes mentioned above. Although service processes are not directly associated with what users see, they are usually performing some actions that users care about (e.g., playing music in the background or downloading data from the network). Therefore, unless the memory is insufficient to keep all foreground and visible processes running simultaneously, the system keeps the service process running.
4. Background process
A process containing an activity that is currently not visible to the user (the onStop() method of the Activity has been called). These processes have no direct impact on the user experience and the system may terminate them at any time to reclaim memory for use by foreground processes, visible processes, or service processes. There are usually many background processes running, so they are saved in the LRU (Recently Least Used) list to ensure that the last process containing the user's recently viewed Activity is terminated. If an activity correctly implements the lifecycle method and saves its current state, terminating its process has no significant impact on the user experience, because when the user navigates back to the activity, the activity restores all its visible state. For information on saving and restoring status, see the Activity documentation.
5. Empty process
Processes that do not contain any active application components. The sole purpose of retaining such processes is to be used as a cache to reduce the startup time required to run the component in the next time. To balance overall system resources between process cache and underlying kernel cache, the system often terminates these processes.
Depending on the importance of the currently active component in the process, Android rated the process as the highest level it could reach. For example, if a process hosts a service and a visible activity, the process is rated as a visible process, not a service process.
Furthermore, the level of one process may be increased by the dependence of other processes on it, i.e. the process serving another process will never be lower than the process it serves. For example, if the content provider in process A serves clients in process B, or if the service in process A is bound to a component in process B, process A is always considered to be at least as important as process B.
Since the process running a service has a higher level than the process hosting a background activity, it is best to start a service for a long-running operation rather than simply creating a worker thread, especially when the operation may be more durable than the activity. For example, the Activity that is uploading an image to a website should start a service to perform the upload, so that even if the user exits the Activity, the upload operation can continue in the background. Using the service guarantees that no matter what happens to the Activity, the operation has at least a "Service Process" priority. Similarly, broadcast receivers should also use services instead of simply putting time-consuming and tedious operations into threads.
2. Thread
When the application starts, the system creates an execution thread called "main thread" for the application. This thread is very important because it is responsible for assigning events to the corresponding user interface widgets, including drawing events. In addition, it is also a thread that applies interaction with Android UI toolkit components (components from and packages). Therefore, the main thread is sometimes called a UI thread.
The system will never create separate threads for each component instance. All components running in the same process are instantiated in the UI thread, and system calls to each component are dispatched by that thread. Therefore, methods that respond to system callbacks (for example, onKeyDown() or lifecycle callback methods that report user actions) always run in the process's UI thread.
For example, when a user touches a button on the screen, the applied UI thread will assign the touch event to the widget, which in turn sets its pressed state and publishes invalid requests to the event queue. The UI thread cancels the request from the queue and notifies the widget that it should redraw itself.
This single-threaded mode can lead to poor performance unless the application is properly implemented when it performs heavy tasks in response to user interactions. In particular, if a UI thread needs to handle all tasks, performing a long time-consuming operation (for example, network access or database query) will block the entire UI. Once the thread is blocked, no events, including drawing events, will be dispatched. From the user's point of view, the app appears as pending. Worse, if the UI thread is blocked for more than a few seconds (currently about 5 seconds), the user will see a boring "Application Unresponsive" (ANR) dialog. If it causes dissatisfaction with users, they may decide to quit and uninstall the app.
Additionally, the Android UI toolkit is not a thread-safe toolkit. Therefore, you must not manipulate the UI through worker threads, but only the user interface through UI threads. Therefore, Android's single-threaded mode must comply with two rules:
1. Do not block UI threads
2. Do not access Android UI toolkits outside the UI thread
Worker thread
According to the above single-threaded mode, to ensure the responsiveness of the application UI, the key is not to block UI threads. If the operations performed do not complete very quickly, you should make sure they run in separate threads ("background" or "working" threads).
For example, the following code demonstrates a click listener that downloads an image from a separate thread and displays it in an ImageView:
public void onClick(View v) { new Thread(new Runnable() { public void run() { Bitmap b = loadImageFromNetwork("/"); (b); } }).start(); }
At first glance, this code seems to work well because it creates a new thread to handle network operations. However, it violates the second rule of single-threaded mode: Don't access the Android UI toolkit outside of the UI thread—This example modifies the ImageView from the worker thread (not the UI thread). This can lead to unclear and unforeseen behavior, but it is difficult and time-consuming to track it.
To resolve this issue, Android provides several ways to access UI threads from other threads. Here are a few useful methods:
(Runnable)
(Runnable)
(Runnable, long)
For example, you can fix the above code by using the (Runnable) method:
public void onClick(View v) { new Thread(new Runnable() { public void run() { final Bitmap bitmap = loadImageFromNetwork("/"); (new Runnable() { public void run() { (bitmap); } }); } }).start(); }
Now, the above implementation is thread-safe: complete network operations in a separate thread, and manipulate ImageView in a UI thread.
However, as operations become more complex, such code can become complex and difficult to maintain. To handle more complex interactions through worker threads, consider using Handler in worker threads to process messages from UI threads. Of course, the best solution is perhaps to extend the AsyncTask class, which simplifies the worker thread tasks required to interact with the UI.
Using AsyncTask
AsyncTask allows asynchronous operations to be performed on the user interface. It blocks operations in the worker thread first and then publishes results in the UI thread without you having to handle the thread and/or handlers yourself.
To use it, you must create an AsyncTask subclass and implement the doInBackground() callback method, which will run in the background thread pool. To update the UI, you must implement onPostExecute() to pass the result returned by doInBackground() and run it in the UI thread, so that you can safely update the UI. Later, you can run the task by calling execute() from the UI thread.
For example, you can implement the above example using AsyncTask in the following ways:
public void onClick(View v) { new DownloadImageTask().execute("/"); } private class DownloadImageTask extends AsyncTask<String, Void, Bitmap> { /** The system calls this to perform work in a worker thread and * delivers it the parameters given to () */ protected Bitmap doInBackground(String... urls) { return loadImageFromNetwork(urls[0]); } /** The system calls this to perform work in the UI thread and delivers * the result from doInBackground() */ protected void onPostExecute(Bitmap result) { (result); } }
Now the UI is safe and the code is simplified because the task is broken down into two parts: one should be done within the worker thread and the other should be done within the UI thread.
Here is a brief overview of how AsyncTask works, but to get a comprehensive understanding of how to use this class, you should read the AsyncTask reference documentation:
You can use generics to specify parameter types, progress values, and task final values
Method doInBackground() will be automatically executed on the worker thread
onPreExecute(), onPostExecute(), and onProgressUpdate() are all called in the UI thread
The returned value will be sent to onPostExecute()
You can call publishProgress() in doInBackground() at any time to execute onProgressUpdate() in the UI thread
You can cancel any task in any thread at any time
Notice:Another problem you may encounter when using a worker thread, namely: Runtime configuration changes (for example, a user changes the screen orientation) cause an unexpected restart of the Activity, which may destroy the worker thread. To learn how to stick with tasks in this restart situation, and how to properly cancel tasks when the activity is destroyed, see the source code of the Bookshelf Sample App.
Thread-safe method
In some cases, the methods you implement may be called from multiple threads, so you must make sure that they meet thread-safe requirements when writing them.
This is mainly applicable to methods that can be called remotely, such as those in binding services. If the call to the implemented method in IBinder originates from the same process running IBinder, the method is executed in the caller's thread. However, if the call originates from another process, the method will be executed in some thread selected from the thread pool (rather than in the process's UI thread), which is maintained by the system in the same process as IBinder. For example, even if the service's onBind() method is called from the UI thread of the service process, methods implemented in the object returned by onBind() (for example, subclasses that implement RPC methods) are still called from threads in the thread pool. Since a service can have multiple clients, there may be multiple pool threads using the same IBinder method at the same time. Therefore, the IBinder method must be implemented as a thread-safe method.
Likewise, the content provider may receive data requests from other processes. Although the ContentResolver and ContentProvider classes hide details about how to manage inter-process communication, the ContentProvider methods (query(), insert(), delete(), update(), and getType() methods) that respond to these requests are called from the thread pool of the process where the content provider is located, rather than from the process's UI thread. Since these methods may be called from any number of threads at the same time, they must also be implemented as thread-safe methods.
Inter-process communication
Android utilizes remote procedure call (RPC) to provide an inter-process communication (IPC) mechanism by which methods called by Activity or other application components are executed remotely (in other processes) and all results are returned to the caller. This requires decomposing the method call and its data to a level that the operating system can recognize, and transferring it from the local process and address space to the remote process and address space, and then reassembly and execute the call in the remote process. The return value will then be transferred back in the opposite direction. Android provides all the code needed to perform these IPC transactions, so you only need to focus on defining and implementing the RPC programming interface.
To execute an IPC, you must use bindService() to bind the application to the service. For more information, see the Service Developer Guide.
The above is all the content of this article. I hope it will be helpful to everyone's study and I hope everyone will support me more.