Android 经过Binder机制来处理进程间通讯问题,而经过Handler 音讯机制来处理线程之间通讯问题,或许用来切换线程。毕竟Android要求只能主线程更新界面,对于耗时操作和网络请求都需求在其他线程履行,就涉及到线程切换。

Android运用都是靠音讯来驱动作业的。大致原理便是:

  • 一个音讯行列,往音讯行列投递信息。
  • 一个音讯循环,不断从音讯行列取音讯,然后处理。

而这个作业原理就涉及到三个类:LooperMessageQueueHandler

Looper

Looper的创立需求经过Looper.prepare函数来创立。

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

这儿的sThreadLocal目标是ThreadLocal类型,用于存储线程私有全局变量,具体可看ThreadLocal解析。也便是在线程任何地方获取到的Looper目标都是同一个。经过判断句子来看,一个线程中只能调用一次Looper.prepare创立一次Looper目标。经过Looper的结构函数直接创立了Looper目标。

private Looper(boolean quitAllowed) {
    mQueue = new MessageQueue(quitAllowed);
    mThread = Thread.currentThread();
}

Looper的结构函数中创立了一个音讯行列MessageQueue的实例mQueue,并持有当前线程目标的引用mThread。也便是说prepare函数创立了Looper目标,且该目标持有一个音讯行列。并将Looper目标设置到线程ThreadLocal中,确保每条线程只有一个Looper目标。

有了音讯行列,就要让音讯行列跑起来,在调用了prepare函数,再调用Looper.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 (;;) {
        Message msg = queue.next(); // might block
        if (msg == null) {
            return;
        }
        ...
        msg.target.dispatchMessage(msg);
        ...
        msg.recycleUnchecked();
    }
}

myLooper函数经过ThreadLocal目标sThreadLocal获取Looper目标,所以prepare函数需求在loop函数之前调用,否则会抛出反常。

然后在for死循环中,不断调用音讯行列MessageQueue目标queuenext函数来获取下一个待处理的音讯Message目标message。这儿的音讯行列来自Looper目标,即在创立Looper目标创立的MessageQueue目标。

然后经过Message目标的target目标分发处理该音讯。终究对音讯进行收回处理。那这儿的target目标是谁?也便是Handler目标了。

Handler

Handler能够理解为音讯机制里的辅佐类,因为此刻音讯行列有了,音讯循环也有,咱们能够手动去行列取音讯和往队里方向,但这简单出错。所以经过Handler来投递信息和处理信息。

Handler一共7个结构函数,其中两个现已符号过期了。

//设置Looper
public Handler(@NonNull Looper looper) {
    this(looper, null, false);
}
//设置Looper和Callback
public Handler(@NonNull Looper looper, @Nullable Callback callback) {
    this(looper, callback, false);
}
//设置同步屏障
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public Handler(boolean async) {
    this(null, async);
}
//设置回调和同步
public Handler(@Nullable 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;
}
@UnsupportedAppUsage
public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) {
    mLooper = looper;
    mQueue = looper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}

总的看下来,结构函数首要设置三个参数Looper目标,Callback目标,布尔类型async。Callback目标首要涉及音讯的处理,后面说,而async表明是否设置同步屏障。经过结构函数了解到Handler的音讯行列指向了Looper目标的音讯行列目标。

那么怎么往音讯行列投递信息呢?

Handler投递信息有许多重载函数,但首要经过sendMessage函数投递一个Message目标和post函数投递一个Runable目标。

    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) {
        msg.target = this;
        msg.workSourceUid = ThreadLocalWorkSource.getUid();
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

可见对音讯Message目标设置延迟时刻和同步屏障,并将Handler目标自身设置给了Message目标的target变量,终究调用的音讯行列的enqueueMessage行列将音讯投递到音讯行列中。

public final boolean post(@NonNull Runnable r) {
   return  sendMessageDelayed(getPostMessage(r), 0);
}
private static Message getPostMessage(Runnable r) {
    Message m = Message.obtain();
    m.callback = r;
    return m;
}

post函数仅仅将Runable目标封装到Message目标中的callback函数。终究还是调用sendMessagedDelayed函数的历程去处理。

从这儿咱们定位到了在Looperloop函数中Message目标的targetHandler目标,看看dispatchMessage函数。

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

dispatchMessage函数能够看出对音讯处理的优先级。

  • 音讯Message目标msg自带的Runable目标callback。也便是咱们经过post函数投递的Runable目标会最先被处理。
  • 优先级排第二便是咱们在创立Handler目标时,设置的全局回调Callback目标mCallback
  • 优先级最低的,也是最常用的,重载handleMessage函数,该函数默许是空完成。

MessageQueue

Handler中调用了MessageQueue目标的enqueueMessage函数,将Message目标投递到行列中。

    boolean enqueueMessage(Message msg, long when) {
        if (msg.target == null) {
            throw new IllegalArgumentException("Message must have a target.");
        }
        synchronized (this) {
            if (msg.isInUse()) {
                throw new IllegalStateException(msg + " This message is already in use.");
            }
            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) {//行列头
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;
            } else {
                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;
    }

enqueueMessage函数首要依据音讯处理的时刻when,刺进到行列合适的方位。

再看一下next函数,怎么从音讯队里取音讯。

Message next() {
    //当运用重启该mPtr会被置null
    final long ptr = mPtr;
    if (ptr == 0) {
        return null;
    }
    int pendingIdleHandlerCount = -1; // -1 only during first iteration
    int nextPollTimeoutMillis = 0;
    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) {
                do {//出现同步屏障,优先处理异步信息
                    prevMsg = msg;
                    msg = msg.next;
                } while (msg != null && !msg.isAsynchronous());
            }
            if (msg != null) {
                if (now < msg.when) {//未到音讯的处理时刻,等候
                    nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                } else {//从行列中获取音讯
                    mBlocked = false;
                    if (prevMsg != null) {
                        prevMsg.next = msg.next;
                    } else {
                        mMessages = msg.next;
                    }
                    msg.next = null;
                    msg.markInUse();
                    return msg;
                }
            } else {
                nextPollTimeoutMillis = -1;
            }
            //当一切挂起音讯被处理了,则回来null
            if (mQuitting) {
                dispose();
                return null;
            }
            //首次运转或许没有音讯了 
          if (pendingIdleHandlerCount < 0
                    && (mMessages == null || now < mMessages.when)) {
                pendingIdleHandlerCount = mIdleHandlers.size();
            }
            if (pendingIdleHandlerCount <= 0) {
                //没有空闲的音讯
                mBlocked = true;
                continue;
            }
            if (mPendingIdleHandlers == null) {
                mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
            }
            mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
        }
        // Run the idle handlers.
        // We only ever reach this code block during the first iteration.
        for (int i = 0; i < pendingIdleHandlerCount; i++) {
            final IdleHandler idler = mPendingIdleHandlers[i];
            mPendingIdleHandlers[i] = null; // release the reference to the handler
            boolean keep = false;
            try {
                keep = idler.queueIdle();
            } catch (Throwable t) {
                Log.wtf(TAG, "IdleHandler threw exception", t);
            }
            if (!keep) {
                synchronized (this) {
                    mIdleHandlers.remove(idler);
                }
            }
        }
        //避免重复履行已处理的信息
        pendingIdleHandlerCount = 0;
        // 重置该时刻,这样能够当即处理下一等候的信息
        nextPollTimeoutMillis = 0;
    }
}

