Handler机制(一)——Handler运行流程分析

1 概述

Handler机制是Android的异步消息处理机制,用于在线程间传递消息,主要涉及到四部分:Handler、Looper、Message和MessageQueue。其中Handler是消息的发送者和处理者;Message是消息主体;MessageQueue是消息队列,Handler发送的消息都会放入MessageQueue中等待处理;Looper是MessageQueue的管理者,里面有一个loop方法,无限循环的从MessageQueue中取出需要处理的消息(如果有),并交个Handler处理。
这里有几个概念,一个线程只能有一个Looper对象和MessageQueue对象,而一个线程可以创建多个Handler对象,每一个Handler发送的消息只能由自己进行处理。通常的使用时主线程创建Handler对象,子线程使用这个Handler对象发送消息,由于这个Handler绑定的是主线程的Looper,所以会进入主线程的MessageQueue,也会由主线程的Looper分发给这个Handler回调他的handleMessage方法对消息进行处理。其中的原理看完下面的源码分析就可以理解了。

2 Handler对象创建问题

Android的UI线程是应用的主线程,是非线程安全的。Android规定,所有关于UI的更新都必须在主线程中执行,如果尝试在子线程中执行UI的更新操作会导致程序崩溃。

public class MainActivity extends AppCompatActivity {

    private Handler handler1;
    private Handler handler2;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        handler1 = new Handler();
        new Thread(new Runnable() {
            @Override
            public void run() {
                handler2 = new Handler();
            }
        }).start();
    }
}

上面尝试在主线程和子线程中都创建一个Handler的实例,运行程序后会发现程序崩溃,日志打印如下:

E/AndroidRuntime: FATAL EXCEPTION: Thread-2
    Process: com.xy.handlertest, PID: 3524
    java.lang.RuntimeException: Can't create handler inside thread Thread[Thread-2,5,main] that has not called Looper.prepare()
        at android.os.Handler.<init>(Handler.java:227)
        at android.os.Handler.<init>(Handler.java:129)
        at com.xy.handlertest.MainActivity$1.run(MainActivity.java:21)
        at java.lang.Thread.run(Thread.java:923)

报错显示无法在子线程中创建handler,原因是因为该线程中没有调用Looper.prepare()方法。
接下来可以进行改造,在子线程中调用该方法。

public class MainActivity extends AppCompatActivity {

    private Handler handler1;
    private Handler handler2;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        handler1 = new Handler();
        new Thread(new Runnable() {
            @Override
            public void run() {
                Looper.prepare();
                handler2 = new Handler();
            }
        }).start();
    }
}

发现程序正常运行。接下来探究一下为什么需要调用Looper.prepare()方法后,才能够创建Handler的实例。可以查看Handler类的无参构造方法:

@Deprecated
public Handler() {
    this(null, false);
}

可以看到,这里调用的是有两个参数重载的构造函数。同时,这里标志了废除的注解,说明这个方法是不建议使用的,原因就像是上面的例子一样,如果该线程没有调用过Looper的prepare()方法,就会出现程序崩溃,所以这里不建议使用无参构造方法。至于Looper的prepare()方法中做了什么,接着往下看。

public Handler(@Nullable 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());
        }
    }

    mLooper = Looper.myLooper(); //1
    if (mLooper == null) {
        throw new RuntimeException(
            "Can't create handler inside thread " + Thread.currentThread()
                    + " that has not called Looper.prepare()"); //2
    }
    mQueue = mLooper.mQueue; //3
    mCallback = callback; //4
    mAsynchronous = async;
}

这个构造方法就是无参构造方法调用的,无参构造传入的参数是null和false。这个方法的注释1处,调用了Looper.myLoope()方法,并返回一个Looper对象。

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

myLooper返回的是sThreadLocal.get(),而set在prepare()方法中。在注释1处,判断Looper的实例是否已经创建了,如果已经存在了就会抛异常,所以对于一个线程来说,只能调用一次prepare方法,也只存在一个Looper。在这个方法中会创建一个Looper的实例,并将其设置到sThreadLocal中,get的时候也是获取的这个Looper的实例。到这里就解释了最开始的实验中为什么调用了Looper.prepare()方法后,就可以在子线程中创建Handler的实例,而不调用就不能创建的原因了。
Handler对象的创建需要获取Looper的实例,而Looper的实例只有调用了Looper.prepare()方法才会创建。异常信息的打印就是在Handler的构造函数中的注释2处,由于没有获取到Looper对象而抛出的异常。
接着看回Handler的构造方法,注释3处获取Looper的MessageQueue,用于存储Handler发送的消息,这个后面分析。注释4处传入的是Handler执行的回调,这个后面也会有讲到。

