Android uses Thread threads and AsyncTask asynchronous tasks, androidasynctask
I recently talked with a few friends about the network download problem during Android development, and talked about the Bug that occurs when the Thread is used to enable the download Thread, in fact, it is really Low to start the download task with a sub-thread directly. What is the reason, and what is the practice of a relatively high level? This blog post is used for detailed analysis and record.
I. Introduction to concepts
A Thread is a program running on the CPU. It can have multiple execution paths. The running program is called a process, and the execution path is called a thread (if you are not familiar with these two terms, you can refer to the operating system books ). Multithreading in Java means that multiple threads can be executed synchronously within a period, which can improve the code running efficiency. Java allows a process to have multiple threads and can be infinitely large, however, there must be a thread, that is, the main thread of the current process.
It must be understood that Thread is an underlying class in the Java language, while Android is a system that uses and encapsulates the Java language, therefore, AsyncTask in Android only uses Java's multi-threaded concept and optimizes the encapsulated abstract class. Therefore, Thread and AsyncTask are completely different levels of concept, rather than simple replacement.
This class is used in Android for AsyncTask asynchronous tasks. It is clearly stated when writing this class, "AsyncTask is designed to be a helper class around {@ link Thread} and {@ link Handler} and does not constitute a generic threading framework. asyncTasks shocould ideally be used for short operations (a few seconds at the most .) if you need to keep threads running for long periods of time, it is highly recommended you use the various APIs ", you don't need to paste it If it's not important. you can It can be seen that the asynchronous task is used for long-time operations. Because the running of each App in Android is regarded as a process, and the main thread in this process is the UI thread, which can be seen when we open an App. For time-consuming operations such as downloading, if placed in the UI thread for execution, the UI thread load will be too large, resulting in no response exception in the ANR application. Therefore, the AsyncTask class is created, used to perform time-consuming non-UI update operations.
Through the above introduction, it is easy to think that AsyncTask uses the multithreading technology in Java, but it is not a pure Thread. How to Implement Asynchronous tasks? Let's look at the source code comparison.
The Thread class is in java. lang package, so he does not need to use another pilot package, and Thread is the class that implements the Runnable interface, that is, he can instantiate; Because Thread is the underlying code, the specific source code is no longer analyzed, so I will mainly talk about how AsyncTask uses the asynchronous task implemented by Thread.
The AsyncTask class is an abstract class under the android. OS package and must be imported before use. AsyncTask is used to create a new thread in the thread factory to execute asynchronous tasks in the background. Previously we said that there is a UI thread in Android as the main thread, and then the created threads are all subthreads, as for what the newly created sub-threads do, it depends on our willingness.
Ii. Download analysis:
I introduced the comparison between the two classes for half a day, and I feel that it is faster to use the Demo directly. Below, I will start the sub-thread and enable asynchronous download methods, and briefly analyze the CPU execution sequence of these two methods.
1. Start the sub-Thread to execute the download in the current Activity
(1) create a download sub-thread:
1/** 2 * download Thread 3 */4 private Thread myThread = new Thread (new Runnable () {5 @ Override 6 public void run () {7 Object data = download (PATH); 8 Message msg = Message. obtain (); 9 msg. what = 101; 10 msg. obj = data; 11 handler. sendMessage (msg); 12} 13 });
(2) execute the downloaded task in Handler:
1 private Handler handler = new Handler (new Handler. callback () {2 @ Override 3 public boolean handleMessage (Message msg) {4 if (msg. what = 101) {5 data = msg. obj; 6 // perform the following data operations 7} 8 return false; 9} 10 });
(3) enable the current download thread where the download is needed:
1 myThread. start ();
The download network request can be easily completed in the preceding three steps. Does it seem simple? The problem arises: the download task is executed in the sub-thread of myThread. If the download task is still in progress, the user performs the page Jump operation, that is to say, the UI thread of the current Activity has been destroyed, but the myThread sub-thread has not been destroyed. After myThread executes the download () method, he then calls handler to send information to execute the data operation, while the handler that executes the data operation is defined in the current Activity. With the destruction of the current Activity, the current handler will also be destroyed, in this way, the handler for data Execution cannot be called in myThread, so it will inevitably report NullPointException. Therefore, it is not safe to use sub-threads to download tasks.
2. Use AsyncTask to execute the download task
Therefore, in Android, the native AsyncTask can be used to perform time-consuming operations such as downloading network requests. The specific method is to create a download task that inherits the AsyncTask abstract class and overwrite doInBackground () in the class (), this method is executed in the Child thread to be downloaded. Click the source code of AsyncTask. We can see that there is a comment @ WorkThread in front of doInBackground, I can imagine that this method is executed in the work thread. Is there any way to execute it in the main thread? Of course there are some methods. We will also see that their methods do not execute statements in the body, which means that they can be rewritten using subclasses, and there are constructor methods, execute (), onPreExecute (), onCancelled () and so on are all executed in MainThread.
Some may ask a question. In this way, the task to be downloaded is still executed in the Child thread. Is this another critical event we mentioned above, will there be no NULL pointer exception after the sub-thread download ends?
Of course, the above Bug will never occur when AsyncTask is used. Why? Next, let's analyze.
In the work thread, only the publishProgress () method is available except the doInBackground () method currently being downloaded, while doInBackground () is an abstract method, so if you want to know what the thread has, you can only find clues from publicProgress. We know that this method is used for release progress. The following is the source code of this method,
1 @WorkerThread2 protected final void publishProgress(Progress... values) {3 if (!isCancelled()) {4 getHandler().obtainMessage(MESSAGE_POST_PROGRESS,5 new AsyncTaskResult<Progress>(this, values)).sendToTarget();6 }7 }
Obviously, this getHandler () is to get the Handler object in the current AsyncTask class, that is, the progress of the release in the work thread will send the information to the Handler of the current AsyncTask for processing, no matter how detailed the release progress is in the work thread, you only need to check how the received information is processed in the current AsyncTask.
1 private static Handler getHandler() {2 synchronized (AsyncTask.class) {3 if (sHandler == null) {4 sHandler = new InternalHandler();5 }6 return sHandler;7 }8 }
This method obviously returns an InternalHandler object when sHandler is not empty. The whole process is to lock AsyncTask. Here, locking is the key. After all, we must ensure the security when sending messages, after obtaining an InternalHandler object, the entire AsyncTask is locked. Then let's see what InternalHandler is.
First, we can determine that this is a subclass that inherits the Handler class. In his handlerMessage (), only a few lines of code are executed. Here we should find the root cause of our problem.
1 AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj; 2 switch (msg.what) { 3 case MESSAGE_POST_RESULT: 4 // There is only one result 5 result.mTask.finish(result.mData[0]); 6 break; 7 case MESSAGE_POST_PROGRESS: 8 result.mTask.onProgressUpdate(result.mData); 9 break;10 }
It is found that there are only two types of information to be sent. The second is MESSAGE_POST_PROGRESS. Isn't it the type of information sent in the publicProgress () method of the publish Progress method. The first MESSAGE_POST_RESULT is not difficult to think of. This is the type of information sent after doInBackground () is executed in the work thread, and some people have already commented on it, "There is only one result" and "only one unique result". The following method will be called after the download task is executed, in fact, you don't need to look down, because the method to be executed is the current AsyncTask's own finish () method. This means that after the doInBackground () of the working thread is normally executed, finish () is executed in the main thread, so our thinking is streamlined.
Okay, maybe after reading the above Code and my analysis, some of you may feel like they are in the fog. It seems that there is no explanation of the jump problem. So you have to think about it. What is the root cause of a null pointer exception in the middle jump after the sub-thread download is directly enabled? It is a null pointer exception generated when the handler object in the main thread cannot be used in the child thread. So how does AsyncTask solve the handler of sending information?
When using handler to send information, the system will first call getHandler () to obtain an InternalHandler object. If not, it will be created. If yes, it will be used, in addition, because the AsyncTask of the asynchronous task is locked throughout the process, other threads cannot be used, and the AsyncTask thread cannot be destroyed at will. In this way, the handler returns the sending information, and the NULL pointer can be crossed smoothly.
Iii. Summary
I believe that students who are still confused should understand this explanation. I will make a summary below.
AsyncTask is an asynchronous task that executes time-consuming operations other than tasks updated on the UI. UI interface updates are executed in the main thread, that is, the UI thread. In this asynchronous task, a worker thread is enabled to execute time-consuming operations. The execution sequence of the worker thread and UI thread is not synchronized. That is to say, the onPostExecute () in the UI thread is called to execute subsequent UI operations only after the download of the worker thread is completed, in this way, asynchronous download is implemented. If the worker thread re-sends the download end information after the UI thread is destroyed, because the worker thread is bound to AsyncTask during the re-use process, it will also be destroyed with the destruction of the current AsyncTask, no subsequent download operations will be performed, and naturally no information will be sent to the end of the download.
The sub-thread download is simply enabled. The sub-thread and the UI thread only maintain a simple synchronization relationship. Therefore, it is not safe to simply execute the download time-consuming operations in the sub-thread. It turns out that, although multithreading in Java is a good mechanism, you should pay attention to its side effects during use and learn to use classes and Methods encapsulated by it.