How Processes and Threads Work in Android

Technology News
Recently, Alibaba released heavy news that it has completed the wholly-owned acquisition of Damai.com. Damai.com started with concert ticketing, and is China's largest performance ticketing platform and system service provider covering live performances, sports events and other fields. As early as July 2014, when Ali's big entertainment strategy was still in its infancy, Ali was already an important shareholder of Damai.com.
foreword
When an application component starts and the application is not running any other components, the Android system uses a single thread of execution to start a new Linux process for the application.
By default, all components of the same app run in the same process and thread (called the "main" thread).
If an app component starts and a process already exists for the app (because there are other components of the app), the component starts within that process and uses the same thread of execution. But other components in the app can be arranged to run in separate processes, and additional threads are created for any process.
process
a). By default, all components of the same application run in the same process, and most applications do not change this. However, if you need to control which process a component belongs to, you can do so in the manifest file.
Manifest file entries for various component elements—<activity>, <service>, <receiver>, <provider>—supported  android:process  attribute, this attribute can specify in which process the component should run. You can set this property to have each component run in its own process, or to have some components share a process and others not. Additionally, you can set android:process so that components of different applications run in the same process, but only if the applications share the same Linux user ID and are signed with the same certificate.
b). In addition, the <application> element supports the android:process attribute to set default values ​​that apply to all components.
c). Android may decide to close a process at a certain moment if memory is insufficient and other processes that provide more urgent services to the user need memory. Application components running in the terminated process are also destroyed. When these components need to run again, the system will restart the process for them.
When deciding which processes to kill, the Android system will weigh their relative importance to the user. For example, it is more likely to close a process hosting an activity that is no longer visible on the screen than a process hosting a visible activity. Therefore, the decision to terminate a process depends on the state of the components running in that process.
rules for terminating a process
The Android system will keep the application process as long as possible, but in order to create a new process or run a more important process, the old process will eventually need to be removed to reclaim memory. To determine which processes to keep or kill, the system places each process in an "importance hierarchy" based on the components that are running in the process and the state of those components. When necessary, the system will first eliminate the least important process, then the less important process, and so on, to reclaim system resources.
There are 5 levels of importance hierarchy. The following list lists the various processes in order of importance (the first process is the most important and will be the last to be killed): 
foreground process
Processes necessary for the user's current operation. A process is considered a foreground process if it meets any of the following conditions:
  • Hosting the Activity the user is interacting with (the Activity's onResume() method has been called)
  • Host a Service that is bound to the Activity the user is interacting with
  • Hosts a Service that is running in the "foreground" (the service has called startForeground())
  • Managed Service that is executing a lifecycle callback (onCreate(), onStart() or onDestroy())
  • Hosts the BroadcastReceiver that is executing its onReceive() method
Typically, there are not many foreground processes at any given time. The system will only terminate them as a last resort if there is not enough memory to support them while continuing to run. At this point, the device has often reached the memory paging state, so it is necessary to kill some foreground processes to ensure the normal response of the user interface 
visible process
A process that doesn't have any foreground components, but still affects what the user sees on the screen. A process is considered a visible process if it meets any of the following conditions:
  • Hosts an activity that is not in the foreground, but is still visible to the user (its onPause() method has been called). This can happen, for example, if the foreground activity starts a dialog that allows the previous activity to be displayed after it.
  • Hosts a Service bound to a visible (or foreground) activity.
Visible processes are considered extremely important processes that are not terminated by the system unless they must be terminated in order to keep all foreground processes running simultaneously.
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 categories above. Although service processes are not directly related to what the user sees, they are usually doing something that the user cares about (for example, playing music in the background or downloading data from the network). Therefore, the system keeps the service process running unless there is insufficient memory to keep all foreground and visible processes running at the same time.
backstage process
The process that contains the activity that is currently invisible to the user (the activity's onStop() method 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, visible, or service processes. There are usually many background processes running, so they are kept in an LRU (least recently used) list to ensure that the process containing the user's most recently viewed activity is the last to be killed. If an activity implements the lifecycle methods correctly and saves its current state, killing its process will not have a noticeable impact on the user experience because when the user navigates back to the activity, the activity restores all of its visible state.
empty process
A process that does not contain any active application components. The sole purpose of keeping such a process as a cache is to reduce the startup time required to run a component in it the next time. These processes are often killed by the system in order to balance overall system resources between the process cache and the underlying kernel cache.
Based on the importance of the currently active component in the process, Android rates the process as the highest possible level. For example, if a process is hosting a service and visible activities, the process is rated as a visible process, not a service process.
In addition, a process's rank may be boosted by other processes' dependencies on it, i.e. a process serving another process will never have a lower rank than the process it serves. For example, if a content provider in process A serves a client in process B, or if a service in process A is bound to a component in process B, process A is always considered at least as important as process B.
Since the process running the service is at a higher level than the process hosting the background activity, it is better for an activity that launches a long-running operation to start a service for that operation, rather than simply creating a worker thread, especially when the operation is likely to be more persistent than the activity in this way. For example, an activity that is uploading an image to a website should start a service to perform the upload, so that the upload can continue in the background even if the user exits the activity. Using a service guarantees that, no matter what happens to the activity, the operation has at least a "service process" priority. Similarly, broadcast receivers should use services instead of simply putting time-consuming and lengthy operations into threads.
thread
When the application starts, the system creates an execution thread for the application called the "main thread". This thread is important because it is responsible for dispatching events to the corresponding UI widgets, including drawing events. In addition, it is the thread where the app interacts with the Android UI Kit components (components from the android.widget and android.view packages). Therefore, the main thread is sometimes called the UI thread.
The system does not create a separate thread 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 this thread. Therefore, methods that respond to system callbacks (for example, onKeyDown() or lifecycle callback methods that report user actions) always run on the UI thread of the process.
For example, when the user touches a button on the screen, the app's UI thread dispatches the touch event to the widget, which in turn sets its pressed state and posts an invalidation request to the event queue. The UI thread dequeues the request and informs the widget that it should redraw itself.
Unless the application is properly implemented, this single-threaded model can lead to poor performance when the application performs heavy tasks in response to user interaction. Specifically, if the UI thread needs to handle all the tasks, performing a long operation (eg, network access or database query) will block the entire UI. Once the thread is blocked, no events can be dispatched, including drawing events. From the user's perspective, the app appears to hang. To make matters worse, if the UI thread is blocked for more than a few seconds (currently about 5 seconds), the user sees an annoying "Application Not Responding" (ANR) dialog. If users are dissatisfied, they may decide to quit and uninstall the app.
The Android UI Kit is not a thread-safe kit. Therefore, you must not manipulate the UI through the worker thread, but only through the UI thread. therefore, Android's single-threaded mode must obey two rules:
i. Do not block the UI thread; 
ii. Do not access the Android UI Kit worker thread outside of 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 the UI thread. If you perform operations that don't finish quickly, you should make sure they run in a separate thread ("background" or "worker" thread).
The following code demonstrates a click listener downloading an image from a separate thread and displaying it in an ImageView:

At first glance, this code seems to work fine 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 Kit outside of the UI thread—this example modifies the ImageView from the worker thread (not the UI thread). This can lead to ambiguous, unpredictable behavior that is difficult and time-consuming to track.
To solve this problem, Android provides several ways to access the UI thread from other threads. Several useful methods are listed below:
  • Activity.runOnUiThread(Runnable)
  • View.post(Runnable)
  • View.postDelayed(Runnable, long)
The above code can be fixed by using the View.post(Runnable) method:

The above implementation is thread-safe: network operations are done in a separate thread, while the ImageView is manipulated in the UI thread.
However, as operations become more complex, such code can become complex and difficult to maintain. To handle more complex interactions with worker threads, consider using Handlers in worker threads to handle messages from the UI thread. Of course, the best solution might be to extend the AsyncTask class, which simplifies the tasks of the worker thread that are required to interact with the UI.
AsyncTask allows performing asynchronous operations on the user interface. It blocks the operation in the worker thread and then publishes the result in the UI thread without you having to deal with the thread and/or handler yourself.
To use it, you must subclass AsyncTask and implement the doInBackground() callback method, which will run in the background thread pool. To update the UI, onPostExecute() should be implemented to pass the result returned by doInBackground() and run on the UI thread to update the UI safely. Tasks can be run by calling execute() from the UI thread.
For example, the above example can be implemented using AsyncTask in the following way:

Now the UI is safe and the code is simplified because the task is split into two parts: one part should be done in the worker thread and the other part should be done in the UI thread.
AsyncTask usage overview:
  • You can use generics to specify parameter types, progress values, and task final values;
  • The method doInBackground() is automatically executed on the worker thread;
  • onPreExecute(), onPostExecute() and onProgressUpdate() are all called in the UI thread;
  • The value returned by doInBackground() will be sent to onPostExecute();
  • You can call publishProgress() in doInBackground() at any time to execute onProgressUpdate() in UI thread
注意:使用工作线程时可能会遇到另一个问题,即:运行时配置变更(例如,用户更改了屏幕方向)导致 Activity 意外重启,这可能会销毁工作线程。 要了解如何在这种重启情况下坚持执行任务,以及如何在 Activity 被销毁时正确地取消任务

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325527941&siteId=291194637