当前位置: 移动互联网学院 > Android开发 > Handler机制讲解
Handler机制讲解 时间:2017-05-10     来源:移动互联网学院

   Handler机制是Android中非常重要的一种消息处理机制,为什么会有handler机制?handler机制是怎样的一种工作流程及如何使用handler机制,本篇文章将对handler机制进行具体的阐述。

一、 Handler机制出现的原因

Android应用程序启动时,系统会创建一个主线程,负责与UI组件(widget、view)进行交互,比如控制UI界面界面显示、更新等;分发事件给UI界面处理,比如按键事件、触摸事件、屏幕绘图事件等,因此,Android主线程也称为UI线程。

由此可知,UI线程只能处理一些简单的、短暂的操作,如果要执行繁重的任务或者耗时很长的操作,比如访问网络、数据库、下载等,这种单线程模型会导致线程运行性能大大降低,甚至阻塞UI线程,如果被阻塞超过5秒,系统会提示应用程序无相应对话框,缩写为ANR,导致退出整个应用程序或者短暂杀死应用程序。

除此之外,单线程模型的UI主线程也是不安全的,会造成不可确定的结果。线程不安全简单理解为:多线程访问资源时,有可能出现多个线程先后更改数据造成数据不一致。比如,A工作线程(也称为子线程)访问某个公共UI资源,B工作线程在某个时候也访问了该公共资源,当B线程正访问时,公共资源的属性已经被A改变了,这样B得到的结果不是所需要的的,造成了数据不一致的混乱情况。

线程安全简单理解为:当一个线程访问功能资源时,对该资源进程了保护,比如加了锁机制,当前线程在没有访问结束释放锁之前,其他线程只能等待直到释放锁才能访问,这样的线程就是安全的。基于以上原因,Android的单线程模型必须遵守两个规则:

1.  不要阻塞UI线程;

2.  不要在UI线程之外访问UI组件,即不能在子线程访问UI组件,只能在UI线程访问。

因此,Android系统将大部分耗时、繁重任务交给子线程完成,不会在主线程中完成,解决了第一个难题;同时,Android只允许主线程更新UI界面,子线程处理后的结果无法和主线程交互,即无法直接访问主线程,这就要用到Handler机制来解决此问题。基于Handler机制,在子线程先获得Handler对象,该对象将数据发送到主线程消息队列,主线程通过Loop循环获取消息交给Handler处理。

二、 Handler机制工作的原理

想要搞明白Handler机制,首先我们要知道几个概念:

1. Message

     消息,理解为线程间通讯的数据单元。例如后台线程在处理数据完毕后需要更新UI,则可发送一条包含更新信息的Message给UI线程。

2. Message Queue

     消息队列,用来存放通过Handler发布的消息,按照先进先出执行。

3. Handler

     Handler是Message的主要处理者,负责将Message添加到消息队列以及对消息队列中的Message进行处理。

4. Looper

     循环器,扮演Message Queue和Handler之间桥梁的角色,循环取出Message Queue里面的Message,并交付给相应的Handler进行处理。

5. 线程

     UI thread 通常就是main thread,而Android启动程序时会替它建立一个Message Queue。

每一个线程里可含有一个Looper对象以及一个MessageQueue数据结构。在你的应用程序里,可以定义Handler的子类别来接收Looper所送出的消息。

当应用程序需要做耗时操作时,不能在主线程中进行,这是就需要开启子线程来处理耗时操作,子线程运行期间如果需要更改UI,但是更改UI的操作只能主线程来做,那么就需要使用Handler机制来处理,消息处理的流程如下:

子线程将需要处理的操作和数据封装成Message(消息)对象。

在子线程里用handler将消息发送到主线程,系统会将Message对象添加到MessageQueue(消息队列)中。

Looper(轮询器)对消息队列不停的轮询,当发现有消息时就交给handler进行处理。

在handler的handleMessage(Message msg)方法中来处理消息。

三、 Handler机制应用案例:点名程序

案例需求:

当点击开始按钮,在手机屏幕中间快速的随机的生成一个名字,当点击停止,随机生成名字操作完成。

案例代码:

1. 布局非常简单,这里不再贴出

2. java代码如下:

代码清单

public class MainActivity extends Activity implements OnClickListener {

 

protected static final int SUCCESS = 1;

Button btStart,btStop;

TextView tvName;

boolean flag;

String[] names = {"包浩然","李文波","王健","程凯旋","石林","潘岩","王秀","李清振","王剑","周万里","丁一","王瀚","张胜利","崔宁","陶良军","张红杰"

,"楚海鹏","李阔","黄庆坤","张赫强","苏都毕力格","刘晨","李孟","李宁","伍云贵","何邦哲"

,"冯英豪","涂智","唐云川","孟亚楠","田子豪","谢荣雄","贾鑫","脱瑞鹏" };

Handler handler = new Handler(){

public void handleMessage(Message msg) {

switch (msg.what) {

case SUCCESS:

String name = (String) msg.obj;

tvName.setText(name);

    break;

default:

tvName.setText("名字有误!");

break;

}

};

};

    @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main);

        btStart = (Button) findViewById(R.id.bt_start);

        btStop = (Button) findViewById(R.id.bt_stop);

        tvName = (TextView) findViewById(R.id.tv_name);

        

        btStart.setOnClickListener(this);

        btStop.setOnClickListener(this);    

    }

@Override

public void onClick(View v) {

switch (v.getId()) {

case R.id.bt_start:

flag = true;

new Thread(){

public void run() {

while (flag) {

Random random = new Random();

int index = random.nextInt(names.length);

Message msg =Message.obtain();

msg.what = SUCCESS;

msg.obj = names[index];

handler.sendMessage(msg);

}

};

}.start();

break;

case R.id.bt_stop:

flag = false;

break;

}

}

}