Handler、HandlerThread理解

Refrence

https://blog.csdn.net/bukker/article/details/80601779

Hanlder和Looper的关系
Handler在android线程编程中非常常见。线程中的handler使用原理:

每个线程只有一个Looper来管理消息队列,handler在使用的时候需要绑定到对应的Looper上。Handler给自己绑定的Looper不断的发送消息,Looper来做死循环来不断读取MessageQueue队列中的消息,发送给handler来进行处理。

Android的UI是运行在主线程中,主线程是用MainLooper来管理,循环读取MessageQueue队列中消息的,如果创建Handler对象new Handler( )时构造的时候参数没有指定绑定的Looper,默认是和主线程的Looper绑定在一起。Handler发送和处理消息是默认在主线程中进行的。

应用中UI是在主线程中进行绘制的,为了保证用户和UI交互的流畅,软件中常常耗时的动作,如网络的操作、IO的读取、数据的处理等单独的放在子线程中去处理。比如通过异步的获取数据,获取完成后通过使用主线程的handler来发送msg给主线程的MainLooper队列来通知主线程再进行UI刷新。

Android原生的HandlerThread为我们提供了一种思路HandlerThread,HandlerThread实质是Android封装的一个Thread。

  1. new一个HandlerThread对象,实质是创建线程,然后必须通过start()方法把此线程运行起来;
  2. new 一个Handler对象,在构造Handler对象的时候,把此thread的Looper作为参数传递给我们构造的Handler对象;这样我们创建的Handler对象就和此thread线程的Looper绑定起来;
  3. 此时我们创建的Handler对象就可以给thread的MessageQueue队列发送消息和处理消息了,处理消息是运行在子线程中,可以做耗时的操作,不会阻塞UI线程。等在子线程做完耗时的动作获取完数据后就可以通过主线程的handler发消息给主线程来更新当前UI界面;
  4. 用完后,需要来停止此thread的Looper循环,防止内存泄露。

参考示例:

  1. 创建需要的变量

    private BtSwitchHandler btSwitchHandler;
    private HandlerThread checkBtSwitchThread;
    private long checkBtSwitchDelayTime = 45 * 1000;
    private static final int MSG_CHECK_BT_SWITCH = 0x20;
    
  2. 开始构造HandlerThread对象和构造Handler对象,并发送Message消息

    private void checkBtSwitchIsOpenThread()
    {
        L.d(TAG, "checkBtSwitchIsOpenThread start");
        if (checkBtSwitchThread == null)
            checkBtSwitchThread = new HandlerThread("checkBtSwitch");
        checkBtSwitchThread.start();
        if (btSwitchHandler == null)
            btSwitchHandler = new BtSwitchHandler(checkBtSwitchThread.getLooper());
        btSwitchHandler.removeCallbacksAndMessages(null);
        btSwitchHandler.sendEmptyMessageDelayed(MSG_CHECK_BT_SWITCH, checkBtSwitchDelayTime);
    }
    
  3. 创建内部类Handler类处理消息,运行在子线程

    private class BtSwitchHandler extends Handler
    {
        public BtSwitchHandler(Looper looper)
        {
            super(looper);
        }
        @Override
        public void handleMessage(Message msg)
        {
            if (msg.what == MSG_CHECK_BT_SWITCH)
            {
                if (mBluetooth.isEnabled())
                {
                    L.d(TAG, "bt open success");
                } else
                {
                    L.d(TAG, "bt switch open 45s timeout, show failer view");
                    onekeystate = ONEKEYSTATE.ERROR;
                    Message errMsg = mHandler.obtainMessage(MSG_ERROR);
                    mHandler.sendMessage(errMsg);
                }
                stopBtSwitchThread();
            }
            super.handleMessage(msg);
        }
    }
    
  4. 注销HandlerThread,销毁loop循环

    private void stopBtSwitchThread()
    {
        if (checkBtSwitchThread != null)
        {
            Looper btSwitchLooper = checkBtSwitchThread.getLooper();
            if (btSwitchLooper != null)
            {
                btSwitchLooper.quit();
            }
        }
        checkBtSwitchThread = null;
        btSwitchHandler = null;
    }
    

