Friday, August 8, 2014

Access UI elements from AsyncTask

This post show some examples to Get and Set UI elements from AsyncTask. the "Start AsyncTask" button clicked, it new and execute a AsyncTask, to get and set UI elements in various call-back method, in UI thread and background thread.

  • Starting with HONEYCOMB, AsyncTask are executed on a single thread to avoid common application errors caused by parallel execution - read Run multi AsyncTask at the same time.
    So call myClientTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR) if it is running on device higher than HONEYCOMB.
  • For the ProgressBar, it's a simple and standard demo: update counter in doInBackground(), and call publishProgress() to call onProgressUpdate() in-directly to update the ProgressBar in UI thread.
  • In onPreExecute() and onPostExecute(), it run on UI thread, so you can access UI elements directly.
  • In doInBackground(), it run on background thread; to read data from EditText textIn2 and update TextView textOut2.
    - In background thread, it can read UI elements, so it can call textIn2.getText() directly. (If I am wrong, please comment)
    - In background thread, it CANNOT modify UI element, call runOnUiThread() and pass a Runnable to update textOut2 in UI thread.
MainActivity.java
package com.example.androiduiasynctask;

import android.support.v7.app.ActionBarActivity;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.annotation.TargetApi;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.os.SystemClock;


public class MainActivity extends ActionBarActivity {

 EditText textIn1, textIn2;
 TextView textCounter;
 Button btnStart, btnClear;
 ProgressBar progressBar;
 TextView textOut1, textOut2, textOut3;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        textIn1 = (EditText)findViewById(R.id.textin1);
        textIn2 = (EditText)findViewById(R.id.textin2);
     textCounter = (TextView)findViewById(R.id.counter);
     btnStart = (Button)findViewById(R.id.start);
     btnClear = (Button)findViewById(R.id.clear);
     progressBar = (ProgressBar)findViewById(R.id.progress);
     textOut1 = (TextView)findViewById(R.id.textout1);
     textOut2 = (TextView)findViewById(R.id.textout2);
     textOut3 = (TextView)findViewById(R.id.textout3);
     
     btnStart.setOnClickListener(MyOnClickListener);
     
     btnClear.setOnClickListener(new OnClickListener(){

   @Override
   public void onClick(View arg0) {
    progressBar.setProgress(0);
       textOut1.setText("");
       textOut2.setText("");
       textOut3.setText("");
   }});
    }
    
    OnClickListener MyOnClickListener = new OnClickListener(){

  @TargetApi(Build.VERSION_CODES.HONEYCOMB) @Override
  public void onClick(View v) {
   
   MyAsyncTask myAsyncTask = new MyAsyncTask();
   
   /*
    * Handle multi AsyncTask at the same time
    * Refer: http://android-er.blogspot.com/2014/04/run-multi-asynctask-as-same-time.html
    */
   if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB)
    myAsyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
   else
    myAsyncTask.execute();
   
  }
     
    };
    
    public class MyAsyncTask extends AsyncTask<Void, Integer, Void>{

  @Override
  protected void onPreExecute() {
   //In UI thread, you can access UI here
   super.onPreExecute();
   String s1 = textIn1.getText().toString();
   textOut1.setText(s1);
  }

  @Override
  protected Void doInBackground(Void... arg0) {
   
   for(int i=0; i<=10; i++){
    SystemClock.sleep(1000);
    publishProgress(i);  //update UI in onProgressUpdate
    
    //Can GET from UI elements
    String s2 = textIn2.getText().toString();
    
    final String msgInBackGround = "doInBackground: " + i + " - " + s2;
    
    /*
     * Cannot direct SET UI elements in background thread
     * so do with runOnUiThread()
     */
    runOnUiThread(new Runnable(){

     @Override
     public void run() {
      textOut2.setText(msgInBackGround);
     }});
   }
   return null;
  }

  @Override
  protected void onPostExecute(Void result) {
   //In UI thread, you can access UI here
   textOut3.setText("onPostExecute");
   super.onPostExecute(result);
  }

  @Override
  protected void onProgressUpdate(Integer... values) {
   //In UI thread, you can access UI here
   progressBar.setProgress(values[0]);
   super.onProgressUpdate(values);
  }

    }

}

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:orientation="vertical"
    tools:context="com.example.androiduiasynctask.MainActivity" >

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:autoLink="web"
        android:text="http://android-er.blogspot.com/"
        android:textStyle="bold" />
    
    <EditText
        android:id="@+id/textin1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>
    <EditText
        android:id="@+id/textin2"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>
    <TextView
        android:id="@+id/counter"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>
    <Button
        android:id="@+id/start"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Start AsyncTask"/>
    <Button
        android:id="@+id/clear"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Clear"/>
    <ProgressBar
        android:id="@+id/progress"
        style="?android:attr/progressBarStyleHorizontal"
        android:indeterminate="false"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:max="10"
        android:progress="0"/>
    <TextView
        android:id="@+id/textout1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>
    <TextView
        android:id="@+id/textout2"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>
    <TextView
        android:id="@+id/textout3"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>

</LinearLayout>


No comments: