Consider a very simple app, where you have two buttons: task button and increment button.
Upon click, the task button will notify the user that the task is now running, and carry out some heavy task, which may take up to seconds. When complete, it will notify the user that the task is finally done.
Upon click, the increment button will simply increment counter and display its current value.
Let's take a look at a crude attempt to implement this using a simple Thread. Here is the layout file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="utf-8"?> | |
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" | |
xmlns:tools="http://schemas.android.com/tools" | |
android:id="@+id/activity_handler_example" | |
android:layout_width="match_parent" | |
android:layout_height="match_parent" | |
android:orientation="vertical" | |
tools:context="com.blogspot.unixnme.handlerexample.HandlerExample"> | |
<TextView | |
android:layout_width="wrap_content" | |
android:layout_height="wrap_content" | |
android:id="@+id/text_vew" | |
android:text="0" /> | |
<Button | |
android:layout_width="wrap_content" | |
android:layout_height="wrap_content" | |
android:id="@+id/button" | |
android:text="Ready"/> | |
<Button | |
android:layout_width="wrap_content" | |
android:layout_height="wrap_content" | |
android:text="Increment" | |
android:id="@+id/increment_button"/> | |
</LinearLayout> |
Here is the implementation activity file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.blogspot.unixnme.handlerexample; | |
import android.app.Activity; | |
import android.os.Bundle; | |
import android.util.Log; | |
import android.view.View; | |
import android.widget.Button; | |
import android.widget.TextView; | |
public class HandlerExample extends Activity { | |
private static String TAG = HandlerExample.class.getSimpleName(); | |
private Button taskButton; | |
private Button incrementButton; | |
private TextView textView; | |
private int counter = 0; | |
@Override | |
protected void onCreate(Bundle savedInstanceState) { | |
super.onCreate(savedInstanceState); | |
setContentView(R.layout.activity_handler_example); | |
textView = (TextView) findViewById(R.id.text_vew); | |
taskButton = (Button) findViewById(R.id.button); | |
incrementButton = (Button) findViewById(R.id.increment_button); | |
// when the taskButton is pressed, do some long task, simulated by sleep | |
taskButton.setOnClickListener(new View.OnClickListener() { | |
@Override | |
public void onClick(View v) { | |
taskButton.setText("Busy"); | |
Thread task = new Thread(new Runnable() { | |
public void run() { | |
try { | |
Thread.sleep(3000); | |
} catch (Throwable t) { | |
Log.d(TAG, t.getMessage()); | |
} | |
} | |
}); | |
task.start(); | |
try { | |
task.join(); | |
} catch (Throwable t) { | |
Log.d(TAG, t.getMessage()); | |
} | |
taskButton.setText("Ready"); | |
} | |
}); | |
// when the incrementButton is pressed, it will simply increment the counter | |
incrementButton.setOnClickListener(new View.OnClickListener() { | |
@Override | |
public void onClick(View v) { | |
textView.setText(Integer.toString(++counter)); | |
} | |
}); | |
} | |
} |
Here, upon the task button click, it launches the task, which is simulated by sleep(3000), on a separate thread so that we don't slow down the main UI thread. However, the problem is that we must wait for this task thread to complete because we need to update the button's text upon completion. Thus, we use join() method to wait for the task to complete. With this implementation, we find that the UI thread becomes unresponsive while waiting. This is NOT a good way of implementing the task.
The core problem is that we ask the task thread to carry out some heavy task, and we need to make sure that the main UI thread does not just sit and wait, but rather do its jobs on its own. The task thread then must communicate with the UI thread and let it know the task is complete, at which point, the UI thread can update the UI accordingly.
The Handler class in Android achieves exactly this, and below is modification that makes use of the Hander class.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.blogspot.unixnme.handlerexample; | |
import android.app.Activity; | |
import android.os.Bundle; | |
import android.os.Handler; | |
import android.os.Message; | |
import android.os.StrictMode; | |
import android.util.Log; | |
import android.view.View; | |
import android.widget.Button; | |
import android.widget.TextView; | |
public class HandlerExample extends Activity { | |
private static String TAG = HandlerExample.class.getSimpleName(); | |
private Button taskButton; | |
private Button incrementButton; | |
private TextView textView; | |
private int counter = 0; | |
private Handler handler = new Handler() { | |
@Override | |
public void handleMessage(Message msg) { | |
switch (msg.what) { | |
case 0: | |
// task is complete; update the UI | |
taskButton.setText("Ready"); | |
break; | |
} | |
} | |
}; | |
@Override | |
protected void onCreate(Bundle savedInstanceState) { | |
super.onCreate(savedInstanceState); | |
setContentView(R.layout.activity_handler_example); | |
textView = (TextView) findViewById(R.id.text_vew); | |
taskButton = (Button) findViewById(R.id.button); | |
incrementButton = (Button) findViewById(R.id.increment_button); | |
// when the taskButton is pressed, do some long task, simulated by sleep | |
taskButton.setOnClickListener(new View.OnClickListener() { | |
@Override | |
public void onClick(View v) { | |
taskButton.setText("Busy"); | |
new Thread(new Runnable() { | |
public void run() { | |
try { | |
Thread.sleep(3000); | |
} catch (Throwable t) { | |
Log.d(TAG, t.getMessage()); | |
} | |
// notify that the task is complete | |
handler.sendEmptyMessage(0); | |
} | |
}).start(); | |
} | |
}); | |
// when the incrementButton is pressed, it will simply increment the counter | |
incrementButton.setOnClickListener(new View.OnClickListener() { | |
@Override | |
public void onClick(View v) { | |
textView.setText(Integer.toString(++counter)); | |
} | |
}); | |
} | |
} |
As you can see, the task thread will do its job, and when the task is complete, it notifies the main UI thread that the job is done through handler. The handler is created in the main activity, so it is bound to the main UI thread. When the handler receives a message from the task thread that the job is complete, it will then update the button's text. In the meantime, the main UI task carries out its own tasks, such as incrementing the counter when the increment button is pressed.
Of course, one can implement this without using the Handler class, shown below. However, this solution is possible because we are communicating with the UI thread in this example. If, however, you have two different task threads that must communicate with each other, then you need to use the Handler.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.blogspot.unixnme.handlerexample; | |
import android.app.Activity; | |
import android.os.Bundle; | |
import android.util.Log; | |
import android.view.View; | |
import android.widget.Button; | |
import android.widget.TextView; | |
public class HandlerExample extends Activity { | |
private static String TAG = HandlerExample.class.getSimpleName(); | |
private Button taskButton; | |
private Button incrementButton; | |
private TextView textView; | |
private int counter = 0; | |
private HandlerExample instance; | |
@Override | |
protected void onCreate(Bundle savedInstanceState) { | |
super.onCreate(savedInstanceState); | |
setContentView(R.layout.activity_handler_example); | |
instance = this; | |
textView = (TextView) findViewById(R.id.text_vew); | |
taskButton = (Button) findViewById(R.id.button); | |
incrementButton = (Button) findViewById(R.id.increment_button); | |
// when the taskButton is pressed, do some long task, simulated by sleep | |
taskButton.setOnClickListener(new View.OnClickListener() { | |
@Override | |
public void onClick(View v) { | |
taskButton.setText("Busy"); | |
Thread task = new Thread(new Runnable() { | |
public void run() { | |
try { | |
Thread.sleep(3000); | |
} catch (Throwable t) { | |
Log.d(TAG, t.getMessage()); | |
} | |
instance.runOnUiThread(new Runnable() { | |
public void run() { | |
taskButton.setText("Ready"); | |
} | |
}); | |
} | |
}); | |
task.start(); | |
} | |
}); | |
// when the incrementButton is pressed, it will simply increment the counter | |
incrementButton.setOnClickListener(new View.OnClickListener() { | |
@Override | |
public void onClick(View v) { | |
textView.setText(Integer.toString(++counter)); | |
} | |
}); | |
} | |
} |
No comments:
Post a Comment