android--------Handler 消息传递机制

 请看链接:https://zhuanlan.zhihu.com/p/29612923


小议Handler

本文的思维导图:

众所周知,Handler是Android中用来处理异步的类,为什么有时候可以直接使用子线程,而有时候要使用Handler呢?网上有很多教程讲解Handler,个人认为,很多教程都将Handler复杂化,学会Handler的使用是一件非常简单的事。

1、为什么需要Handler?

我们有这样一个需求,在界面中有一个TextView,当系统启动时,每隔一秒就更改里面的文字为:“当前值: ” + i,其中i是一个变量,每次自增1。要实现这个需求,我们很容易想到如下思路:创建一个子线程,在子线程中定义变量i,进行循环,每次循环中首先sleep(1000),然后再设置TextView对象的文本,示例代码如下:

复制代码
 1 new Thread() {
 2     public void run() {
 3         try {
 4             for (int i = 0; i < 20; i++) {
 5                 Thread.sleep(1000);
 6                 tv.setText("当前的值为: " + i);
 7             }
 8         } catch (InterruptedException e) {
 9             e.printStackTrace();
10         }
11     };
12 }.start();
复制代码

运行代码,则会在LogCat中出现如下错误:

1 android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.

这句话的意思是:只有创建View的线程才可以操作这个View对象。这是什么意思呢?试想一下:当一个Activity中创建了很多个子线程,每个子线程都要修改同一个控件的内容,这时候将会变得混乱。

2、Android的消息机制

为了解决这种子线程之间同步互斥的问题,Android的设计人员就规定:创建当前Activity的线程就是主线程,也称为UI线程;只有这个主线程可以更新界面中的内容。OK!如果子线程也要更新界面中的内容,此时应该怎么处理呢?Android中使用了消息机制。当子线程要对界面UI进行更新时,要发送一个消息给主线程,主线程中有一个消息队列,消息到达主线程之后便进入消息队列中。主线程通过一个轮循器,检查消息队列中是否有消息,如果有消息,主线程便会执行这个消息。而这种发送消息、处理消息便封装为一个对象,那就是Handler。

3、使用Handler

换句话说,子线程负责通过Handler对象的sendMessage()方法发送消息给主线程,而UI更新操作,则是由主线程调用Handler对象的handleMessage()方法来完成。

上面的需求就可以如下实现:

复制代码
 1 public class DemoActivity extends Activity {
 2     private TextView tv = null;
 3     // 定义Handler对象来发送和处理消息
 4     private Handler handler = new Handler() {
 5         // 主线程通过这个方法处理消息
 6         @Override
 7         public void handleMessage(Message msg) {
 8             tv.setText("当前的值为:" + msg.obj);
 9         }
10     };
11 
12 public void onCreate(Bundle savedInstanceState) {
13         super.onCreate(savedInstanceState);
14         setContentView(R.layout.main);
15         tv = (TextView) this.findViewById(R.id.tv);
16         // 创建一个子线程
17         new Thread() {
18             public void run() {
19                 try {
20         //子线程中只发送更新UI的消息,不进行更新操作
21                     for (int i = 0; i < 20; i++) {
22                         Thread.sleep(1000);
23                         Message msg = new Message();
24                         msg.obj = i;
25                         handler.sendMessage(msg);
26                     }
27                 } catch (InterruptedException e) {
28                     e.printStackTrace();
29                 }
30             };
31         }.start();
32 }
复制代码

注意Handler中sendMessage()和handleMessage()两个方法的执行顺序:首先是创建了子线程,然后子线程调用handler的sendMesssage()方法向主线程发送消息,之后主线程收到消息,调用handler的handleMessage()方法来处理消息。

4、例子

模拟下载的进度条:

复制代码
public class DemoActivity extends Activity {
    // 定义进度条的最大值和当前值
    private int max = 100;
    private int num = 0;
    
    private ProgressBar pb = null;
    private TextView tv_progress = null;
    
    private Handler pbHandler = new Handler() {
        public void handleMessage(Message msg) {
            int num = (Integer) msg.obj;
            pb.setProgress(num);
            double rate = ((double)num) / max * 100;
            String text = "已下载:" + rate + "%";
            if (num >= max) {
                text = "恭喜你,下载成功!";
            }
            tv_progress.setText(text);
        };
    };
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        pb = (ProgressBar) this.findViewById(R.id.pb);
        pb.setMax(max);
        tv_progress = (TextView) this.findViewById(R.id.tv_progress);
        new MyThread().start();
    }
    class MyThread extends Thread {
        public void run() {
            try {
                while (num < max) {
                    Thread.sleep(1000);
                    num += 5;
                    Message msg = new Message();
                    msg.obj = num;
                    pbHandler.sendMessage(msg);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        };
    }
}
复制代码

布局文件如下:

复制代码
 1 <?xml version="1.0" encoding="utf-8"?>
 2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 3     android:layout_width="fill_parent"
 4     android:layout_height="fill_parent"
 5     android:orientation="vertical" >
 6     <ProgressBar
 7         android:id="@+id/pb"
 8         style="?android:attr/progressBarStyleHorizontal"
 9         android:layout_width="match_parent"
10         android:layout_height="wrap_content" />
11     <TextView
12         android:id="@+id/tv_progress"
13         android:textSize="20sp"
14         android:layout_width="fill_parent"
15         android:layout_height="wrap_content"
16         android:text="@string/hello" />
17 
18 </LinearLayout>
复制代码

总结

1、 只有主线程才能更新UI;

2、 子线程如果需要更新UI,可以发送消息给主线程,让主线程更新;

3、 Handler可通过sendMessage()发送消息,通过handleMessage()处理消息。



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值