by design it’s impossible to update user interface elements from outside the main UI thread.
if you’re sharing state between two or more threads you always need to synchronize this shared data using synchronization primitives such as Java’s synchronize and volatile keywords, or a Lock object.
we highly recommend Java Concurrency in Practice by Brian Goetz.
we’re not allowed to update the user interface from a worker thread directly, so there’s no way we can update progress that way. Clearly, we need a way to communicate with the UI thread from another thread, so that we can send our update messages and have it react to them.
You could store progress information in a shared variable and access it from both threads: the worker thread writes to it, and the UI thread periodically reads from it.
But this would require us to synchronize access to it,
It turns out that there’s an easier way to do these things on Android—Android’s messagepassing facilities. This approach uses message queues to allow interthread communication in a controlled, thread-safe manner. Progress information can therefore be passed from a worker to the UI thread by posting update messages to the UI thread’s message queue using Android’s Handler and Message classes.
A handler is an object that can be bound to an arbitrary thread (the handler thread). The handler can then be used by other threads to send messages to or even execute
code on the handler thread. Binding is implicit: a handler is always bound to the thread in which it’s being instantiated. If, for instance, a handler is bound to the UI thread, it’ll start monitoring that thread’s message queue. A second thread (the worker) can then use the handler object to send messages to the UI thread by calling its sendMessage(Message) method, or even ask it to execute a method on the UI thread by calling the post(Runnable) method. No additional synchronization is
needed—it just works!
the main UI thread maintains a message loop from which messages can be routed to a Handler.
The receiving thread reacts by implementing the handleMessage(Message) method defined by the Handler.Callback interface. A common approach is to let an activity
implement Handler.Callback and configure the handler object as the object responsible for processing a message.
1 Create a Handler object and bind it to the UI thread.
2 Implement the Handler.Callback interface, for example on the Activity.
3 From the download thread, use the handler object to send a message containing the new status text to the UI thread.
4 In the callback method, read the status text form the message object and update the text view.
Message passing can be used to communicate state between threads
//Implement callback interface public class ImageDownloadWithMessagePassing extends Activity implements Handler.Callback{ private Handler handler=new Handler(this);//Create/bind handler private Runnable imageDownloader=new Runnable(){ //Helper to send status message private void sendMessage(String what){ Bundle bundle=new Bundle(); bundle.putString("status", what); Message message=new Message(); message.setData(bundle); handler.sendMessage(message); } @Override public void run() { // TODO Auto-generated method stub //Call helper sendMessage("Download started"); try{ URL imageUrl=new URL("http://www.android.com/images/froyo.png"); Bitmap image=BitmapFactory.decodeStream(imageUrl.openStream()); if (image != null) { sendMessage("Successfully retrieved file!"); } else { sendMessage("Failed decoding file from stream"); } }catch(Exception e){ sendMessage("Failed downloading file!"); e.printStackTrace(); } } }; public void startDownload(View source) { new Thread(imageDownloader, "Download thread").start(); } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); } @Override public boolean handleMessage(Message msg) { // TODO Auto-generated method stub String text=msg.getData().getString("status"); TextView statusText=(TextView)findViewById(R.id.status); statusText.setText(text); return true; } }
Because the callback is executed on the UI thread, and a Bitmap is parcelable (it implements the Parcelable interface), we could stick the bitmap into the bundle and pass it over to the callback, too! That way we could immediately update an ImageView using the downloaded image.Recall that message passing doesn’t mean we invoke the callback directly. Instead, we post the message to a message queue, which means that queue must be polled periodically by the receiving thread to check for new messages. It turns out that Android handles this for us by automatically creating a message loop for the application’s UI thread. If we were to pass messages between two custom threads instead, then we’d have to handle this ourselves.
clicking on the button will always start a new download thread, without us having any control over how many threads run at once. threads are expensive to create and handle. It would be nice to gain more control over how threads are managed.