Handler机制干货(自己玩)

本文详细解析了Android中的Handler机制,包括其组成元素、工作原理及如何避免子线程更新UI的问题。Handler机制对于实现进程间通信至关重要,并能有效防止程序因ANR导致的崩溃。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

         handler机制主要用于进程间通信,google规定主线程做耗时操作,子线程不能更新UI。而且他还能避免程序因为ANR导致的crash。ANR的主要原因是:activity5s内未响应下一事件,BroadcastReceive10s内未响应,比如一些网络请求、大文件读取以及数据的计算都有可能引发。在子线程中更新ui会报这个错误:
android.view.ViewRootImpl$CalledFromWrongThreadException: 
Only the original thread that created a view hierarchy can touch its views.

 大概意思就是谁创建的view谁更改,别人不能碰。

 为什么系统不能让子线程更新ui呢:

我感觉主要是UI控件不是线程安全的,多线程并发访问可能会导致ui控件处于不可预期的状态。 那为什么不对UI控件的访问加上 上锁机制 呢:

     1.主要是加个锁后会使UI控件变得复杂而低效

     2.上锁后会阻塞某些进程的执行,这对手机系统是不可接受的,所以采取简单高效的方法就是单线程模型来处理UI控件,通过 Handler切换一下访问的线程的就好    。

  handler机制主要由四部分构成:

message消息对象   messagequeue消息队列   looper轮询器    handler处理消息 或者发送消息给另一个messagequeue 。

工作机制就是:handler中的messagequeue中的enqueueMessage插入一条信息到messagequeue中,然后looper不断轮询Messagequeue的next()方法,如果发现message调用handeler的dispatchMessge,idispatchMessage处理消息,接着调用handlermessage()。

messagequeue消息队列内部是链表结构,先进先出,有入队,出队方法。

looper轮询器主要作用是一直轮询messagequeue,发现message就交给handler处理 ,没有消息就一直处于阻塞状态。在主线程中系统自动提供looper 。而在子线程中创建handler一定要写looper.prepare()否则会报"Can't create handler inside thread that has not called Looper.prepare()"); ,,因为线程中使默认没有looper的,主线程除外。looper里面有一个Threadlocal,主要是帮助handler获得当前线程的looper。

looper.quit() 直接退出looper

looper.quitSafely()   建立一个标记,只有当目前已有消息处理完毕之后才会执行退出操作

Handler是一个消息辅助类,主要负责向消息池发送各种消息事件Handler.sendMessage() 和处理相应的消息事件Handler.handleMessage()



HandlerThread 是 Android API 提供的一个便捷的类,使用它可以让我们快速地创建一个带有 Looper 的线程,有了 Looper 这个线程,我们又可以生成 Handler,本质上也就是一个建立了内部 Looper 的普通 Thread

他继承自thread  ,所以在run()中的逻辑都是在子线程中运行的。

public class HandlerThread extends Thread {
    int mPriority;
    int mTid = -1;
    Looper mLooper;

    public HandlerThread(String name) {
        super(name);
        mPriority = Process.THREAD_PRIORITY_DEFAULT;
    }


    public HandlerThread(String name, int priority) {
        super(name);
        mPriority = priority;
    }


    protected void onLooperPrepared() {
    }

    @Override
    public void run() {
        mTid = Process.myTid();
        Looper.prepare();
        synchronized (this) {
            mLooper = Looper.myLooper();
            notifyAll();
        }
        Process.setThreadPriority(mPriority);
        onLooperPrepared();
        Looper.loop();
        mTid = -1;
    }


    public Looper getLooper() {
        if (!isAlive()) {
            return null;
        }

        // If the thread has been started, wait until the looper has been created.
        synchronized (this) {
            while (isAlive() && mLooper == null) {
                try {
                    wait();
                } catch (InterruptedException e) {
                }
            }
        }
        return mLooper;
    }


    public boolean quit() {
        Looper looper = getLooper();
        if (looper != null) {
            looper.quit();
            return true;
        }
        return false;
    }


    public boolean quitSafely() {
        Looper looper = getLooper();
        if (looper != null) {
            looper.quitSafely();
            return true;
        }
        return false;
    }


    public int getThreadId() {
        return mTid;
    }
}

源码里有两个重要的方法run()和getLooper():

run()中可以看到是很简单的创建Looper以及让Looper工作的逻辑。
run()里面当mLooper创建完成后有个notifyAll(),getLooper()中有个wait(),这有什么用呢?因为的mLooper在一个线程中执行创建,而我们的handler是在UI线程中调用getLooper()初始化的。
也就是说,我们必须等到mLooper创建完成,才能正确的返回。getLooper();wait(),notify()就是为了解决这两个线程的同步问题。







   

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值