Handler简单回顾

作者: _deadline | 来源:发表于2018-03-26 20:00 被阅读95次

我们都知道ActivityThread是程序的入口,以下是App入口处的部分代码

        // 创建主线程Looper对象
        Looper.prepareMainLooper();

        ActivityThread thread = new ActivityThread();
        thread.attach(false);

        // 创建主线程Handler        
        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();

我们先来看主线程Looper消息循环的创建过程

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

Looper对象真正创建的地方,可以看到传递了一个quitAllowed值,重字面意思上来看是否允许退出,而prepareMainLooper方法里传递的是false, 因此主线程的Looper是不允许退出的,然后同时把创建的Looper对象跟线程的sThreadLocal绑定,同时可以看到判空处理每个线程只能持有一个Looper对象,并且每个线程之前的Looper对象是互相隔离的。

    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));
    }

上面prepareMainLooper方法还调用了myLooper方法来给Looper赋值


   /**
     * 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();
    }

  
    public static @NonNull MessageQueue myQueue() {
        return myLooper().mQueue;
    }

    // Looper 对象持有了对应线程的引用,同时内部维护了一个单链表的消息队列
    private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }

写到这,我们简单总结下结论,一个线程只能有一个Looper对象,每个Looper对象内部只有一个单链表的消息队列(MessageQueue).

我们再来看下Handler类

 // handler持有了所在Looper的引用   
 public Handler(Looper looper, Callback callback, boolean async) {
        mLooper = looper;
        mQueue = looper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

    // 快捷方式获取一个跟主线程关联的Handler
    /** @hide */
    @NonNull
    public static Handler getMain() {
        if (MAIN_THREAD_HANDLER == null) {
            MAIN_THREAD_HANDLER = new Handler(Looper.getMainLooper());
        }
        return MAIN_THREAD_HANDLER;
    }

实际开发中的使用场景

1、在其他线程想操作UI

    new Thread(new Runnable() {
            @Override
            public void run() {
                Looper.prepare();
                
                // 把消息发送到主线程的Looper中,因此 handlerMessage()方法也会在主线程中执行
                Handler handler = new Handler(Loop.getMainLooper()) {
                    @Override
                    public void handleMessage(Message msg) {
                        super.handleMessage(msg);
                    }
                };


                Looper.loop();
            }
        }).start();
  1. 消息发送到当前线程的Looper中处理,因此为了方便使用,系统提供了HandlerThread类简化代码,方便开发者。
    new Thread(new Runnable() {
            @Override
            public void run() {
                Looper.prepare();
                Handler handler = new Handler() {
                    @Override
                    public void handleMessage(Message msg) {
                        super.handleMessage(msg);
                    }
                };


                Looper.loop();
            }
        }).start();
  1. UI线程延时更新等

    如果在主线程中通过new Handler()方式创建Handler的对象handler,那么handler对象中持有的Looper是主线程的Looper因此handler对象中持有的MessageQueue队列也是主线程的消息队列,因此handler对象发送的消息也都是发送到主线程的消息队列中了。

Q: 如何保证谁(handler)发送的消息谁来处理?

A:Handler发送消息到MessageQueue之后会把Message的target属性设置为Handler自身,正如Looper取出消息处理消息的处理一致

  final long start = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
            final long end;
            try {
                msg.target.dispatchMessage(msg);
                end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
           

Q: 为什么在非UI线程中直接创建Handler对象会崩溃?

public Handler(Callback callback, boolean async) {
        if (FIND_POTENTIAL_LEAKS) {
            final Class<? extends Handler> klass = getClass();
            if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
                    (klass.getModifiers() & Modifier.STATIC) == 0) {
                Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
                    klass.getCanonicalName());
            }
        }

        // 获取当前线程的Looper, 如果是非UI线程默认是没有Looper对象的因此, 直接创建Handler必然会崩溃, 所以需要主动调用Looper.prepare(), Looper.loop();
        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

相关文章

网友评论

    本文标题:Handler简单回顾

    本文链接:https://www.haomeiwen.com/subject/kfqdcftx.html