美文网首页Android高级进阶
Android进阶之基础知识:Handler,Android消息

Android进阶之基础知识:Handler,Android消息

作者: 小小小小怪兽_666 | 来源:发表于2020-11-10 20:35 被阅读0次

    原文:https://juejin.im/post/6850418119094042632

    Handler是消息机制的上层接口,开发中基本只用和Handler交互即可。Handler可以将一个任务切换到Handler指定的线程中执行。如在用Handler在子线程更新UI。

    Android消息机制主要就是Handler的运行机制。Handler的运行还依赖MessageQueue、Looper,及Looper内部使用到的ThreadLocal。 MessageQueue是消息队列,用于存放Handler发送的消息,实际是单链表的结构。 Looper会在消息队列中无限循环的查找消息,有消息就取出,没有就等待。 ThreadLocal本质作用是在每个线程中存储数据。在Looper中的作用就是给每个线程存Looper实例。因为我们知道,创建Handler时是需要线程的Looper实例的,而非UI线程默认是没有Looper的。

    一、Handler使用与概述

    1.1使用步骤

    1. 在任务执行的线程,使用Looper.prepare()来给线程创建Looper实例。

    2. 在任务执行的线程,创建Handler实例。

    3. 在任务执行的线程,使用Looper.loop()开启消息循环。

    4. 任务发出的线程,使用Handler实例发送消息。

    举个例子🌰 如下所示,点击按钮,在主线程发送消息,就会在子线程执行。 (这个例子为了完整展示使用步骤,所以在子线程创建了handler,在主线程发送和消息。通常实际我们使用是在主线程创建handler,在子线程发送消息然后再主线程执行UI的更新,而主线程默认是有Looper并开启的,所以一般不需要第一步和第三部。)

        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            testHandler();
        }
    
        private void testHandler() {
    
            new Thread(new Runnable() {
                @Override
                public void run() {
                    //1、准备looper,即threadLocal<Looper>.set(new Looper())
                    Looper.prepare();
    
                    //2、创建handler实例
                    // 这个重写了handleMessage,handler是属于Handler的子类的实例
                    mHandler = new Handler() {
                        @Override
                        public void handleMessage(Message msg) {
                            super.handleMessage(msg);
                            Log.i(TAG, "child thread, handleMessage: what="+msg.what);
                        }
                    };
    
                    //3、looper启动,sThreadLocal.get()拿到looper,拿到queue,开始queue.next
                    Looper.loop();
                }
            }).start();
        }
    
        public void onClick(){
            //4.2、handler.sendMessage发送消息,queue.enqueueMessage(msg),即消息入队列。
            Log.i(TAG, "main thread, sendMessage");
            Message message = Message.obtain();
            message.what = 100;
            mHandler.sendMessage(message);   
        }
    

    1.2Handler的使用背景

    Handler可以将子线程中更新UI的任务切换到主线程。为什么要切换呢?我们知道,UI的访问只能在主线程进行。子线程访问UI就会出现异常,因为在ViewRootImpl中对线程做了校验,只有创建了这个View树的线程,才能访问这个view。 一般情况创建View的线程就是主线程,即UI线程,所以子线程访问会异常。

        void checkThread() {
            if (mThread != Thread.currentThread()) {
                throw new CalledFromWrongThreadException(
                        "Only the original thread that created a view hierarchy can touch its views.");
            }
        }
    

    而且,UI线程一般不能做耗时操作,不然会发生ANR。所以当 在子线程做完耗时操作后 又需要更新UI,这时就需要用到Handler了。那为啥一定要用checkThread()保证不让子线程访问UI呢? 因为UI控件不是线程安全的。那为啥不加锁呢?一是加锁会让UI访问变得复杂;二是加锁会降低UI访问效率,会阻塞一些线程访问UI。所以干脆使用单线程模型处理UI操作,使用时用Handler切换即可。

    二、Android消息机制分析

    前面说了,Android消息机制包含几个概念:Handler、MessageQueue、Looper、Looper内部使用到的ThreadLocal。下面详细介绍下。

    2.1 ThreadLocal

    外界想要在不同thread中存值,就可以threadLocal = new ThreadLocal,然后在不同线程中threadLocal.set(value)就可以了,获取值用threadLocal.get() 。

    举个例子🌰,下面例子中 先只看booleanThreadLocal,在主线程设置true,a线程设置false,b线程设置null,然后每个线程都打印 booleanThreadLocal.get()的结果,发现每个线程get的值是不同的,是在每个线程中set的值。这就是神奇之处,同样的booleanThreadLocal.get(),所在线程不同,结果就不同。

            ThreadLocal<Boolean> booleanThreadLocal = new ThreadLocal<>();
            ThreadLocal<Integer> integerThreadLocal = new ThreadLocal<>();
    
            booleanThreadLocal.set(true);
            integerThreadLocal.set(0);
            Log.i(TAG, "testThreadLocal: main thread, boolean= "+booleanThreadLocal.get());
            Log.i(TAG, "testThreadLocal: main thread, int = "+integerThreadLocal.get());
    
            new Thread(new Runnable() {
                @Override
                public void run() {
                    booleanThreadLocal.set(false);
                    integerThreadLocal.set(1);
                    Log.i(TAG, "testThreadLocal: a thread, boolean="+booleanThreadLocal.get());
                    Log.i(TAG, "testThreadLocal: a thread, int = "+integerThreadLocal.get());
                }
            }).start();
    
            new Thread(new Runnable() {
                @Override
                public void run() {
                    booleanThreadLocal.set(null);
                    integerThreadLocal.set(2);
                    Log.i(TAG, "testThreadLocal: b thread, boolean="+booleanThreadLocal.get());
                    Log.i(TAG, "testThreadLocal: b thread, int = "+integerThreadLocal.get());
                }
            }).start();
    

    结果:

    2020-01-08 10:15:38.623 8976-8976/com.hfy.demo01 I/hfy: testThreadLocal: main thread, boolean= true
    2020-01-08 10:15:38.623 8976-8976/com.hfy.demo01 I/hfy: testThreadLocal: main thread, int = 0
    2020-01-08 10:15:38.624 8976-9226/com.hfy.demo01 I/hfy: testThreadLocal: a thread, boolean=false
    2020-01-08 10:15:38.624 8976-9226/com.hfy.demo01 I/hfy: testThreadLocal: a thread, int = 1
    2020-01-08 10:15:38.626 8976-9227/com.hfy.demo01 I/hfy: testThreadLocal: b thread, boolean=null
    2020-01-08 10:15:38.626 8976-9227/com.hfy.demo01 I/hfy: testThreadLocal: b thread, int = 2
    

    下面看下ThreadLocal的get()、set()方法。

        /**
         * Returns the value in the current thread's copy of this
         * thread-local variable.  If the variable has no value for the
         * current thread, it is first initialized to the value returned
         * by an invocation of the {@link #initialValue} method.
         *
         * @return the current thread's value of this thread-local
         */
        public T get() {
            Thread t = Thread.currentThread();
            ThreadLocalMap map = getMap(t);
            if (map != null) {
                ThreadLocalMap.Entry e = map.getEntry(this);
                if (e != null) {
                    @SuppressWarnings("unchecked")
                    T result = (T)e.value;
                    return result;
                }
            }
            return setInitialValue();
        }
    
        /**
         * Variant of set() to establish initialValue. Used instead
         * of set() in case user has overridden the set() method.
         *
         * @return the initial value
         */
        private T setInitialValue() {
            T value = initialValue();
            Thread t = Thread.currentThread();
            ThreadLocalMap map = getMap(t);
            if (map != null)
                map.set(this, value);
            else
                createMap(t, value);
            return value;
        }
    

    get():获取当前线程的ThreadLocalMap,这里可以先理解成普通 键值对的Map。然后传入threadLocal实例,获取键值对Entry,然后获取Entry的value。如果map为空或value为空则会初始化map、value。

        /**
         * Sets the current thread's copy of this thread-local variable
         * to the specified value.  Most subclasses will have no need to
         * override this method, relying solely on the {@link #initialValue}
         * method to set the values of thread-locals.
         *
         * @param value the value to be stored in the current thread's copy of
         *        this thread-local.
         */
        public void set(T value) {
            Thread t = Thread.currentThread();
            ThreadLocalMap map = getMap(t);
            if (map != null)
                map.set(this, value);
            else
                createMap(t, value);
        }
         /**
         * Create the map associated with a ThreadLocal. Overridden in
         * InheritableThreadLocal.
         *
         * @param t the current thread
         * @param firstValue value for the initial entry of the map
         */
        void createMap(Thread t, T firstValue) {
            t.threadLocals = new ThreadLocalMap(this, firstValue);
        }
    

    set()中也是获取当前线程的ThreadLocalMap,然后ThreadLocal实例作为key, 和value一起设置给map。没有map就去创建并把value初始化进去。

    我们再去看下Thread,有个默认为空的ThreadLocalMap实例threadLocals。

        /* ThreadLocal values pertaining to this thread. This map is maintained
         * by the ThreadLocal class. */
        ThreadLocal.ThreadLocalMap threadLocals = null;
    

    那ThreadLocalMap是啥呢?ThreadLocalMap是ThreadLocal的内部类,作用类似Map,内部有个Entry[]的属性table。所以上面看的get、set方法就是对ThreadLocalMap的Entry[]取和存 。下面详细看下。

    /**
             * Construct a new map initially containing (firstKey, firstValue).
             * ThreadLocalMaps are constructed lazily, so we only create
             * one when we have at least one entry to put in it.
             */
            ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
                table = new Entry[INITIAL_CAPACITY];
                int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
                table[i] = new Entry(firstKey, firstValue);
                size = 1;
                setThreshold(INITIAL_CAPACITY);
            }
    
            /**
             * Get the entry associated with key.  This method
             * itself handles only the fast path: a direct hit of existing
             * key. It otherwise relays to getEntryAfterMiss.  This is
             * designed to maximize performance for direct hits, in part
             * by making this method readily inlinable.
             *
             * @param  key the thread local object
             * @return the entry associated with key, or null if no such
             */
            private Entry getEntry(ThreadLocal<?> key) {
                int i = key.threadLocalHashCode & (table.length - 1);
                Entry e = table[i];
                if (e != null && e.get() == key)
                    return e;
                else
                    return getEntryAfterMiss(key, i, e);
            }
    
            /**
             * Set the value associated with key.
             *
             * @param key the thread local object
             * @param value the value to be set
             */
            private void set(ThreadLocal<?> key, Object value) {
    
                // We don't use a fast path as with get() because it is at
                // least as common to use set() to create new entries as
                // it is to replace existing ones, in which case, a fast
                // path would fail more often than not.
    
                Entry[] tab = table;
                int len = tab.length;
                int i = key.threadLocalHashCode & (len-1);
    
                for (Entry e = tab[i];
                     e != null;
                     e = tab[i = nextIndex(i, len)]) {
                    ThreadLocal<?> k = e.get();
    
                    if (k == key) {
                        e.value = value;
                        return;
                    }
    
                    if (k == null) {
                        replaceStaleEntry(key, value, i);
                        return;
                    }
                }
    
                tab[i] = new Entry(key, value);
                int sz = ++size;
                if (!cleanSomeSlots(i, sz) && sz >= threshold)
                    rehash();
            }
    

    使用Entry[] 存多个threadLocal-value键值对,数组下标index与是ThreadLocal 实例的hashCode相关。而ThreadLocalMap唯一实例是createMap(Thread t, T firstValue)赋给Thread的变量threadLocals。 例如 线程A threadLocalMap的table[] 可以存储 int、String、boolean类型的3个键值对threadLocal-int, threadLocal-String、threadLocal-Boolean。还是上面的例子。

    (常规的HashMap的键值得类型是固定的;threadLocalMap的key是ThreadLocal,value是T,即可以存多种类型的value)

            ThreadLocal<Boolean> booleanThreadLocal = new ThreadLocal<>();
            ThreadLocal<Integer> integerThreadLocal = new ThreadLocal<>();
    
            booleanThreadLocal.set(true);
            integerThreadLocal.set(0);
            Log.i(TAG, "testThreadLocal: main thread, boolean= "+booleanThreadLocal.get());
            Log.i(TAG, "testThreadLocal: main thread, int = "+integerThreadLocal.get());
    
            new Thread(new Runnable() {
                @Override
                public void run() {
                    booleanThreadLocal.set(false);
                    integerThreadLocal.set(1);
                    Log.i(TAG, "testThreadLocal: a thread, boolean="+booleanThreadLocal.get());
                    Log.i(TAG, "testThreadLocal: a thread, int = "+integerThreadLocal.get());
                }
            }).start();
    
            new Thread(new Runnable() {
                @Override
                public void run() {
                    booleanThreadLocal.set(null);
                    integerThreadLocal.set(2);
                    Log.i(TAG, "testThreadLocal: b thread, boolean="+booleanThreadLocal.get());
                    Log.i(TAG, "testThreadLocal: b thread, int = "+integerThreadLocal.get());
                }
            }).start();
    

    结果:

    2020-01-08 10:15:38.623 8976-8976/com.hfy.demo01 I/hfy: testThreadLocal: main thread, boolean= true
    2020-01-08 10:15:38.623 8976-8976/com.hfy.demo01 I/hfy: testThreadLocal: main thread, int = 0
    2020-01-08 10:15:38.624 8976-9226/com.hfy.demo01 I/hfy: testThreadLocal: a thread, boolean=false
    2020-01-08 10:15:38.624 8976-9226/com.hfy.demo01 I/hfy: testThreadLocal: a thread, int = 1
    2020-01-08 10:15:38.626 8976-9227/com.hfy.demo01 I/hfy: testThreadLocal: b thread, boolean=null
    2020-01-08 10:15:38.626 8976-9227/com.hfy.demo01 I/hfy: testThreadLocal: b thread, int = 2
    

    到目前为止我们知道,ThreadLocal的作用,就是操作线程内部的threadLocals,存和取value。value的实际类型就是 实例化ThreadLocal时定义的泛型T。

    2.2 messageQueue

    messageQueue,消息队列,实际是单向链表。看下存、取消息。

    enqueueMessage(),存消息,单链表的插入。

        boolean enqueueMessage(Message msg, long when) {
            if (msg.target == null) {
                throw new IllegalArgumentException("Message must have a target.");
            }
            if (msg.isInUse()) {
                throw new IllegalStateException(msg + " This message is already in use.");
            }
    
            synchronized (this) {
                if (mQuitting) {
                    IllegalStateException e = new IllegalStateException(
                            msg.target + " sending message to a Handler on a dead thread");
                    Log.w(TAG, e.getMessage(), e);
                    msg.recycle();
                    return false;
                }
    
                msg.markInUse();
                msg.when = when;
                Message p = mMessages;
                boolean needWake;
                if (p == null || when == 0 || when < p.when) {
                    // New head, wake up the event queue if blocked.
                    msg.next = p;
                    mMessages = msg;
                    needWake = mBlocked;
                } else {
                    // 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;
                }
    
                // We can assume mPtr != 0 because mQuitting is false.
                if (needWake) {
                    nativeWake(mPtr);
                }
            }
            return true;
        }
    

    next():取一条消息,没有消息就无限循环,会阻塞。

        Message next() {
            //...
            //有msg就return,没有消息就无限循环,会阻塞。如quit,return null。
            for (;;) {
                if (nextPollTimeoutMillis != 0) {
                    Binder.flushPendingCommands();
                }
    
                nativePollOnce(ptr, nextPollTimeoutMillis);
    
                synchronized (this) {
                    // Try to retrieve the next message.  Return if found.
                    final long now = SystemClock.uptimeMillis();
                    Message prevMsg = null;
                    Message msg = mMessages;
                    if (msg != null && msg.target == null) {
                        // Stalled by a barrier.  Find the next asynchronous message in the queue.
                        do {
                            prevMsg = msg;
                            msg = msg.next;
                        } while (msg != null && !msg.isAsynchronous());
                    }
                    if (msg != null) {
                        if (now < msg.when) {
                            // Next message is not ready.  Set a timeout to wake up when it is ready.
                            nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                        } else {
                            // Got a message.
                            mBlocked = false;
                            if (prevMsg != null) {
                                prevMsg.next = msg.next;
                            } else {
                                mMessages = msg.next;
                            }
                            msg.next = null;
                            if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                            msg.markInUse();
                            //有消息就return
                            return msg;
                        }
                    } else {
                        // No more messages.
                        nextPollTimeoutMillis = -1;
                    }
    
                    // Process the quit message now that all pending messages have been handled.
                    if (mQuitting) {
                        dispose();
                        //quit后返回null
                        return null;
                    }
    
                    // ...
        }
    

    2.3 Looper

    looper,消息循环器。

    先看静态方法prepare():

    
        // sThreadLocal.get() will return null unless you've called prepare().
        static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
    
        /** Initialize the current thread as a looper.
          * This gives you a chance to create handlers that then reference
          * this looper, before actually starting the loop. Be sure to call
          * {@link #loop()} after calling this method, and end it by calling
          * {@link #quit()}.
          */
        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));
        }
    
        /**
         * Initialize the current thread as a looper, marking it as an
         * application's main looper. The main looper for your application
         * is created by the Android environment, so you should never need
         * to call this function yourself.  See also: {@link #prepare()}
         */
        public static void prepareMainLooper() {
            prepare(false);
            synchronized (Looper.class) {
                if (sMainLooper != null) {
                    throw new IllegalStateException("The main Looper has already been prepared.");
                }
                sMainLooper = myLooper();
            }
        }
    
        private Looper(boolean quitAllowed) {
            mQueue = new MessageQueue(quitAllowed);
            mThread = Thread.currentThread();
        }
    

    可见sThreadLocal是个静态常量,value类型是Looper。 prepare()方法调sThreadLocal.set(new Looper),创建looper实例,设置给当前线程ThreadLocalMap属性中的table[i](i是threadLocal实例的hashCode相关)。

    且创建looper实例时默认创建了对应的消息队列mQueue实例。另外,prepareMainLooper()是主线程,是给主线程创建looper实例。

    再看下获取looper实例、queue实例的方法:

        /**
         * Returns the application's main looper, which lives in the main thread of the application.
         */
        public static Looper getMainLooper() {
            synchronized (Looper.class) {
                return sMainLooper;
            }
        }
    
        /**
         * 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();
        }
    
        /**
         * Return the {@link MessageQueue} object associated with the current
         * thread.  This must be called from a thread running a Looper, or a
         * NullPointerException will be thrown.
         */
        public static @NonNull MessageQueue myQueue() {
            return myLooper().mQueue;
        }
    

    myLooper() 方法,调用sThreadLocal.get()。就是上面讲解的ThreadLocal的使用方法。通过静态常量sThreadLocal获取对应每个线程的Looper实例。

    looper的quit,两种,立即退出,执行完消息再退出。

        /**
         * Quits the looper.
         * <p>
         * Causes the {@link #loop} method to terminate without processing any
         * more messages in the message queue.
         * </p><p>
         * Any attempt to post messages to the queue after the looper is asked to quit will fail.
         * For example, the {@link Handler#sendMessage(Message)} method will return false.
         * </p><p class="note">
         * Using this method may be unsafe because some messages may not be delivered
         * before the looper terminates.  Consider using {@link #quitSafely} instead to ensure
         * that all pending work is completed in an orderly manner.
         * </p>
         *
         * @see #quitSafely
         */
        public void quit() {
            mQueue.quit(false);
        }
    
        /**
         * Quits the looper safely.
         * <p>
         * Causes the {@link #loop} method to terminate as soon as all remaining messages
         * in the message queue that are already due to be delivered have been handled.
         * However pending delayed messages with due times in the future will not be
         * delivered before the loop terminates.
         * </p><p>
         * Any attempt to post messages to the queue after the looper is asked to quit will fail.
         * For example, the {@link Handler#sendMessage(Message)} method will return false.
         * </p>
         */
        public void quitSafely() {
            mQueue.quit(true);
        }
    

    静态方法loop():用threadLocal.get()获取当前线程的Looper,然后拿到queue,循环取消息,给到handler的dispatchMessage方法-handleMessage方法。唯一跳出循环是取到null,null是因为调用了quit或quitSafly。 ==因为静态方法loop()是在线程中调用的,所以不论handler从哪里发送msg都会在loop的线程中执行==。

        /**
         * Run the message queue in this thread. Be sure to call
         * {@link #quit()} to end the loop.
         */
        public static void loop() {
            final Looper me = myLooper();
            if (me == null) {
                throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
            }
            final MessageQueue queue = me.mQueue;
    
            // ...
    
            for (;;) {
                //没有msg ,queue.next()阻塞,loop() 也就阻塞了。next有msg就处理,无限循环。
                Message msg = queue.next(); // might block
                if (msg == null) {
                    //调用quit()时才会 跳出循环
                    // No message indicates that the message queue is quitting.
                    return;
                }
    
                // ...
                //用target(handler)处理消息,dispatchMessage执行在loop() 调用的地方,即looper所在线程。
                try {
                    msg.target.dispatchMessage(msg);
                    dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
                } finally {
                    if (traceTag != 0) {
                        Trace.traceEnd(traceTag);
                    }
                }
                ...
            }
        }
    

    流程 prepare()-new hanler()- loop() 连续的在同个线程调用。保证handleMessage执行在当前线程。即使handler.sengMessage()在其他线程调用。

    2.4 Handler

    发送,处理消息。 先看Handler构造方法,可见调用了Looper.myLooper(),就是获取当前线程的looper,没有就会抛出异常。

        /**
         * Default constructor associates this handler with the {@link Looper} for the
         * current thread.
         *
         * If this thread does not have a looper, this handler won't be able to receive messages
         * so an exception is thrown.
         */
        public Handler() {
            this(null, false);
        }
    
        public Handler(Callback callback) {
            this(callback, false);
        }
    
        public Handler(Callback callback, boolean async) {
            ...
            mLooper = Looper.myLooper();
            if (mLooper == null) {
                throw new RuntimeException(
                    "Can't create handler inside thread " + Thread.currentThread()
                            + " that has not called Looper.prepare()");
            }
            mQueue = mLooper.mQueue;
            mCallback = callback;
            mAsynchronous = async;
        }
    

    发送消息,就是把消息放入队列

        private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
            msg.target = this;
            if (mAsynchronous) {
                msg.setAsynchronous(true);
            }
            return queue.enqueueMessage(msg, uptimeMillis);
        }
    

    处理消息,根据Handler的创建形式和使用方法对应处理。

        /**
         * Handle system messages here.
         */
        public void dispatchMessage(Message msg) {
            if (msg.callback != null) {
                //msg.callback就是handler.post()发送的runable
                handleCallback(msg);
            } else {
                if (mCallback != null) {
                    //mCallback是创建Handler时传入CallBack的情况。
                    if (mCallback.handleMessage(msg)) {
                        return;
                    }
                }
                //覆写handleMessage()创建handler的情况
                handleMessage(msg);
            }
        }
    

    三、主线程的消息机制

    主线程的消息

    Looper中:

        /**
         * Initialize the current thread as a looper, marking it as an
         * application's main looper. The main looper for your application
         * is created by the Android environment, so you should never need
         * to call this function yourself.  See also: {@link #prepare()}
         */
        public static void prepareMainLooper() {
            prepare(false);
            synchronized (Looper.class) {
                if (sMainLooper != null) {
                    throw new IllegalStateException("The main Looper has already been prepared.");
                }
                sMainLooper = myLooper();
            }
        }
    

    ActivityThread的静态方法main:

        final H mH = new H();
    
        public static void main(String[] args) {
            ...
            //1、准备主线程的Looper
            Looper.prepareMainLooper();
    
            // Find the value for {@link #PROC_START_SEQ_IDENT} if provided on the command line.
            // It will be in the format "seq=114"
            long startSeq = 0;
            if (args != null) {
                for (int i = args.length - 1; i >= 0; --i) {
                    if (args[i] != null && args[i].startsWith(PROC_START_SEQ_IDENT)) {
                        startSeq = Long.parseLong(
                                args[i].substring(PROC_START_SEQ_IDENT.length()));
                    }
                }
            }
            //这里实例化ActivityThread,也就实例化了上面的mH,就是handler。
            ActivityThread thread = new ActivityThread();
            thread.attach(false, startSeq);
    
            //获取handler
            if (sMainThreadHandler == null) {
                sMainThreadHandler = thread.getHandler();
            }
    
            ...
            //主线程looper开启
            Looper.loop();
            //因为主线程的Looper是不能退出的,退出就无法接受事件了。一旦意外退出,会抛出异常
            throw new RuntimeException("Main thread loop unexpectedly exited");
        }
    

    H处理了四大组件的启动停止等。ActivityThread通过ApplicationThread和AMS进行进程间通信,AMS完成ActivityThread的请求后,回调到ApplicationThread中的binder方法,然后ApplicationThread使用H发送消息,然后就把此消息切换到ActivityThread中执行,即在主线程执行。这就是主线程的消息循环。

    那么该如何系统学习Android呢?

    这里今天给大家分享一份Android进阶学习资料,主要为安卓相关知识点及面试资料为主,在这个PDF中,通过详解各大互联网公司的 Android 常见面试题为主线,从面试的角度带你介绍必备知识点,以及该知识点在项目中的实际应用。

    帮你在现在的基础上,重新梳理和建立 Android 开发的知识体系。无论是你短期内想提升 Android 内功实力,突破自己工作中的能力瓶颈,还是准备参加 Android 面试,都会在这个PDF中有所收获。一些基础不好的,这里也有一份安卓基础资料包,帮助巩固基础。

    以下是这份PDF主要内容

    • Android 核心技术:介绍 Android 开发中常用的核心技术,比如自定义 View、Handler,以及一些开源框架的原理实现,来夯实你的底层能力。只有底层能力足够出色,之后的进阶之路才会更加轻松。
    • 常见问题剖析:介绍一些项目中常见的疑难问题,使你能够对现有项目做出合理的重构优化。

    1、确定好方向,梳理成长路线图

    不用多说,相信大家都有一个共识:无论什么行业,最牛逼的人肯定是站在金字塔端的人。所以,想做一个牛逼的程序员,那么就要让自己站的更高,成为技术大牛并不是一朝一夕的事情,需要时间的沉淀和技术的积累。

    关于这一点,在我当时确立好Android方向时,就已经开始梳理自己的成长路线了,包括技术要怎么系统地去学习,都列得非常详细。

    知识梳理完之后,就需要进行查漏补缺,所以针对这些知识点,我手头上也准备了不少的电子书和笔记,这些笔记将各个知识点进行了完美的总结:

    2、通过源码来系统性地学习

    只要是程序员,不管是Java还是Android,如果不去阅读源码,只看API文档,那就只是停留于皮毛,这对我们知识体系的建立和完备以及实战技术的提升都是不利的。

    真正最能锻炼能力的便是直接去阅读源码,不仅限于阅读各大系统源码,还包括各种优秀的开源库。

    《486页超全面Android开发相关源码精编解析》

    3、阅读前辈的一些技术笔记

    《960全网最全Android开发笔记》

    4、刷题备战,直通大厂

    历时半年,我们整理了这份市面上最全面的安卓面试题解析大全
    包含了腾讯、百度、小米、阿里、乐视、美团、58、猎豹、360、新浪、搜狐等一线互联网公司面试被问到的题目。熟悉本文中列出的知识点会大大增加通过前两轮技术面试的几率。

    如何使用它?

    1.可以通过目录索引直接翻看需要的知识点,查漏补缺。
    2.五角星数表示面试问到的频率,代表重要推荐指数

    《379页Android开发面试宝典》

    以上文章中的资料,均可以免费分享给大家来学习,无论你是零基础还是工作多年,现在开始就不会晚。

    以上内容均放在了开源项目:github 中已收录,大家可以自行获取(或者关注主页扫描加微信获取)。

    学习技术是一条慢长而艰苦的道路,不能靠一时激情,也不是熬几天几夜就能学好的,必须养成平时努力学习的习惯。所以:贵在坚持!

    相关文章

      网友评论

        本文标题:Android进阶之基础知识:Handler,Android消息

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