android in practice_Communicating change between threads(ImageDownloadWithMessagePassing)

android in practice_Communicating change between threads(ImageDownloadWithMessagePassing)_第1张图片

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.

android in practice_Communicating change between threads(ImageDownloadWithMessagePassing)_第2张图片

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;
	}
		
}

Both the handler creation and the callback method will be executed on the UI thread. In our download job, we then create a helper method D that prepares the message using a Bundle that holds the status text, and then dispatches the message via the handler object. (Think of a Bundle as being analogous to Java’s Map, but able to pass key-value-pairs even across thread or process boundaries.) We then use this helper in the run method to send our status updates E.These steps are executed on the download thread.

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.

你可能感兴趣的:(android in practice_Communicating change between threads(ImageDownloadWithMessagePassing))