Handler机制

Handler机制

线程间通信

Handler在android中应用非常普遍,主要用于线程间通信,比如在子线程中做耗时任务,然后通过handler机制在主线程刷新UI。

不同线程中Handler的使用

1.主线程中使用Handler
public class TestActivity extends AppCompatActivity {
    private TextView text;

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

        text = findViewById(R.id.text);
        Handler handler1 = new Handler(){
            @Override
            public void handleMessage(@NonNull Message msg) {
                text.setText("TextView");
            }
        };
        Message msg = Message.obtain();
        handler1.sendMessage(msg);
    }
}

可以看到,主线程中只需要实例化Handler对象,然后通过sendMessage()方法将message发送出去,就可以在handleMessage()方法中接受到消息并进行UI操作

2.子线程中使用Handler
public class TestActivity extends AppCompatActivity {
    private TextView text;

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

        new Thread(){
            Handler handler2;
            @Override
            public void run() {
                handler2 = new Handler();
                Message msg = Message.obtain();
                handler2.sendMessage(msg);
            }
        }.start();
    }
}

如上,我们在子线程中也实例化一个Handler对象,然后直接通过sendMessage()发送消息,看会有什么结果
在这里插入图片描述

可以看到,在子线程中不能直接使用Handler,需要先调用Looper.prepare(),修改一下看看

public class TestActivity extends AppCompatActivity {
    private TextView text;

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

        new Thread(){
            Handler handler2;
            @Override
            public void run() {
            	Looper.prepare();
                handler2 = new Handler();
                Message msg = Message.obtain();
                handler2.sendMessage(msg);
            }
        }.start();
    }
}

添加*Looper.prepare()*之后,运行没问题。为什么之前在子线程中使用Handler需要调用Looper.prepare()呢?

要搞清楚这个问题,我们可以从问题点切入,首先找到相关报错信息的根源,因为在子线程中,我们只是实例化了一个Handler对象,所以我们取Handler源码里面去搜寻相关报错信息,通过搜索,我们可以找到报错信息的定位
在这里插入图片描述

可以看到,这个异常信息是在Handler的构造函数中抛出的,如果当前Handler中的looper为null,则抛出异常。而looper又是通过Looper.myLooper()获取的,因此,我们需要看下Looper里面的具体实现。

我们首先来看Looper.myLooper()源码

 /**
     * Return the Looper object associated with the current thread.  Returns
     * null if the calling thread is not associated with a Looper.
     */
    public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }

从ThreadLocal中获取Looper然后直接返回,而Looper实例又是通过Looper.prepare()存入ThreadLocal中的

 public static void prepare() {
        prepare(true);
    }

    private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed));
    }

所以这里就解释了为何在子线程中使用Handler需要先调用Looper.prepare()。

那为何在主线程中使用Handler我们又没有先调用Looper.prepare()呢?我们知道,要是用Handler,必须先通过Looper.prepare()创建一个Looper对象并将其存入ThreadLocal中,因此主线程中必定在某个地方事先创建过Looper对象。提到主线程,我们立马就会想到ActivityThread.main(),我们打开其源码看看

public static void main(String[] args) {
        // ...
        Looper.prepareMainLooper();

        // ...
        ActivityThread thread = new ActivityThread();
        thread.attach(false, startSeq);

        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }

        if (false) {
            Looper.myLooper().setMessageLogging(new
                    LogPrinter(Log.DEBUG, "ActivityThread"));
        }

        // End of event ActivityThreadMain.
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        Looper.loop();

        throw new RuntimeException("Main thread loop unexpectedly exited");
    }

这里我们省略了无关代码,可以看到,在main()中,事先调用了Looper.prepareMainLooper()

public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }

prepareMainLooper()又调用了prepare()创建了Looper对象。这就解释了为何在主线程中我们不必手动调用Looper.prepare()来创建Looper了。

Handler是如何实现线程间通信的

我们平常使用Handler都是在主线程中创建Handler对象,然后在子线程中做耗时任务,得到结果后通过Handler将消息发送到主线程做UI操作,如

public class TestActivity extends AppCompatActivity {
    private TextView text;

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

        text = findViewById(R.id.text);
        Handler handler1 = new Handler(){
            @Override
            public void handleMessage(@NonNull Message msg) {
                text.setText("TextView");
            }
        };
        MyThread thread = new MyThread(handler1);
        new Thread(thread).start();
    }

    static class MyThread implements Runnable{
        Handler myHandler;
        MyThread(Handler handler) {
            myHandler = handler;
        }
        @Override
        public void run() {
            // 1.做耗时任务
            // 2.将耗时任务得到的记过通过handler发送给主线程进行UI操作
            Message msg = Message.obtain();
            myHandler.sendMessage(msg);
        }
    }
}

我们在线程MyThread中做耗时任务,然后通过主线程中创建的Handler将消息发送出去,最后在Handler.handleMessage()中收到消息做UI操作。这里就涉及到子线程与主线程的通信。

发送消息时,我们通常是使用Handler.sendMessage()方法,那我们从这个方法入手,来看看Handler发送消息到接受消息的整个过程是怎样实现的。

 public final boolean sendMessage(@NonNull Message msg) {
        return sendMessageDelayed(msg, 0);
 }

 public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
 }

 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) {
        // 将Handler自身引用赋值给Message的target
        msg.target = this;
        msg.workSourceUid = ThreadLocalWorkSource.getUid();

        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
 }

