Android is a message-driven system, it implements the message loop mechanism through Looper and Handler. The message loop of Android is thread-oriented, each thread can have its own message queue and message loop.
The Looper in the Android system manages the message queue and message loop of the thread. Looper.myLooper() method can return current thread’s Looper object, and Looper.getMainLooper() method will return current process’s main thread’s Looper object.
1. Android Child Thread Example.
- Now let us look at the below example first. The below example does not use Handler. When the button is clicked, a message should be displayed under it.
- activity_handler.xml
<LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" tools:layout_editor_absoluteY="8dp" tools:layout_editor_absoluteX="8dp"> <Button android:id="@+id/handlerExampleButton" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Click Me" /> <TextView android:id="@+id/handlerExampleTextView" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="" /> </LinearLayout>
- HandlerActivity.java
package com.dev2qa.example; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.view.View; import android.widget.Button; import android.widget.TextView; public class HandlerActivity extends AppCompatActivity { private Button handlerExampleButton; private TextView handlerExampleTextView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_handler); setTitle("dev2qa.com - Handler Example"); // Get the button. handlerExampleButton = (Button)findViewById(R.id.handlerExampleButton); handlerExampleButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { // Start a child thread when button is clicked. WorkerThread workerThread = new WorkerThread(); workerThread.start(); } }); // Get the TextView which show button click message. handlerExampleTextView = (TextView)findViewById(R.id.handlerExampleTextView); } // This is a child thread class. private class WorkerThread extends Thread{ @Override public void run() { // This line code will throw an exception, because can not update UI component status in child thread. handlerExampleTextView.setText("Above button has been clicked."); } } }
- But when you run it, you may encounter the below exception.
com.dev2qa.example E/AndroidRuntime: FATAL EXCEPTION: Thread-4 Process: com.dev2qa.example, PID: 7878 android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can tou at android.view.ViewRootImp1.checkThread(ViewRootImpl.java: 6391) ...... at com.dev2qa.example.HandlerActivity$WorkerThread.run(HandlerActivity.java:38)
- This is because of the following reasons.
- Android UI components are not thread-safe.
- Can not update android view components in child thread.
- Only the application main thread can modify view components.
2. Use Android Handler To Communicate Between Child Thread And Main Thread.
- To resolve the above error, we should use android.os.Handler and android.os.Message class.
- We should create the message object in the child thread and then use the main thread Handler object to put the message in the main thread message queue, below is the example source code.
package com.dev2qa.example; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.support.v7.app.AppCompatActivity; import android.view.View; import android.widget.Button; import android.widget.TextView; public class HandlerActivity extends AppCompatActivity { private Button handlerExampleButton; private TextView handlerExampleTextView; private Handler mainThreadHandler; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_handler); setTitle("dev2qa.com - Handler Example"); // This handler is used to handle child thread message from main thread message queue. mainThreadHandler = new Handler(){ @Override public void handleMessage(Message msg) { if(msg.what == 1) { // Update view component text, this is allowed. handlerExampleTextView.setText("Above button has been clicked."); } } }; // Get the button. handlerExampleButton = (Button)findViewById(R.id.handlerExampleButton); handlerExampleButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { // Start a child thread when button is clicked. WorkerThread workerThread = new WorkerThread(); workerThread.start(); } }); // Get the TextView which show button click message. handlerExampleTextView = (TextView)findViewById(R.id.handlerExampleTextView); } // This is a child thread class. private class WorkerThread extends Thread{ @Override public void run() { // Create a message in child thread. Message childThreadMessage = new Message(); childThreadMessage.what = 1; // Put the message in main thread message queue. mainThreadHandler.sendMessage(childThreadMessage); } } }
- Now the app will not throw exceptions when you click the button. And you can see the message text under the button also, below is the demo video on youtube, if you can not watch the above video, you can see it on the youtube URL https://youtu.be/n5vW6xUcqDQ