next函数中,假如有同步屏障,会优先回来异步信息Looper目标,然后进行处理。假如行列中没有音讯,则直接处理已被挂起的音讯。

异步音讯与同步音讯

默许情况下,Handler目标会将mAsynchronous设置为false,会把音讯Message目标的asynchronous设置为false,表明异步音讯。当Message目标的asynchronousture表明同步音讯。而当target==null,表明这是一个同步屏障。

这三者的效果便是为了处理一个优先级的问题,当同步屏障到来时msg.target==null,优先处理行列中的异步信息。

为什么说msg.target==null是一个同步屏障,经过Handler投递出来的信息msg.target都是不为null的。而发送一个同步屏障的代码。

public int postSyncBarrier() {
    return postSyncBarrier(SystemClock.uptimeMillis());
}
private int postSyncBarrier(long when) {
    // Enqueue a new sync barrier token.
    // We don't need to wake the queue because the purpose of a barrier is to stall it.
    synchronized (this) {
        final int token = mNextBarrierToken++;
        final Message msg = Message.obtain();
        msg.markInUse();
        msg.when = when;
        msg.arg1 = token;
        Message prev = null;
        Message p = mMessages;
        if (when != 0) {
            while (p != null && p.when <= when) {
                prev = p;
                p = p.next;
            }
        }
        if (prev != null) { // invariant: p == prev.next
            msg.next = p;
            prev.next = msg;
        } else {
            msg.next = p;
            mMessages = msg;
        }
        return token;
    }
}

可见,发送一个屏障,并没有设置Handler目标给target变量。而异步信息优先权大于同步信息从何体现?

MessageQueuenext函数中有这么段代码。

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

当发送一个同步屏障时,需求手动移除该屏障,否则会一向停留在异步音讯的处理中,因为MessageQueuenext函数没有移除同步屏障。同步屏障的一个运用例子就在ViewRootImplescheduleTraversals函数中。

线程切换

Handler的实践运用便是UI线程与其他线程之间的切换。默许情况下,主线程会创立Looper目标,不需求咱们手动创立。咱们在主线程创立Handler目标时没有传入Looper目标,那默许就运用了主线程的Looper目标。在其他线程投递信息终究也是在主线程处理,到达线程切换的原理。

RxJavaGlide中网络请求,耗时操作的原理也是运用Handler切回到主线程。

也有面试官会问,能否在子线程创立Handler,答案是能够,条件是要先创立Looper目标,并且敞开循环,即调用Looper.preare();Looper.loop()

HandlerThread

正常情况下,运用主线程的Looper.myLooper函数获取的Looper目标来创立的Handler一般不会有问题。

但假如在运用子线程的Looper目标呢。如下面代码。

public class LooperThread extends Thread {
    public Looper myLooper;
    @Override
    public void run() {
        Looper.prepare();
        myLooper=Looper.myLooper();
        Looper.loop();
    }
}
val looperThread = LooperThread()
looperThread.start()
Handler(looperThread.myLooper)
handler.sendEmptyMessage(1)

界说了一个具有Looper目标的线程,这样就能够运用LooperThread目标的Looper目标。但上面代码Handler(looperThread.myLooper)有问题,或许此刻Looper目标还没初始化。因而需求采用同步机制来处理该问题,不过Android现已帮咱们界说了好了HandlerThread来出来该问题了。

run函数的完成

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

getLooper函数的完成

public Looper getLooper() {
    synchronized (this) {
        while (isAlive() && mLooper == null) {
            try {
                wait();
            } catch (InterruptedException e) {
                wasInterrupted = true;
            }
        }
    }
    return mLooper;
}

【参阅链接】

Handler的学习总结