Android thread interaction

 In the Android development process, time-consuming operations are not allowed to be written in the main thread (UI thread), so as to avoid ANR due to long waiting time. Therefore, time-consuming operations need to create sub-threads to complete, but often these operations need to communicate and interact with the main thread (such as updating the UI of the main thread), but android stipulates that except for the UI thread, other threads cannot access UI controls Or manipulation, so we need to implement these functions through some methods.

 

1. Handler:

 

Handler is a tool in android specially used to pass information between threads. API reference: https://developer.android.google.cn/reference/android/os/Handler

1. Call Looper.prepare and Looper.loop in the B thread. (The main thread does not need)
2. Write the Handler class and rewrite the handleMessage method.
3. Create an instance of the Handler class and bind the looper
4. Call the handler's sentMessage method to send a message.

 

  • The child thread updates the main thread (UI)

Because the main thread has its own Looper mechanism, we don't need to create a Looper:

Handler mHandler = new Handler(){  
 
@Override  
public void handleMessage(Message msg) {  
    super.handleMessage(msg);  
    switch (msg.what) {  
        case 1:  
            //do something,refresh UI;  
            break;  
        default:  
            break;  
        }  
    }    
   
};  

 

Then start a sub-thread and use Handler to send messages directly in the sub-thread:

new Thread() {
    public void run() {
    Message message = new Message();
    message.what = 1;     message.obj
= "The message Hi~Hi sent by the child thread" ;     mHandler .sendMessage(message);
  }; }.start();

 

  • Interaction between child threads

public class ThreadActivity extends AppCompatActivity {

  private final String TAG = "ThreadActivity";

  // Sub thread Handler
  private Handler mSubHandler = null;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_thread1);
    new MyThread().start();
    createThread();
  }


  /**
   * Create a child thread for sending messages
   */
  private void createThread() {
    new Thread() {
      @Override
      public void run() {
        int count = 0;
        while (count < 10) {
          Message msg = new Message ();
          msg.obj = "Sub thread timer: " + count;
          msg.what = 1;
          // use sub thread Handler to send message
          mSubHandler.sendMessage(msg);
          try {
            Thread.sleep(1000);
          } catch ( InterruptedException e) {
            e.printStackTrace();
          }
          count++;
        }
      }
    }.start();
  }

  /**
   * Used to receive messages sent by child threads
   */
  class MyThread extends Thread {

    @Override
    public void run() {
      Looper.prepare();
      mSubHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
          switch (msg.what) {
            case 1:
            Log.i(TAG, (String) msg.obj);
            break;
          }
        }
      };
      Looper.loop();
    }
  }
}

 

2. HandlerThread:

HandlerThread is a Thread that contains a Looper, and we can directly use this Looper to create a Handler. API reference: https://developer.android.google.cn/reference/android/os/HandlerThread

 

HandlerThread is suitable for single-threaded + asynchronous queue model scenarios, which is simpler than Handler + Thread.

// You can also implement the run method 
HandlerThread mHandlerThread = new HandlerThread("HandlerThread_Test" ); mHandlerThread.start(); Handler mThreadHandler = new Handler(mHandlerThread.getLooper()) { @Override public void handleMessage(Message msg) { switch (msg.what) { case 1: Log.i(TAG, "threadName--" + Thread.currentThread().getName() + (String) msg.obj); break; } } }; // Send message to HanderThread mThreadHandler.sendMessage(msg);

 

3. runOnUiThread

The runOnUiThread ( Runnable ) method in the Activity makes it more concise for the child thread to update the UI. There are also View.Post(Runnable) and View.PostDelayed(Runnabe, long) methods, which are basically the same as runOnUiThread.