补充:

  1. Handler.post(Runnable r)时,看大家都在讲run函数处理运行在UI主线程中。

    (此种用法一般不常使用,一般适合于你不想利用handler发msg来处理更新UI时,可以这样简单的在主线程直接去更新一下UI,相当于将Runnable直接放到了主线程的Looper中进行直接处理)

    参考blog:

    但我在使用过程中发现有个handler对象的post运行run函数的时候看日志发现并没有运行在主线程,和基本原理相悖,比较诡异,查了好久并没有发现有在其它地方有重置此handler对象的looper。

    而在创建handler对象的时候,必须指定handler绑定的线程的looper。如果在主线程创建handler对象,没有指定主线程looper时,系统会帮我们默认将主线程的looper绑定到handler对象;如果在非主线程中创建handler对象,没有指定线程的looper时,直接会报错,程序会退出,报错没有looper,必须先执行looper.prepare(),或者需要将线程的looper传给handler对象来进行创建。

    请教别人说,handler必须有对应线程的looper来和它绑定,线程中的looper会不断读取MessageQueue队列中的msg来处理,绑定looper后handler才能具有和收发能力,否则原理是讲不通的。

    我发现的handler的post的run函数运行在子线程,确定是此handler对象创建时它的looper参数在创建的时候被在子线程中设置了,looper参数传的子线程的looper才会出现这种现象。

     Log.d("zws", "new myloop ? " + (Looper.myLooper().toString()));
     Log.d("zws", "new isMainLoop ? " + (Looper.myLooper() == Looper.getMainLooper()));
     Log.d("zws", "thread name? " + Thread.currentThread().getName());
    

    加打印后,可以直接将创建handler时的looper打印出来,发现是在子线程中的一处插件进行设置的。验证了此想法是正确的,所以post的run函数才会运行在子线程。否则,一般都运行在主线程。

  2. Thread和handler、HandlerThread

  • Thread就是一般的线程,可以通过起一个线程来执行做事情,一般适合于不和UI相关的事情,耗时的事情。

    new Thread(new Runnable() 
    {
        @Override
        public void run() 
        {
            
        }
    }).start();
    
  • handler用来线程间的通信,和更新UI相关

    1. 可以在子线程中来利用handler来给主线程发消息来通知主线程更新UI;
    2. 利用handler来在主线程延时执行一段程序。
    public class TestHandler extends Handler
    {
        private static TestHandler ourInstance = new TestHandler();
        private TestRunnable mTestRunnable = null;
        private static final long DELAY_TIME = 30 * 1000;
    
        public static TestHandler getInstance()
        {
            return ourInstance;
        }
    
        private TestHandler()
        {
    
        }
    
        public void startTest()
        {
            if (mTestRunnable != null)
            {
                stopTest();
            }
            mTestRunnable = new TestRunnable();
            this.postDelayed(mTestRunnable, DELAY_TIME);
        }
    
        public void stopTest()
        {
            if (mTestRunnable == null)
            {
                return;
            }
            this.removeCallbacks(mTestRunnable);
            mTestRunnable = null;
        }
    
        private class TestRunnable implements Runnable
        {
    
            @Override
            public void run()
            {
                Log.d("zws", "TestRunnale ....");
            }
        }
    }
    

    利用handler延时开启和关闭线程,只需要做下面操作:

    • TestHandler.getInstance().startTest(); 开启线程
    • TestHandler.getInstance().stopTest(); 关闭线程

    参考blog:android利用Handler开启线程和关闭线程

  • HandlerThread:那么现在我们要是想子线程与子线程之间的通信要怎么做呢?

当然说到底也是用Handler+Thread来完成(不推荐,需要自己操作Looper),Google官方很贴心的帮我们封装好了一个类,那就是刚才说到的:HandlerThread。

参考blog,讲的非常清楚:Thread、Handler和HandlerThread关系何在?


有些理解不太到位,后续继续补充…

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值