深入了解android中的消息机制Handler

什么是Handler?
handler是Android给我们提供用来更新UI的一套机制,也是一套消息处理机制.
我们可以使用它发送消息,也可以通过它处理消息.

我们为什么要使用Handler?
Android在设计的时候,就封装了一套消息创建,传递,处理机制,如果不遵循这样的机制,就没有办法更新UI,而且还会抛出异常信息.

例如:大家都知道,更新UI的操作一般都是放在main线程中,当我们需要在子线程中更新UI时,就需要使用到了Handler,虽然在子线程更新Ui的方法有好几种,但内部实现原理基本都是通过Handler发送消息处理的,不要着急,下面会提到.

Handler的使用:

  • sendMessage()方法的使用:
package com.hnthgys.mytext;

import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {
    private TextView tv;
    //创建main线程的Handler
    private Handler handler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            tv.setText("我是通过handler发送消息更新的");
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        tv = (TextView) findViewById(R.id.textview);
        //开启子线程
        new Thread(){
            @Override
            public void run() {
                super.run();
                //例如此处我们正在执行一个耗时操作,执行完毕后发送消息更新ui

                //发送一个空消息
                handler.sendEmptyMessage(0);

                //如果此处我们需要使用执行完耗时操作的数据,可以这样写
                //Message msg = handler.obtainMessage();
                //msg.obj = "数据";
                //handler.sendMessage(msg);

            }
        }.start();


    }
}
  • sendEmptyMessage(); 此方法和sendMessage使用一致,区别就是发送一个空消息.

  • sendMessageDelayed(); 发送一个延时执行的消息

  • post(Runnable);该方法可以在子线程中更新UI,该方法运行在main线程中

  • removeCallbacksAndMessages();移除回调和消息;例如:我们在使Handler轮播一些图片时,想让它停止轮播,就可以使用这个方法.

android为什么要设计只能通过handler来更新UI呢?

最根本的的目的就是解决多线程并发问题.假设如果在一个activity当中,有多个线程去更新UI,并且都没有加锁机制,那么会产生什么样子的问题呢? 更新界面错误.

你可能会说,我可以使用加锁的多线程啊,如果对更新UI的操作都进行加锁处理的话,应用程序的性能会大大下降.
处于对以上问题的考虑,Android给我们提供了一套更新UI的机制,我们只需要遵循这样的机制就可以了.
根本不用关心多线程的问题,所以更新UI的操作,都是在主线程的消息队列当中去轮询处理的.

Handler的原理是什么呢?
一,Handler封装了消息的发送,(主要包括消息发送给谁)
Looper(Handler内部自己的Looper)
1,内部包含一个消息队列,也就是MessageQueue,所有的Handler发送消息
都走向这个消息队列.
2,Looper.loop()方法,就是一个死循环,不断的从MessageQueue中取消息,
如果有消息就处理消息,没有消息就阻塞.

二,MessageQueue,就是一个消息队列,可以添加消息,并处理消息.

三,Handler,内部会跟Lopper进行关联,也就是说在Handler的
内部可以找到Looper,找到了Lopper也就找到 了MessageQueue,
在Handler中发送消息,其实就是向MessageQueue队列中发送消息.

Handler原理总结:
Handler负责发送消息,Loooper负责接收Handler发送的消息,
并直接把消息回传给Handler自己.
MessageQueue就是一个存储消息的容器.

Handler使用中遇到的问题:
在非UI线程中更新UI,抛出的异常:
这里写图片描述
在子线程创建Handler,抛出的异常:
这里写图片描述
注意:当需要在子线程中创建Handler时,需要先创建一个Looper,因为子线程中没有Looper对象

HandlerThread又是什么?

当我们向创建一个与线程相关的Handler时,我们可以使用HandlerThread,来解决多线程的并发问题.
这里写图片描述

子线程与主线程如何互发消息:

  • 主线程Handler向子线程发送消息(伪代码)
    这里写图片描述
  • 子线程Handler向主线程发送消息(伪代码)
    这里写图片描述

Android在子线程中更新UI的几种方式:
使用图片吧,以前做的笔记,看着感觉更加清晰..
这里写图片描述

非UI线程真的不能更新UI吗?
答案是能,对.你没有看错,非UI线程也能更新UI.可能你会觉得我在扯淡,下面看一段代码:

package com.hnthgys.mytext;

import android.os.Bundle;
import android.os.Handler;
import android.support.v7.app.AppCompatActivity;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {
    private TextView tv;
    //创建main线程的Handler
    private Handler handler = new Handler();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        tv = (TextView) findViewById(R.id.textview);
        //开启子线程
        new Thread(){
            @Override
            public void run() {
                super.run();
                //SystemClock.sleep(100);
                tv.setText("我是在子线程中更新的UI");
            }
        }.start();


    }
}

我把布局代码也贴出来,

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
   >


    <TextView
        android:id="@+id/textview"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="呵呵"
        android:textSize="40sp"
        />
</LinearLayout>

执行效果图:
这里写图片描述

可能看到这,你已经目瞪口呆了,这怎么可能,fuck,这完全颠覆了啊…..

主要原因:
当我们在更新UI时,Android中的ViewRootImpl类中的checkThread()方法会检查当前更新UI所在的线程,如图

3937    void More ...checkThread() {
            //检查执行更新UI所在的线程
3938        if (mThread != Thread.currentThread()) {
                //如果不在UI线程,就会抛出下面的异常,大家应该很眼熟吧
3939            throw new CalledFromWrongThreadException(
3940                    "Only the original thread that                 created a view hierarchy can touch its views.");
3941        }
3942    }

查看系统源码后,你会发现,ViewRootImpl类会在 Activity的onResume()方法执行完成后才初始化,这也就解释了上面代码能运行的原因了,但是,你发现没有,我们在子线程中没有做任何的耗时操作,如果我在子线程中添加这句代码:

SystemClock.sleep(100);

那么系统将会抛出异常:”Only the original thread that created a view hierarchy can touch its views.”
不能在非UI线程中更新UI.
那么问题来了,如果ViewRootImpl类没有初始化完成,那么view视图是如何显示出来的呢???我也正在解决中…….

另外,当我们在子线程中获取到ViewRoot,我们可以调用addView()方法在子线程中更新UI,这其中的详情就靠大家去探索了…..

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值