现在的疑惑是为什么可以在主线程中直接创建Handler对象呢,这是因为应用启动的时候,主线程中会自动地调用Looper.prepare()方法。

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

在ActivityThread的main方法中,会调用Looper.prepareMainLooper()方法, 在这个方法中就会调用prepare()方法。

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

这个方法的最开始处就会调用prepare()方法,可以看到这个方法有一个废弃的注解,但是并不是意味着这个方法已经废弃了,而是不建议开发者自己去调用这个方法,给的解释是因为android环境自己会调用这个方法,不需要开发者去调用。所以主线程中可以创建Handler的实例,因为主线程初始化的时候已经为我们创建了Looper对象了。

3 Message构建、发送、入MessageQueue

在使用时,我们完整的消息构建和处理大致如下

public class MainActivity extends AppCompatActivity {

    private Handler handler = new Handler(Looper.getMainLooper()) { //1
        @Override
        public void handleMessage(@NonNull Message msg) {
            Log.d("MainActivity", "handleMessage: " + msg.getData().getString("data"));
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        new Thread(() -> { //2
            Message msg = new Message(); //3
            msg.what = 1;
            Bundle bundle = new Bundle();
            bundle.putString("data", "hello");
            msg.setData(bundle);
            handler.sendMessage(msg); //4
        }).start();
    }
}

注释1处创建一个匿名内部类,并重写了Handler这个父类的handleMessage方法,这个方法在Handler中是一个空实现,就是给其子类重写并书写消息处理逻辑的。注释2处创建子线程,并在注释3处创建Message对象,注释4处通过handler对象发送到主线程进行消息处理。
接下来看一下Message类的属性
Handler机制(一)——Handler运行流程分析_第1张图片
Message可以携带多种消息,其中what是用户可以对此次Message定义的一个标签,用于标志特定的Message,并根据这个标签来处理Message。arg1和arg2是用于传输简单的消息,是setData的轻量级方案。obj可以存储对象信息。data就是通过setData()方法设置的Bundle类型的数据。target标志着这个消息是哪个Handler发送的。
Handler机制(一)——Handler运行流程分析_第2张图片
Handler中有多个sendMessage相关的方法,除了标红的以外,其他的都会调用到sendMessageAtTime()方法。调用到sendMessageAtTime()是按照时间顺序存放到MessageQueue中,而sendMessageAtFrontOfQueue()方法这是将Message插入到MessageQueue队列的头部优先处理。

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

mQueue这个MessageQueue的对象是在Handler的构造函数中从Looper中获取的,与Looper相关联。这里调用enqueueMessage()方法将Message放入到MessageQueue中。

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

这里设置了target为当前的Handler对象,这是一个标志,标志着这个消息属于当前的Handler,后面Looper从MessageQueue中取出Message的时候就是根据这个target来分发给对应的Handler进行处理。最后调用MessageQueue的enqueueMessage将消息入消息队列。

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

    synchronized (this) {
        if (msg.isInUse()) {  //1
            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(); //2
        msg.when = when; //3
        Message p = mMessages; //4
        boolean needWake;
        if (p == null || when == 0 || when < p.when) { //5
            // New head, wake up the event queue if blocked.
            msg.next = p;
            mMessages = msg;
            needWake = mBlocked;
        } else { //6
            // 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;
}

注释1处判断当前消息是否在使用中,如果在使用中则抛异常。注释2处设置当前消息为使用中。消息在消息队列中通常是按时间顺序来排列的,注释3这里就是将时间信息存储到Message的when中。注释4处获取消息队列的队列头部。注释5处判断,如果队列中没有消息或者当前消息没有延时(即when=0)或者当前的消息处理时间在表头消息的处理时间之前,则将当前这条消息插入到消息队列的头部。否则就在注释6处遍历消息队列,根据消息的处理时间when来将消息插入到队列的合适位置。
when这个参数的设置在Handler中

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

这里获取的是开机为止的时间的毫秒值。
至此就将Message消息插入到了MessageQueue的队列中了。实际上,MessageQueue中消息队列存储Message并不是使用的容器,而是只是保存有队列头的Message的对象,并且Message中有一个next属性指向了下一个的Message。就是依靠Message中的next属性来关联整个的消息队列。
至此就分析清楚了消息构建发送到消息入消息队列的整个流程。

4 Looper处理分发

Message分发到对应的Handler处理这个工作是由Looper来完成的。
在第二节中讲到的ActivityThread的main方法中不止调用了Looper.prepareMainLooper()这个方法,在最后还调用了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; //1

    // Make sure the identity of this thread is that of the local process,
    // and keep track of what that identity token actually is.
    Binder.clearCallingIdentity();
    final long ident = Binder.clearCallingIdentity();

    // Allow overriding a threshold with a system prop. e.g.
    // adb shell 'setprop log.looper.1000.main.slow 1 && stop && start'
    final int thresholdOverride =
            SystemProperties.getInt("log.looper."
                    + Process.myUid() + "."
                    + Thread.currentThread().getName()
                    + ".slow", 0);

    me.mSlowDeliveryDetected = false;

    for (;;) { //2
        if (!loopOnce(me, ident, thresholdOverride)) {
            return;
        }
    }
}

注释1处设置当前处于loop循环中,在上面会判断这个标志,如果多次调用loop()方法会打印警告信息。注释2处死循环执行loopOnce方法,只有这个方法返回false时才会退出。

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); //1
        if (observer != null) {
            observer.messageDispatched(token, msg);
        }
        dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
    }
    ...

