Handler机制

本文详细解析了Handler在Android中的作用,包括如何在子线程处理耗时操作并通过MessageQueue和Looper进行通信,以及Looper.prepare()和loop()方法的工作原理。重点讲解了Handler的创建、消息发送和处理机制,以及线程切换背后的原理。

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

一些基础:

handler主要用于在子线程中经行耗时操作避免阻塞UI线程再把消息发送到UI线程再经行处理,handler机制中海包括了Looper,MessageQueue,ThreadLocal等。

Looper:

handler一般的写法为:

    private Handler handler = new Handler(Looper.myLooper()) {
        @Override
        public void handleMessage(@NonNull Message msg) {
            //在这里即可以做一些UI操作了。。。
        }
    };
        new Thread(new Runnable() {
            @Override
            public void run() {
                //开启一个线程这里做一些耗时操作
                Message message = Message.obtain();
//                message.setData(...);
//                message.what=...
                handler.sendMessage(message);
            }
        }).start();

这样就实现了在子线程中经行耗时操作,把结果通过handler、message发送到UI线程中再做处理,在我们平常使用中一般不需要去声明一个looper或者messageQueue因为一般我们的handler都是写在主线程中的,Android的入口是ActivityThread的main方法在那里面已经帮我们声明好了一个looper。当然我们的handler声明在一个子线程的话则必须显示的声明一个Looper 比如:

        new Thread(new Runnable() {
            @Override
            public void run() {
                Looper.prepare();//获取当前线程的looper
                Handler handler = new Handler(Looper.myLooper()) {
                    @Override
                    public void handleMessage(@NonNull Message msg) {

                    }
                };
                Message message = Message.obtain();
                handler.sendMessage(message);
                Looper.loop();//looper开始处理消息
            }
        }).start();

这里出现了两个方法Looper.prepare()和Looper.loop()两个方法,下面是Looper.prepare方法的源码:

 public static void prepare() {
        prepare(true);
    }
    private static void prepare(boolean quitAllowed) {
        //通过sThreadLocal.get()去判断要是拿到的looper对象不为空的话则抛出异常
        //因为一个线程中只能有一个looper,这里后面再讨论
        //当取到的looper对象为空会会调用方法 new Looper(quitAllowed)
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed));
    }

    //这里又声明了一个MessageQueue对象,这样就保证了一个线程一个looper一个MessageQueue
    private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }

下面是loop()方法的部分源码:

    public static void loop() {
        //mylooper()方法就一段代码sThreadLocal.get();
        //sThreadLocal.get();返回了之前set进去的looper
        final Looper me = myLooper();
        .......

        //拿到了looper就拿到了对应的MessageQueue。
        final MessageQueue queue = me.mQueue;

        //一直循环的取messageQueue中的message对象
        for (;;) {
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }

            try {
                //通过这句代码把取出的message对象转发到了handler中
                //这里的target其实就是handler后面会再分析
                msg.target.dispatchMessage(msg);
                if (observer != null) {
                    observer.messageDispatched(token, msg);
                }
                dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
            } catch (Exception exception) {    
           
        }
    }

handler:

在之前的代码中看到new Handler中传入了Looper.myLooper()

 public static @Nullable Looper myLooper() {
        //这里就返回了之前set进去的looper对象
        return sThreadLocal.get();
    }
 //构造方法
 public Handler(@NonNull Looper looper) {
        this(looper, null, false);
    }
    
    //this(looper, null, false);会调用这里的方法
    //在这里不仅初始化了mLooper,还通过looper拿到了对应的MessageQueue对象
    @UnsupportedAppUsage
    public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) {
        mLooper = looper;
        mQueue = looper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

 

在handler中用个sendMessage方法点进源码查看会一路调用各种sendMesage。。。最后会来到sendMessageAtTime方法

   //这里的msg就是我们在外部发送的那个message对象
 public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {
        MessageQueue queue = mQueue;
        if (queue == null) {
            RuntimeException e = new RuntimeException(
                    this + " sendMessageAtTime() called with no mQueue");
            Log.w("Looper", e.getMessage(), e);
            return false;
        }
        return enqueueMessage(queue, msg, uptimeMillis);
    }

    private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
            long uptimeMillis) {
        //这里就和之前looper.loop()方法对应上了,target就是handler
        msg.target = this;
        msg.workSourceUid = ThreadLocalWorkSource.getUid();

        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        //把这个msg压入messageQueue中通过looper.loop取获取再经行处理
        return queue.enqueueMessage(msg, uptimeMillis);
    }

现在知道之前的looper.loop()方法最终会调用到handler.dispatchMessage(msg)方法

    public void dispatchMessage(@NonNull Message msg) {
        //msg.callback这个属性是runnable,是handler.post方法用的
        //我们之前的写法没有去设置这个属性所以它是个null所以会执行
        //handleMessage这个方法,而这个方法就是我们在外部重写的方法
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }
        //这就是平时最简单的handler的post写法
        new Handler(Looper.myLooper()).postDelayed(new Runnable() {
            @Override
            public void run() {

            }
        },300);
     public final boolean postDelayed(@NonNull Runnable r, long delayMillis) {
        //其实跟sendMessage方法差不不大,通过getPostMessage(r)把Runnable封装成一个message对象
        return sendMessageDelayed(getPostMessage(r), delayMillis);
    }

        private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();
        //这里就对message对象的callback属性进行了赋值,这样在dispatchMessage方法中
        //就不会走handleMessage方法了而是handleCallback(msg);方法
        m.callback = r;
        return m;
    }

handler.post方法和sendMessage方法基本一样,post更简单它时候只对一件事情处理的时候用,message有setData和what等方法适合对复杂的事情做处理。

最后至于handler到底是怎样实现线程切换的实际上只是因为线程间资源共享,我们的handler声明在主线程,对应的looper、messageQueue都在主线程,所以发出的消息最终就是在主线程中处理

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值