new Thread(new Runnable() {
    @Override
    public void run() {
        try {
            Thread.sleep( 1000 );
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
 
        runOnUiThread(new Runnable() {
            @Override
            public  void run() {
                 // Execute UI operation 
                Toast.makeText(MainActivity.this , "Test" , Toast.LENGTH_SHORT ).show();
            }
        });
    }
}).start();

 

4. AsyncTask

API reference: https://developer.android.google.cn/reference/android/os/AsyncTask

AsyncTask is an abstract class, which is a lightweight asynchronous class encapsulated by Android. It can perform background tasks in the thread pool, then pass the progress and final results of the execution to the main thread and update the UI in the main thread. AsyncTasks should be used for short operations (up to a few seconds). If you need to keep threads running for a long time, it is highly recommended that you use the various APIs provided by the java.util.concurrent package, such as Executor, ThreadPoolExecutor and FutureTask. Asynchronous tasks are defined by 3 generic types: Params, Progress and Result, and 4 steps: onPreExecute, doInBackground, onProgressUpdate and onPostExecute.

When using AsyncTask, you need to inherit the AsyncTask class and must implement the doInBackground(params...) method. In most cases, you need to implement the onPostExecute(Result) method.

 

private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {
    protected Long doInBackground(URL... urls) {
        int count = urls.length;
        long totalSize = 0;
        for (int i = 0; i < count; i++) {
            totalSize += Downloader.downloadFile(urls[i]);
            publishProgress((int) ((i / (float) count) * 100));
            // Escape early if cancel() is called
            if (isCancelled()) break;
        }
        return totalSize;
    }

    protected void onProgressUpdate(Integer... progress) {
        setProgressPercent(progress[0]);
    }

    protected void onPostExecute(Long result) {
        showDialog("Downloaded " + result + " bytes");
    }
}

 

It is very simple to execute the task after the creation is successful:

new DownloadFilesTask().execute(url1, url2, url3); // Params

 

Generic type of AsyncTask:

  • Params: The parameter type passed to the task at execution time;
  • Progress: During the execution of the asynchronous task, the type of the execution progress value is returned;
  • Result: Background task execution result type.

Not all types must be used in async tasks, if they are not needed, use Void instead.

rivate class MyTask extends AsyncTask<Void, Void, Void> { ... }

 

When executing an asynchronous task, you need to go through 4 steps:

  • onPreExecute(): Called in the UI main line and before the asynchronous task is executed. This step is often used to set up tasks, such as displaying a progress bar in the user interface.
  • doInBackground(Params...): This method is called in the background thread immediately after onPreExecute() is executed. This step is used to execute background tasks that may run for a long time, and the parameters are passed in by execute(params...). Go through this step to get the result and go back to the last step. During the calculation process, you can call the publishProgress(Progress...) method and update the UI to display the task execution progress through onProgressUpdate(Progress...).
  • onProgressUpdate(Progress...): When the publishProgress(Progress...) method is executed, this step is called in the UI main thread and updates the UI's current task progress.
  • onPostExecute(Result): Called on the UI thread after the background thread computation is complete. The result of the background thread computation is passed as a parameter to this step.

AsyncTask also provides the onCancelled() method, which is also executed in the main thread. When the asynchronous task is canceled, onCancelled() will be called, but onPostExecute() will not be called at this time, but it should be noted that in AsyncTask The cancel() method does not really cancel the task, it just sets the task to be canceled. We need to judge the termination of the task in doInBackground(). It is like if you want to terminate a thread, call the interrupt() method, and just mark it as interrupted, you need to mark and judge inside the thread and then interrupt the thread.

Using AsyncTask also need to pay attention to the following issues:

  • An instance of an asynchronous task must be created in the UI thread, that is, the AsyncTask object must be created in the UI thread.
  • execute(Params...) must be executed on the UI thread.
  • Do not manually call onPreExecute(), onPostExecute(Result), doInBackground(Params...), onProgressUpdate(Progress...) methods.
  • The task can only be executed once (if a second execution is attempted, an exception will be thrown).
  • UI component information cannot be changed in doInBackground(Params...params).
  • AsyncTask is not bound to any component life cycle, so when creating and executing AsyncTask in Activity/or Fragment, it is best to call cancel(boolean) in onDestory() of Activity/Fragment;
  • If AsyncTask is declared as a non-static inner class of Activity, then AsyncTask will keep a reference to the Activity that created the AsyncTask. If the Activity has been destroyed and the background thread of AsyncTask is still executing, it will continue to retain this reference in the memory, so that the Activity cannot be recycled, causing memory leaks.
  • When the screen is rotated or the Activity is killed by the system in the background, the Activity will be re-created. The previously running AsyncTask (non-static inner class) will hold a reference to the previous Activity, which is no longer valid. Call onPostExecute() at this time. Going to update the interface will no longer take effect.

There are two thread pools in AsyncTask for us to call, and SERIAL_EXECUTOR is used by default. The core thread of AsyncTask is 5, the queue capacity is 128, and the maximum number of threads is 9.

  • THREAD_POOL_EXECUTOR, asynchronous thread pool.
  • SERIAL_EXECUTOR, synchronization thread pool.

 

Guess you like

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