前言
Android的消息传递机制是另外一种形式的“事件处理”,这种机制主要是为了解决Android应用中多线程的问题,在Android中不允许Activity新启动的线程访问该Activity里的UI组件,这样会导致新启动的线程无法改变UI组件的属性值。但实际开发中,很多地方需要在工作线程中改变UI组件的属性值,比如下载网络图片、动画等等。本篇博客主要介绍Handler是如何发送与处理线程上传递来的消息,并讲解Message的几种传递数据的方式,最后均会以小Demo来演示。
Handler
Handler,它直接继承自Object,一个Handler允许发送和处理Message或者Runnable对象,并且会关联到主线程的MessageQueue中。每个Handler具有一个单独的线程,并且关联到一个消息队列的线程,就是说一个Handler有一个固有的消息队列。当实例化一个Handler的时候,它就承载在一个线程和消息队列的线程,这个Handler可以把Message或Runnable压入到消息队列,并且从消息队列中取出Message或Runnable,进而操作它们。
Handler主要有两个作用:
- 在工作线程中发送消息。
- 在UI线程中获取、处理消息。
上面介绍到Handler可以把一个Message对象或者Runnable对象压入到消息队列中,进而在UI线程中获取Message或者执行Runnable对象,所以Handler把压入消息队列有两大体系,Post和sendMessage:
- Post:Post允许把一个Runnable对象入队到消息队列中。它的方法有:post(Runnable)、postAtTime(Runnable,long)、postDelayed(Runnable,long)。
- sendMessage:sendMessage允许把一个包含消息数据的Message对象压入到消息队列中。它的方法有:sendEmptyMessage(int)、sendMessage(Message)、sendMessageAtTime(Message,long)、sendMessageDelayed(Message,long)。
从上面的各种方法可以看出,不管是post还是sendMessage都具有多种方法,它们可以设定Runnable对象和Message对象被入队到消息队列中,是立即执行还是延迟执行。
Post
对于Handler的Post方式来说,它会传递一个Runnable对象到消息队列中,在这个Runnable对象中,重写run()方法。一般在这个run()方法中写入需要在UI线程上的操作。
在Handler中,关于Post方式的方法有:
- boolean post(Runnable r):把一个Runnable入队到消息队列中,UI线程从消息队列中取出这个对象后,立即执行。
- boolean postAtTime(Runnable r,long uptimeMillis):把一个Runnable入队到消息队列中,UI线程从消息队列中取出这个对象后,在特定的时间执行。
- boolean postDelayed(Runnable r,long delayMillis):把一个Runnable入队到消息队列中,UI线程从消息队列中取出这个对象后,延迟delayMills秒执行
- void removeCallbacks(Runnable r):从消息队列中移除一个Runnable对象。
下面通过一个Demo,讲解如何通过Handler的post方式在新启动的线程中修改UI组件的属性:
1 package com.bgxt.datatimepickerdemo; 2 3 import android.app.Activity; 4 import android.os.Bundle; 5 import android.os.Handler; 6 import android.view.View; 7 import android.widget.Button; 8 import android.widget.TextView; 9 10 public class HandlerPostActivity1 extends Activity { 11 private Button btnMes1,btnMes2; 12 private TextView tvMessage; 13 // 声明一个Handler对象 14 private static Handler handler=new Handler(); 15 16 @Override 17 protected void onCreate(Bundle savedInstanceState) { 18 super.onCreate(savedInstanceState); 19 setContentView(R.layout.message_activity); 20 21 btnMes1=(Button)findViewById(R.id.btnMes1); 22 btnMes2=(Button)findViewById(R.id.btnMes2); 23 tvMessage=(TextView)findViewById(R.id.tvMessage); 24 btnMes1.setOnClickListener(new View.OnClickListener() { 25 26 @Override 27 public void onClick(View v) { 28 // 新启动一个子线程 29 new Thread(new Runnable() { 30 @Override 31 public void run() { 32 // tvMessage.setText("..."); 33 // 以上操作会报错,无法再子线程中访问UI组件,UI组件的属性必须在UI线程中访问 34 // 使用post方式修改UI组件tvMessage的Text属性 35 handler.post(new Runnable() { 36 @Override 37 public void run() { 38 tvMessage.setText("使用Handler.post在工作线程中发送一段执行到消息队列中,在主线程中执行。"); 39 } 40 }); 41 } 42 }).start(); 43 } 44 }); 45 46 btnMes2.setOnClickListener(new View.OnClickListener() { 47 48 @Override 49 public void onClick(View v) { 50 new Thread(new Runnable() { 51 @Override 52 public void run() { 53 // 使用postDelayed方式修改UI组件tvMessage的Text属性值 54 // 并且延迟3S执行 55 handler.postDelayed(new Runnable() { 56 57 @Override 58 public void run() { 59 tvMessage.setText("使用Handler.postDelayed在工作线程中发送一段执行到消息队列中,在主线程中延迟3S执行。"); 60 61 } 62 }, 3000); 63 } 64 }).start(); 65 66 } 67 }); 68 } 69 }