Android learning-concurrent programming: AsyncTask and UI thread

Source: Internet
Author: User

The Android UI is single-threaded, so it must be run asynchronously for long-running programs. AsyncTask is a convenient tool for implementing asynchronous tasks. It completely hides a lot of detailed information about the threads running the task. AsyncTask: in a very simple application, a game engine needs to be initialized. When loading content, some plug-in Advertisement images are displayed. Assume that you want to display the wait interface of the loader on an animated background (similar to Windows Phone 8) while waiting for the game to start. After you click the start button, multiple initialization tasks are executed. Problem: If remote service call Initialization is performed in the UI thread, no other operations can be performed on the UI interface. Solution: Use AsyncTask to solve this problem. The Code is as follows:

Private final class AsyncInitGame extends AsyncTask
 
  
{Private final View root; private final Game game; private final TextView message; private final Drawable bg; public AsyncInitGame (View root, Drawable bg, Game game, TextView msg) {this. root = root; this. bg = bg; this. game = game; this. message = msg;} // run on the UI thread // 1. when the UI thread calls the execute method of a task, the method is called first. During the operations, the task can initialize itself and the environment, in this example, install the background animation @ Override protected void onPreExecute () {I F (0> = mInFlight ++) {root. setBackgroundResouce (R. anim. dots); (AnimationDrawable) root. getBackground ()). start () ;}// runs on the UI thread // 3. when the doInBackground method is complete, the background thread is deleted and the onPostExecute method is called in the UI thread. @ Override protected void onPostExecute (String msg) {if (0 >=-- mInFlight) {(AndimationDrawable) root. getBackground ()). stop (); root. setBackgroundDrawable (bg);} message. setText (msg);} // runs on a background thread // 2. after the onPreExecute method is complete, AsyncTask creates a new background thread and executes the doInBackground method concurrently. @ Override protected String doInBackground (String... args) {return (1! = Args. length) | (null = args [0])? Null: game. initialize (args [0]) ;}} private final class AsyncInitGame extends AsyncTask
  
   
{Private final View root; private final Game game; private final TextView message; private final Drawable bg; public AsyncInitGame (View root, Drawable bg, Game game, TextView msg) {this. root = root; this. bg = bg; this. game = game; this. message = msg;} // run on the UI thread // 1. when the UI thread calls the execute method of a task, the method is called first. During the operations, the task can initialize itself and the environment, in this example, install the background animation @ Override protected void onPreExecute () {I F (0> = mInFlight ++) {root. setBackgroundResouce (R. anim. dots); (AnimationDrawable) root. getBackground ()). start () ;}// runs on the UI thread // 3. when the doInBackground method is complete, the background thread is deleted and the onPostExecute method is called in the UI thread. @ Override protected void onPostExecute (String msg) {if (0 >=-- mInFlight) {(AndimationDrawable) root. getBackground ()). stop (); root. setBackgroundDrawable (bg);} message. setText (msg);} // runs on a background thread // 2. after the onPreExecute method is complete, AsyncTask creates a new background thread and executes the doInBackground method concurrently. @ Override protected String doInBackground (String... args) {return (1! = Args. length) | (null = args [0])? Null: game. initialize (args [0]) ;}}
  
 
If the implementation of AsyncTask is correct, you only need to create an instance and call it by clicking the "Start" button, as shown below:
(Button) findViewById (R. id. start )). setOnClickListener (new View. onClickListener () {@ Override public void onClick (View v) {new AsyncInitGame (root, bg, game, msg0000.exe cute ("basic") ;}}); // note: the code in this article is not comprehensive, just to elaborate AsyncTask
DoInBackground is a Game-like proxy ). Explanation: AsyncTask requires a set of parameters and returns a result. Because the parameter needs to be passed between threads and the result is returned, some handshaking mechanisms are required to ensure thread security. 1. Call the execute method through parameter passing to call AsyncTask. 2. When the thread is executed in the background, these parameters are finally passed to the doInBackground method through the AsyncTask mechanism, and doInBackground returns the result. 3. AsyncTask passes the result as a parameter to the doPostExecute method. The doPostExecute method and the initial execute method run in the same thread.
AsyncTask not only ensures data flow security, but also ensures type security. This abstract base class (AsyncTask) uses Java to reflect on the types that can be used to develop task parameters and results. The following is an example:
public class AsyncDBReq extends AsyncTask
 
  {    @Override protected ResultSet doInBackground(PreparedStatement...q){        //implementation.....    }    @Override protected onPostExecute(ResultSet result){        //implementation    }}public class AsyncHttpReq extends AsyncTask
  
   {    @Override protected HeepResponse doInBackground(HttpRequest.....req){        //implementaion.....    }    @Override protected void onPostExecute(HttpResponse result){        //implementaion    }}
  
 
In the first class, the execute method parameter of the AsyncDBReq instance is one or more PreparedStatement variables. The doInBackground method of the AsyncDBReq instance in this class uses these PreparedStatement parameters as its parameters, and the returned result is ResultSet. The onPostExecute method uses the ResultSet as its parameter. The same applies to the second class.
An instance of AsyncTask can only run once !!!The second execution of the execute method will throw an IllegalStateException. Therefore, a new instance is required for each task call.
Although AsyncTask simplifies parallel processing, it has strong constraints and cannot automatically verify these conditions. Be sure not to violate these constraints. The most obvious constraint is the doInBackground method because it is executed on another thread, only variables in the scope can be referenced !!! This is the difficulty of thread security. If the following two errors may occur in actual use, you may need a lot of exercises to familiarize yourself with and understand them. Case 1:
// Easy to make mistakes ,//.... some clasint mCOunt; public void initButton1 (Button button) {mCOunt = 0; button. setonClickListener (new View. onClickListener () {@ SuppressWarnings ("unchecked") @ Override public void onCLick (View v) {new AsyncTask
 
  
() {@ Override protected Void doInBackground (Void... args) {mCount ++ ;//!!! Not thread safe !! Return null;} cmd.exe cute ();}});}
 
Here, no compilation error is generated during compilation, and no running warning is provided. It may not even fail immediately when a bug is triggered, but the code is absolutely wrong. Two different threads access the variable mCount, but the two threads do not execute synchronization. In view of this situation, you may be surprised when the mInFlight access in the first code in this article executes synchronization. In fact, it is correct. The AsyncTask constraint ensures that the onPreExecute method and the onPostExecute method are executed in the same thread, that is, the thread called by the execute method. Unlike mCount, mInFlight only has one thread to access and does not need to be synchronized.
Case 2: the most fatal concurrency problem may occur when a parameter variable is used up and its reference is not released. The following code:
// Error prone II. public void initButton (Button button, final Map
 
  
Vals) {button. setOnClickListener (new View. OnClickListener () {@ Override public void onClick (View v) {new AsyncTask
  
   
, Void, Void> () {@ Override protected Void doInBackground (Map
   
    
... Params) {// implementation, uses the params Map} cmd.exe cute (vals); vals. clear (); // this is not thread safe !!!! }});}
   
  
 
Error cause: the initButton parameter valse is referenced concurrently, but the synchronization is not executed !!! When AsyncTask is called, it is passed as a parameter to the execute method. The syncTask framework ensures that the reference is correctly passed to the background thread when the doInBackground method is called. However, there is no way to handle the vals references saved and used in the initButton method. Call vals. caear to modify the status in use on another thread, but the synchronization is not executed. Therefore, it is not thread-safe. Best Solution: Make sure that the parameters of AsyncTask are unchangeable. If these parameters are immutable, such as String, Integer, or POJO objects that only contain final variables, they are thread-safe and do not require more operations. The only way to ensure program security is to ensure that only AsyncTask holds the reference. In Case 2, the parameter vals is passed to the initButton method, and we cannot guarantee that it does not have a floating reference (dangline references ). Even if vals. clear is deleted, the Code is not correct, because the instance that calls the initButton method may save the reference of Its Parameter map, and the final passing is the parameter vals. The only correct way to make this code is to completely copy (deep copy) the map and its contained objects !!!
There is also AsyncTask. Another method is not used: onProgressUpdate. Purpose: Enable tasks that run for a long time to periodically and securely return the status to the UI thread.
Let's use an example to describe and end this article. Ah, it's not easy to type. I don't have eclipse on my sister's computer, so I can only use editplus, and I can't afford it with smart prompts. This example shows how to use the onProgressUpdate method to implement the progress bar and show the user how long it will take to initialize the game.
Public class AsyncTaskDemoWithProgress extends Activity {private final class AsyncInit extends AsyncTask
 
  
Implements Game. initProgressLIstener {private final View root; private final Game game; private final TextView message; private final Drawable bg; public AsyncInit (View root, Drawable bg, Game game, TextView msg) {this. root = root; this. bg = bg; this. game = game; this. message = msg;} // run on the UI thread // 1. when the UI thread calls the task's execute method, it will first call this method @ Override protected void onPreExecute () {if (0> = mInFli Ght ++) {root. setBackgroundResouce (R. anim. dots); (AnimationDrawable) root. getBackground ()). start () ;}// runs on the UI thread // 3. when the doInBackground method is complete, the background thread is deleted and the onPostExecute method is called in the UI thread. @ Override protected void onPostExecute (String msg) {if (0 >=-- mInFlight) {(AndimationDrawable) root. getBackground ()). stop (); root. setBackgroundDrawable (bg);} message. setText (msg);} // runs on its own thread // 2. after the onPreExecute method is complete, AsyncTask creates a new background thread and executes the doInBackground method concurrently. @ Override protected String doInBackground (String... args) {return (1! = Args. length) | (null = args [0])? Null: game. initialize (args [0]);} // runs on its UI thread @ Override protected void onProgressUpdate (Integer... vals) {updateProgressBar (vals [0]. intValue ();} // runs on the UI thread @ Override public void onInitProgress (int pctComlete) {// to correctly publish the Process status to the UI thread, onInitProgress calls the publicProgress of AsyncTask. // AsyncTask processes the publicProgress scheduling details of the UI thread, so that onProgressUpdate can safely use the View method. PublicProgress (Integer. valueOf (pctComplete);} int mInFlight, mComplete;/** @ see android. app. activity # onCreate (android. OS. bundle) */@ Override public void onCreate (Bundle state) {super. onCreate (state); setContentView (R. layout. asyncdemoprogress); final View root = findViewById (R. id. root); final Drawable bg = root. getBackground (); final TextView msg = (TextView) findViewById (R. id. msg); final Game game = Game. newGame (); (Button) findViewById (R. id. start )). setOnClickListener (new View. onClickListener () {@ Override public void onCLick (View v) {mComplete = 0; new AsyncInit (root, bg, game, msg0000.exe cute ("basic ");}});} void updateProgressBar (int progress) {int p = progress; if (mComplete <p) {mComplete = p; (ProgressBar) findViewById (R. id. progress )). setprogress (p );}}}
 
... Well, it's not over yet. Here's a summary: · The Android UI thread is single-threaded. To be familiar with the Android UI, developers must be familiar with the concept of task queue. · In order to ensure timely UI response, the execution time of the task to be run exceeds several milliseconds, or hundreds of commands are required, and should not be executed in the UI thread. · Concurrent programming is tricky, prone to mistakes, and difficult to identify errors. · AsyncTask is a convenient tool for running simple and asynchronous tasks. Remember that doInBackground runs on another thread. It cannot write any visible state of other threads, nor read any writable state of other threads. This also includes its parameters! · An important tool for transferring information between parallel threads when an object cannot be changed.

Mr. Fu: Study Notes
Thank you for your reference in Android Programming: Programming Android by Zigurd Mednieks, Laird Dornin, G. blake Meike, and Masumi Nakamura. copyright 2011 O 'Reilly Media, Inc ., 978-1-449-38969-7

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.