[android] Handler机制

Handler概念

主要用于异步消息的处理,它的设计通常用来处理相对耗时比较长的操作。

Android的单线程机制,只有主线程可以对UI(界面)进行更新,为了Android平台来说UI控件都没有设计成为线程安全类型,所以需要引入一些同步的机制来使其刷新。

由于Handler是运行在主线程中(UI线程中),  而且它的设计可以使它与子线程通过Message对象来传递数据, 这个时候,Handler就承担着接受子线程传过来的(子线程用sedMessage()方法传弟)Message对象,(里面包含数据)  , 把这些消息放入主线程队列中,配合主线程进行更新UI。

子线程对主线程发信息Message对象(Message包含的信息可以是int,object类型)


使用方法:在初始化Handler对象时重写的handleMessage方法来接收Message并进行相关操作。

	private Handler handler = new Handler() {
		public void handleMessage(android.os.Message msg) {};
	};

API上对这句的注释是:Handler子类必须实现这个方法以接收信息,在这个方法里可以实现执行更新UI界面(譬如更新ListView等操作)
<span style="color:#333333;">  /**
     * </span><span style="color:#ff0000;">Subclasses must implement this to receive messages.</span><span style="color:#333333;">
     */
    public void handleMessage(Message msg) {
    }</span>

耗时操作一般执行在非UI线程中,比如AsyncTask和AsyncTaskLoader都可以实现耗时操作,然后更新UI界面。这些也都是为了解决安卓线程设计上的问题的办法。

常用类

五个:Handler、Looper、Message、MessageQueue


Message:消息,其中包含了消息ID,消息处理对象以及处理的数据等,由MessageQueue统一列队,终由Handler处理。

Handler:处理者,负责Message的发送及处理。使用Handler时,需要实现handleMessage(Message msg)方法来对特定的Message进行处理,例如更新UI等。Handler类的主要作用:(有两个主要作用)1)、在工作线程中发送消息;2)、在主线程中获取、并处理消息。

MessageQueue:消息队列,用来存放Handler发送过来的消息,并按照FIFO规则执行。当然,存放Message并非实际意义的保存,而是将Message串联起来的,等待Looper的抽取。

Looper:消息泵,不断地从MessageQueue中抽取Message执行。因此,一个MessageQueue需要一个Looper。

Thread:线程,负责调度整个消息循环,即消息循环的执行场所。


如何使用

当我们开启线程请求了一段网络数据或数据库查询:

<pre name="code" class="java">new Thread(){
<span style="white-space:pre">	</span>public void run(){
<span style="white-space:pre">	</span>//下载数据
<span style="white-space:pre">	</span>..
<span style="white-space:pre">	</span>获得了一段字符串或cursor----》mMessage
<span style="white-space:pre">	</span>//发送到handlerMessage进行UI更新操作
<span style="white-space:pre">	</span>*******
<span style="white-space:pre">	</span>1、获取message对象
<span style="white-space:pre">	</span>2、给message对象传值
<span style="white-space:pre">	</span>3、发送message
<span style="white-space:pre">	</span>*******
<span style="white-space:pre">	</span>}
}.start();


Message

使用Message需要注意4点:
1、Message虽然也可以通过new来获取,但是通常使用Message.obtain()或Handler.obtainMessage()方法来从消息池中获得空消息对象,以节省资源;
2、如果一个Message只需要携带简单的int型数据,应优先使用arg1和arg2属性来传递数据,这样比其他方式节省内存;
3、尽可能使用Message.what来标识信息,以便用不同的方式处理Message;
4、如果需要从工作线程返回很多数据信息,可以借助Bundle对象将这些数据集中到一起,然后存放到obj属性中,再返回到主线程。

获取message对象:

  1. obtainMessage()---》Handler
  2. 通过构造方法Message()
  3. Obtain()--->Message

为message对象传值

message携带数据,有四个属性

arg1、arg2:携带int值