loopOnce是执行一次取Message的操作,首先第一行获取Message,先是获取到MessageQueue对象再调用next()方法获取到一个Message对象。获取Message后在注释1处分发消息,可以看到注释1处是获取到了Message对象中的target之后调用target对象的dispatchMessage方法,那么这个target是什么呢?在上节中我们讲了,target存储的就是发送Message的Handler对象。
首先看一下MessageQueue的next()方法,看一下是怎么获取到Message的

Message next() {
	...
    for (;;) {
        if (nextPollTimeoutMillis != 0) {
            Binder.flushPendingCommands();
        }

        nativePollOnce(ptr, nextPollTimeoutMillis); //1

        synchronized (this) {
            // Try to retrieve the next message.  Return if found.
            final long now = SystemClock.uptimeMillis();
            Message prevMsg = null;
            Message msg = mMessages; //2
            if (msg != null && msg.target == null) { //3
                // 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) { //4
                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 msg;
                }
            } else {
                // No more messages.
                nextPollTimeoutMillis = -1;
            }
			...
    }
}

注释1处是使Looper阻塞的地方,是一个native方法。当没有消息处理的时候,就会在这里阻塞,释放cpu资源。注释2处获取消息队列的头节点,注释3处判断如果消息中的target不存在则找下一个消息,直到获取到Message后退出,target等于null相当于没有Handler,则不知道讲消息交给谁处理。注释4处如果消息不为空,则在这个里面执行消息的取出和返回操作。这里首先判断消息是否到期需要处理了,如果没有到期则不做处理,执行下一次loop循环,如果到期了则从队列中移除这个Message并返回。

(待补充:这里对于阻塞和唤醒没有分析,后面如果懂了这一块再来补充,猜测应该是nativePollOnce这个方法中进行判断的,在分发操作时,首先判断第一个Message的when是多久,分两种情况,一种是第一个都没有到期的,由于我们知道消息在队列中是按照到期时间来排列的,第一个都没有到期,后面的也没有,所以对于这种情况,算出到期时间,然后下一次loopOnce的时候会阻塞在nativePollOnce这个方法中,释放cpu资源。第二种情况就是到期了,则直接将有效的Message进行返回并移除出队列。next方法中后面还有nextPollTimeoutMillis = -1这一行,应该是标志没有消息,等待有消息才会重新唤醒Looper)。
然后看一下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);
    }
}

这里判断Message的callback属性有没有值,如果有则调用handleCallback方法,如果没有则判断Handler的mCallback属性有没有值,有则调用这个里面的handleMessage方法,没有则调用Handler的handleMessage方法。这里其实涉及到了其他几种使用Handler的方式。请参考(待补充)。这里前面我们使用的方式是创建匿名内部类,重写Handler的handleMessage方法来创建的Handler,所以这里会调用最后面的Handler中的handleMessage方法。

5 总结

  • Handler功能是发送和处理消息,一个Handler发送的消息只能是该Handler自己处理。这是在发送消息时就决定了的,具体表现在发送Message时,Message会保存Handler对象到target属性中,Looper进行分发给Handler处理的时候,调用的是target的handleMessage方法。
  • 一个线程只能有一个Looper和一个MessageQueue。
  • 一个线程可以有多个Handler,多个Handler发送的消息会由Looper分发给对应的Handler进行处理。

6 遗留问题

  • Looper是死循环,且在主线程中运行,为什么不会影响其他的执行
  • Looper的阻塞原理
  • 其他更新UI方式的分析
  • Message池的分析

你可能感兴趣的:(#,Android基础知识,ui,android,java)