Handler机制

Handler可以说是经常使用的一个知识点,基本上了解完Android中的异步就开始了解Handler的运作,对于这个类可以说熟悉,但是还没有好好总结过这个类的详细,今天就对它做一次好好的摸底。

Handler概念

Handler是android.os包下的一个类,主要用于线程之间的通信,可以将它看做一个消息处理器,一般会与Looper以及Message搭配使用达到线程通信的目的。

Handler、Looper、Message的关系

Handler、Looper、Message这三者是怎样相互配合完成消息的传递与处理的,可以先通过下图做一个简单的了解。
这里写图片描述

简单讲解一下,首先这幅图并不十分严谨,只是为了方便理解才画成这样,某线程内指的是Message队列Looper,而Handler如果也在同一线程下,那么也就没有太大存在的意义了。
再同一线程下最多有1个MessageQueue(也就是Message队列,以下统称)和1个Looper
MessageQueue负责处理线程内信息的存储
Looper负责对MessageQueue轮循查看获取
Handler通过sendMessage等方法向MessageQueue发送消息,handleMessage方法处理Looper取回的消息

消息发送及处理基本过程

handler发送消息到MessageQueue,由looper不断轮循查看队列,当发现有消息存在时,取出消息查找targetHandler,也就是向MessageQueue发送Message的handler。完成查找后,将消息交付给对应handler的handleMessage处理。


Handler的基本使用方法

Handler在使用过程中有以下几个比较常用的方法:

//以下几个方法都是用来向Message队列中发送消息的
1.sendEmptyMessage(int what);
2.sendMessage(Message msg);
3.sendMessageDelay(Message msg,int millis);
//以下方法是用来处理消息的
handleMessage(Message msg);

下面用一个简单的例子演示怎样使用handler

//例子1
//首先在一个Activity中
public class MyActivity extentds Activity{

    private MyHandler handler = null;

    protected void onCreate(Bundle saveInstanceState){
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_my);
        //创建消息对象
        Message msg = Message.obtian();
        //为消息对象设置内容
        msg.obj = "handler test";
        //设置msg标记
        msg.what = 0;
        //发送消息
        handle.sendMessage(msg);
    }

    class MyHandler extends Handler{
        public void handleMessage(Message msg){
            //标记较多时推荐使用switch
            //判断msg标记
            if(msg.what == 0){
                //获取msg内容
                String str = (String) msg.obj;
                //打印Log
                Log.i("TAG",str);
            }
        }
    }

}

通过以上的例子,就能够完成一次handler的消息发送与处理,将handler消息发送放置在子线程内就能够完成子线程与主线程的通信。

//例子2
//同样在一个Activity中
public class MyActivity extentds Activity{

    private MyHandler handler = null;

    protected void onCreate(Bundle saveInstanceState){
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_my);
        handler = new Handler(new Callback(){
            public boolean handleMessage(Message msg){
                //标记较多时推荐使用switch
                //判断msg标记
                if(msg.what == 0){
                //获取msg内容
                String str = (String) msg.obj;
                //打印Log
                Log.i("TAG",str);
                //这里的返回值是关键,当传入参数CallBack对象后,public boolean handleMessage能够先执行,并且返回值为false时,不再处理public void handleMessage方法,返回true则执行
                return false;
            }){
            public void handleMessage(Message msg){
                Log.i("TAG","void return")
            }
        };

        //创建消息对象
        Message msg = Message.obtian();
        //为消息对象设置内容
        msg.obj = "handler test";
        //设置msg标记
        msg.what = 0;
        //发送消息
        handle.sendMessage(msg);
    }
}

子线程中的Handler

之前描述的Handler都是在主线程中完成的,与子线程不同的是,主线程在创建完成后,在线程内就已经包含了Looper和MessageQueue,不需要进行手动创建,而需要在子线程中使用的时候,就需要自己创建这两个对象

//下面先演示一段自定义的子线程Handler,该段代码存在一个严重问题
//仅演示部分代码
MyThread thread = new Myheard();
thread.start();
//主线程使用子线程Looper向子线程传递信息
Handler handler = new Handler(thread.loop){
//问题点:在使用子线程Looper对象时,因为线程并发问题可能出现looper对象为空的情况
    public void handleMessage(Message msg){

    }
}

class MyThread extends Thread{

