Wednesday, September 23, 2015

Prevent CalledFromWrongThreadException


CalledFromWrongThreadException is a common error if you tried to send UI events to the UI thread from outside the UI thread.

In this example, there are 3 AsyncTask:

- MyAsyncTask1 update UI element in doInBackground() via publishProgress() & onProgressUpdate(), no error generated.

- MyAsyncTask1 update UI element in doInBackground() directly, error will reported:
Caused by: android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.

- MyAsyncTask1 update UI element in doInBackground() by calling runOnUiThread() with Runnable(),  no error generated.


MainActivity.java
package com.blogspot.android_er.androidcalledfromwrongthreadexception;

import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {

    TextView textView1, textView2;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Button btn1 = (Button)findViewById(R.id.button1);
        Button btn2 = (Button)findViewById(R.id.button2);
        Button btn3 = (Button)findViewById(R.id.button3);
        textView1 = (TextView)findViewById(R.id.textview1);
        textView2 = (TextView)findViewById(R.id.textview2);

        btn1.setOnClickListener(new View.OnClickListener(){
            @Override
            public void onClick(View v) {
                new MyAsyncTask1().execute();
            }
        });

        btn2.setOnClickListener(new View.OnClickListener(){
            @Override
            public void onClick(View v) {
                new MyAsyncTask2().execute();
            }
        });

        btn3.setOnClickListener(new View.OnClickListener(){
            @Override
            public void onClick(View v) {
                new MyAsyncTask3().execute();
            }
        });
    }

    //No access UI in doInBackground()
    //No error
    private class MyAsyncTask1 extends AsyncTask<Void, Integer, Void>{

        int i;

        @Override
        protected void onPreExecute() {
            i = 0;
            textView1.setText("onPreExecute()");
        }

        @Override
        protected void onPostExecute(Void aVoid) {
            textView1.setText("onPostExecute");
        }

        @Override
        protected void onProgressUpdate(Integer... values) {
            textView1.setText("onProgressUpdate(): " + values[0]);
        }

        @Override
        protected Void doInBackground(Void... params) {
            //update UI via publishProgress() & onProgressUpdate()
            while(i<10){
                publishProgress(i++);
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            return null;
        }
    }

    //Update UI in background thread
    //error caused by CalledFromWrongThreadException!!!
    private class MyAsyncTask2 extends MyAsyncTask1{
        @Override
        protected Void doInBackground(Void... params) {

            while(i<10){
                //publishProgress(i++);
                textView2.setText("doInBackground(): " + String.valueOf(i++));
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            return null;
        }
    }

    //Update UI via runOnUiThread()
    //No error
    private class MyAsyncTask3 extends MyAsyncTask1{
        @Override
        protected Void doInBackground(Void... params) {

            while(i<10){
                //publishProgress(i++);
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        textView2.setText("doInBackground(): " + String.valueOf(i++));
                    }
                });

                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            return null;
        }
    }

}


layout/activity_main.xml
<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:padding="16dp"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/title"
        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" />

    <Button
        android:id="@+id/button1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="No access UI in doInBackground()"
        android:textAllCaps="false" />
    <Button
        android:id="@+id/button2"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Update UI in background thread\nCalledFromWrongThreadException"
        android:textAllCaps="false" />
    <Button
        android:id="@+id/button3"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="runOnUiThread()"
        android:textAllCaps="false" />
    <TextView
        android:id="@+id/textview1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textSize="40dp" />
    <TextView
        android:id="@+id/textview2"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textSize="40dp" />

</LinearLayout>

No comments: