android中的消息机制--浅谈Handler的原理及使用

handler是什么?
首先,android中为什么需要消息机制呢?我们知道进程是一个运行的代码单元,而一个进程中又有若干个线程,线程可以并发执行,同时也可以互相通信。我们在谈论android消息机制的时候,实质上就是在谈论android中线程和线程之间该如何通信,而线程通信中最常见的应用场景就是子线程与主线程(UI线程)之间的切换,而Handler正是android提供给我们的上层接口,只要掌握了Handler的原理和用法,也就理解了android中的消息机制。
这里有必要先解释一下,为什么主线程又叫UI线程,在android中,规定UI操作必须在主线程中进行,因为UI是线程不安全的,如果放在子线程中操作,由于多线程的并发访问,可能导致无法预期的结果。而通常我们要进行网络请求等耗时操作的时候,为了不阻碍UI线程的事件分发,导致ANR,就必须起一个子线程,操作完毕后,如果需要更改UI则又必须返回主线程,消息机制就很好的解决了这个问题。
那么,为什么handler可以实现消息机制呢?一句话概括就是,handler可以让一个执行体在其所在的线程中执行。因此,要想从一个子线程切换到UI线程,只需要在子线程中将一个执行体抛到UI线程中即可。听起来挺简洁,但真正实现确并非这么简单。

消息机制的流程
当然,要实现消息机制单靠Handler还是不够的,他还有另外两个好基友:MessegeQueue和Looper,下面分别来介绍一下。
MessageQueue直译过来就是:消息队列,没错,他就是一个用于存放和管理消息的“队列”,这里之所以加引号是因为消息队列在数据结构上并不是一个队列,而是一个单链表,之所以使用单链表主要原因是,MessageQueue管理消息的主要操作方式就是插入和读取并删除,显然,链表结构非常适合。
Looper翻译过来就是:循环,他做的事情就是不断的循环拿到消息,然后交给Handler处理。
前面提到,Handler可以将子线程中的执行体抛到UI线程中,然后在UI线程中执行相应逻辑,结合上面MessageQueue和Looper的简单介绍,我们就可以梳理出消息机制的整个流程了:
1.Handler在子线程中封装一个Message,然后sendMessage,post方法实质上最后还是调用了 sendMessage ,只是把post参数中的Runnable封装在Message中而已,即把想要抛到UI线程中的执行体赋给了Message的成员callBack;
2.Message被发出后,来到了MessageQueue, MessageQueue 先调用enqueueMessage方法将该Message插入到链表中,然后,再调用另外一个不断循环的方法next将Message取出同时在链表中删除;
3.取出之后,一直在循环“检查消息”的Looper发现有新消息,在Looper内部调用Handler的dispatchMessage方法处理Message。由于Looper所在的线程就是主线程,调用的dispatchMessage方法自然就能让执行体在UI线程中进行了,从而达到了线程切换的效果。
下面的图描述了消息机制的整个流程:
这里需要说明一下,我们在创建Handler的线程中,必须要有一个Looper,当然,由于主线程(ActivityThread)初始化时已经自动生成了Looper,而在主线程之外,则需要我们手动调用Looper.prepare()来创建Looper,然后Looper.loop(),如果没有Looper,sendMessage将返回false。
与创建对应的就是qiut,由于Looper干的事情就是检查新消息,然后处理,在没有新消息时,Looper处于阻塞状态,如果不手动qiut(qiutSafely)的话,线程就会一直处于等待状态。因此,必须在所有消息处理完毕后,调用qiut,从而通知MessageQueue调用qiut并退出,此时next方法会返回null,Looper结束。

另外,在处理消息时,通常的处理顺序为:
1.判断Message的Callback是否为空,Callback就是post方法中的Runnable,如果以post的方式抛出执行逻辑,就执行这个逻辑;
2.判断Handler的构造函数中的参数callback,我们通常采用派生Handler子类,重写handleMessage的方法来创建Handler,其实这里还有一种方式,使用含参数callback的构造函数创建Handler,重写里面的handleMessage,这样就不需要派生子类,如果是这样,就调用callback中的handleMessage;
3.如果前两个条件都不满足,调用Handler的handleMessage方法。

以上就是消息机制的实现流程及相关细节,接下来,我们看两个handler的常规用法:
当然,除了以下两种用法外,View的post方法和Activity的runOnUiThread方法也可以在子线程中进行UI操作,但归根结底他们内部还是通过Handler的post方法实现。

<pre class="java" name="code">public class MainActivity extends Activity {

	private Handler handler;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		handler = new Handler();
		new Thread(new Runnable() {
			@Override
			public void run() {
				handler.post(new Runnable() {
					@Override
					public void run() {
						// 在这里进行UI操作
					}
				});
			}
		}).start();
	}
}
<span style="font-size:14px;"></span>
<pre class="java" name="code"><pre class="java" name="code">public class MainActivity extends Activity {

    private Handler handler;
    private TextView tv;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main_activity);
        tv = (TextView)findViewById(R.id.tv); 

        handler = new Handler(){
            @Override
            public void handleMessage(Message msg) {
                if (msg.what == 1)
                tv.setText("helloWorld!");
            }
        };
        
        new Thread(new Runnable() {
            @Override
            public void run() {
                Message msg = new Message();
                msg.what = 1;
                handler.sendMessage(msg);
            }
        });
    }
}
 
 

                
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值