obj:携带一个对象---》Object

what:int,用来识别Messag

分发消息的一些方法(handler)

post(Runnable)可以传一个线程,这个线程运行在主线程中!所以事实上一些轻量级的操作,可以这样来实现,下面有介绍。

postAtTime(Runnable,long)

postDelayed(Runnable long)
sendEmptyMessage(int)
sendMessage(Message)
sendMessageAtTime(Message,long)

sendMessageDelayed(Message,long)

 这些方法底层经过层层调用最终调用的都还是sendMessageAtTime(),然后由此调用了Handler私有的函数enqueueMessage,把handler对象赋值给msg.target,调用queue.enqueueMessage,进入了消息队列机制中。

 以上post类方法允许你排列一个Runnable对象到主线程队列中,sendMessage类方法, 允许你安排一个带数据的Message对象到队列中,等待更新.

例子:更新进度条demo

发送一段数据:

new Thread(new Runnable() {
		@Override
		public void run() {
			for (int i = 0; i < 100; i++) {
				Message msg = handler.obtainMessage();
				msg.arg1 = i;		//arg1和arg2参数都是int类型
			<span style="white-space:pre">	</span>msg.what=1;<span style="white-space:pre">		</span>//设置消息的编号
				handler.sendMessage(msg);//发送数据
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}
	});

在handler里接收数据:

<span style="white-space:pre">	</span>// 处理消息-----》处理子线程发过来的消息;----handlerMessage,更新UI
	private Handler handler = new Handler() {
		public void handleMessage(Message msg) {
			int i = msg.arg1;
			progressbar.setProgress(i);//更新进度条
		};
	};


主线程给子线程发送消息:

原理一样,只是把handler的实例化放到了子线程,发送消息的方法执行在UI线程;

public class MainActivity extends Activity {
	private Handler handler;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		new Thread() {
			public void run() {
				Looper.prepare();
				handler = new Handler() {
					public void handleMessage(android.os.Message msg) {
						Toast.makeText(MainActivity.this,
								Thread.currentThread().getName(), 0).show();
					};
				};
			Looper.loop();	
			}}.start();
	}
	
	public void btnClick(View view)
	{
		handler.sendEmptyMessage(1);
	}
}


Looper

Looper对象用来为一个线程开启一个消息循环,从而操作MessageQueue;默认情况下,Android创建的线程没有开启消息循环Looper,但是主线程例外。系统自动为主线程创建Looper对象,开启消息循环;所以主线程中使用new来创建Handler对象。而子线程中不能直接new来创建Handler对象就会异常。
子线程中创建Handler对象,步骤如下:

Looper.prepare();
<span style="white-space:pre">	</span>Handler handler = new Handler() {
   <span style="white-space:pre">	</span> //handlemessage(){}
<span style="white-space:pre">	</span>}
   Looper.loop(); 

Looper的loop方法可以循环的从消息队列中监听消息,属于阻塞式操作。

 线程这块涉及的概念比较多,Looper,Message,Handler,由此衍生的有HandlerThread、java.util.concurrent、Task、AsyncTask

  Looper又是什么呢? 其实Android中每一个Thread都跟着一个Looper,Looper可以帮助Thread维护一个消息队列,但是Looper和Handler没有什么关系,我们从开源的代码可以看到Android还提供了一个Thread继承类HanderThread可以帮助我们处理,在HandlerThread对象中可以通过getLooper方法获取一个Looper对象控制句柄,我们可以将其这个Looper对象映射到一个Handler中去来实现一个线程同步机制,Looper对象的执行需要初始化Looper.prepare方法就是昨天我们看到的问题,同时推出时还要释放资源,使用Looper.release方法。

 简单来说就是:

 每一个线程只能有一个Looper对象Handler持有一个LooperHandler、Looper持有一个MessageQueue(消息队列)。


附:Handler、Looper、Message、MessageQueue之间的关系:







评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值