    public Handler handler;
    public Looper looper;

    public void run(){
        //创建Looper,同时会自动创建MessageQueue
        Looper.prepare();
        //获取Looper对象
        looper = Looper.myLooper();
        //Looper开始轮循
        Looper.loop();
    }
}

这样的自定义方式可能会存在Looper对象为空的情况,这样的话要如何处理,一般来说有线程同步问题的话就需要使用synchronize加锁了,不过对于Handler来说就不需要了,因为已经有完成了的HandleThread提供给我们使用。

HandlerThread

这就是能够使用在子线程中的Handler,并且已经完成了并发问题的处理,使用也比较简单。

HandlerThread thread = new HandlerThread("name"){
    public void run(){
        //子线程方法
    }
};
thread.start();

handler = new Handler(thread.getLooper()){
    public void handleMessage(Message msg){
        Log.i("TAG","HandlerThread test");
    }
};

子线程刷新UI

之前一直有一个固有的思维,那就是只有在UI线程中才能刷新UI,否则就会报错,知道前段时间看了一篇文章,才发现子线程中同样能够刷新UI,不过有一个指定的条件,那就是需要在onResume()之前进行刷新。
原因就在于ViewRootImp()这个方法,这是源码中的方法,用来判断线程是否是主线程,也就是说在判断之前子线程就进行UI刷新,这时候是不会报错的。

//在Activity中
protected void onCreate(Bundle saveInstanceState){
    super(saveInstanceState);
    setContentView(R.layout.xxxxxx);
    //创建子线程,用于刷新UI
    new Thread(new Runnable(){
        public void run(){
            textView.setText("test");
        }
    }).start();
}

//此时运行是由很大几率不会报错的,当然也有可能主线程一直占用资源到完成onResume(),不过几率很小。虽然这个方法能够达到目的,但是强烈不推荐这一种方法,毕竟还是存在不确定性。

通过上面的例子,介绍了一种子线程也能刷新UI的情况,事实上这样的刷新意义并不大,并且存在一定的风险(子线程刷新时主线程已经完成onResume())。所以,在子线程想要刷新UI还是推荐使用handler完成。


handleMessage的源码分析

通过以上的内容应该可以比较好的使用Handler了,对于Handler在sendMessage之后为什么能够执行对应的handleMessage,通过阅读源码就能够很好的了解这一过程中到底经历了哪些逻辑操作。
这里写图片描述

再一次拿出这张图片,事实上就是一次图片描述内容的一次解答。

开端是在handler.sendMessage(),也就是橙色线条,在这一过程中,提取出message对象,将handler对象赋值给message.target。
完成后通过queue调用enqueueMessage(),紫线上半部分,在这个方法中将message对象添加至MessageQueue中,将message对象赋值给Queue中某个message.next(由此看得出MessageQueue应该是链式结构)。
Looper对象调用loop方法后,紫线下半部分,将会开始轮询MessageQueue,当MessageQueue中不存在Message时,将会阻塞等待;获取到Message时,将会提取出Message对象
判断Message是否存在CallBack,蓝色线条,当存在CallBack时,执行CallBack后结束,当不存在CallBack时,将会取出Message.target,也就是handler,调用handleMessage(),完成执行。

以上就是handler执行sendMessage()到handleMessage()的基本过程,在源码部分执行经过的几个步骤总结。从这一过程可以总结出一下几点:
1.handler对象能够执行与本身对应的message是因为message.target保存了handler对象。
2.MessageQueue的结构是链式队列,选择这一结构也是因为MessageQueue更多的是增删,而不是查排。


关于Handler的介绍基本就到这了,如果还有关于Handler新的发现和收获都会发在这篇文里,欢迎大家交流学习。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值