可以看到,sendMessage()最后会调用其enqueMessage()方法,这里我们要注意一点:Handler将自身引用赋值给了Message的target。 而最终会调用MessageQueue.enqueueMessage()

boolean enqueueMessage(Message msg, long when) {
        if (msg.target == null) {
            throw new IllegalArgumentException("Message must have a target.");
        }

        synchronized (this) {
                // ... 省略无关代码

                Message p = mMessages;
                // Inserted within the middle of the queue.  Usually we don't have to wake
                // up the event queue unless there is a barrier at the head of the queue
                // and the message is the earliest asynchronous message in the queue.
                needWake = mBlocked && p.target == null && msg.isAsynchronous();
                Message prev;
                for (;;) {
                    prev = p;
                    p = p.next;
                    if (p == null || when < p.when) {
                        break;
                    }
                    if (needWake && p.isAsynchronous()) {
                        needWake = false;
                    }
                }
                msg.next = p; // invariant: p == prev.next
                prev.next = msg;
        }
        return true;
    }

在enqueueMessage()里,通过死循环不断地遍历Message的下一个节点,直到为null跳出循环,然后将Message.next赋值为最后的一个Message,到这里,Handler将消息加入到消息队列的过程就结束了。

既然Handler负责将Message发送到了MessageQueue中,那势必会有一个专门负责将消息从消息队列中取出的角色,这时就轮到Looper出场了。

Looper

Looper通过loop()方法将消息从MessageQueue中不断地取出消息

 public static void loop() {
        final Looper me = myLooper();
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        if (me.mInLoop) {
            Slog.w(TAG, "Loop again would have the queued messages be executed"
                    + " before this one completed.");
        }

        me.mInLoop = true;

        // ...省略无关代码
        for (;;) {
            if (!loopOnce(me, ident, thresholdOverride)) {
                return;
            }
        }
    }

private static boolean loopOnce(final Looper me,
            final long ident, final int thresholdOverride) {
        Message msg = me.mQueue.next(); // might block
        if (msg == null) {
            // No message indicates that the message queue is quitting.
            return false;
        }

        // ...省略无关代码

        try {
            msg.target.dispatchMessage(msg);
            if (observer != null) {
                observer.messageDispatched(token, msg);
            }
            dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
        }
        return true;
    }

这个方法通过在一个死循环中遍历MessageQueue中的每一个Message(Message msg = me.mQueue.next()),得到的msg有调用了msg.target.dispatchMessage(msg),这个msg.target是什么呢? 前面我们其实已经说了Handler将自身引用赋值给了Message的target,因此这里的msg.target其实就是Handler,紧接着我们来看看Handler.dispatchMessage()方法

public void dispatchMessage(@NonNull Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

看到这里,原来最终是调用的其自身的handleMessage()方法,而子线程中创建的Message最终也发送到了Handler所在的主线程中,实现了跨线程通信

Looper.loop()死循环为什么不会阻塞主线程,造成UI卡顿呢?

Looper.loop()通过死循环的方式不断地通过MessageQueue.next()方法来取出消息队列中的消息,next()方法调用了native方法nativePollOnce(),其底层实现是借助了Linux的epoll机制,简单来说就是当消息队列中没有消息时会进入阻塞状态,此时主线程会进入休眠状态并释放CPU资源,直到有新的消息进入消息队列后重新唤起主线程。当我们的应用在运行时,我们的主线程是一直存在的,因为Looper.loop()是一个死循环。主线程需要接收各种系统或用户户消息进而做出相应的处理。比如Activity的生命周期onCreate()之所以可以调用到,就是因为主线程中的Handler(ActivityThread中有一个Handler的引用H)接收到了系统发送过来的onCreate()事件进而调用了Activity的onCreate()方法。因此我们应用的所有事件都会通过Handler被动地触发,只要这些事件中不会出现太耗时的操作就不会出现卡顿现象。

总结

  1. Handler负责将消息Message发送到消息队列MessageQueue中
  2. MessageQueue通过enqueueMessage()方法将消息Message加入消息队列中
  3. Looper负责将消息Message从消息队列MessageQueue中取出
  4. Looper通过loop()方法不断地遍历MessageQueue的下一个节点消息,得到消息Message后通过持有的Handler引用Message.tarter调用dispatchMessage()方法将消息发送出去,最后在Handler.handleMessage()方法中接收消息
  5. Handler主要用于线程间通信:子线程中通过Handler发送消息,主线程中通过Handler.handleMessage()方法接收消息
  6. 主线程中Looper.loop()不会造成应用卡顿,因为所有的事件都会被动地调用到。

参考链接

Android中为什么主线程不会因为Looper.loop()里的死循环卡死?

探索 Android 大杀器—— Handler

Android异步消息处理机制完全解析,带你从源码的角度彻底理解

详解 Android 中的 HandlerThread

Android应用程序消息处理机制(Looper、Handler)分析

Handler异步消息与同步屏障(SyncBarrier)

ThreadLocal

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

fastsy

打赏一份隆江猪脚饭吧

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值