美文网首页
认识Handler消息机制

认识Handler消息机制

作者: 编程的猫 | 来源:发表于2020-03-10 14:37 被阅读0次
什么是Handler消息机制?

Handler是android提供用于更新UI的一套机制,也是消息处理机制。用于UI线程与子线程间消息的收发
本文将围绕handler消息处理的主要流程,跟随源码进行分析:


message的处理流程
handler中不得不讲得四个对象:
  • Message

Message是在线程之间传递的消息,它可以在内部携带少量的信息,用于在不同线程之间交换数据。使用Message的arg1和arg2便可携带int数据,使用obj便可携带Object类型数据。(可以简单理解为信息数据的载体)

  • Handler

Handler顾名思义就是处理者的意思,它只要用于在子线程发送消息对象Message,在UI线程处理消息对象Message,在子线程调用sendMessage方法发送消息对象Message,而发送的消息经过一系列地辗转之后最终会被传递到Handler的handleMessage方法中,最终在handleMessage方法中消息对象Message被处理(可以简单理解为Handler是Message对象的发送和接收者)

  • MessageQueue

MessageQueue就是消息队列的意思,它只要用于存放所有通过Handler发送过来的消息。这部分消息会一直存放于消息队列当中,等待被处理。每个线程中只会有一个MessageQueue对象,请牢记这句话。其实从字面上就可以看出,MessageQueue底层数据结构是队列,而且这个队列只存放Message对象。(可以简单理解为Message对象的存储队列)

  • Looper

Looper是每个线程中的MessageQueue的管家,调用Looper的loop()方法后,就会进入到一个无限循环当中,然后每当MesssageQueue中存在一条消息,Looper就会将这条消息取出,并将它传递到Handler的handleMessage()方法中。每个线程只有一个Looper对象。(可以简单理解为Looper是消息队列MessageQueue的维护者)

通常我们使用Handler在主线程和子线程间处理消息时,都是直接在activity中创建handler的对象并同时复写handlerMessage方法,在子线程中发送消息即可。那为什么我们能够直接就使用Handler的处理,肯定是下层代码在应用启动的时候就帮我们做好了,接下来我们一起看看:

首先在ActivityThread类有我们熟悉的main的函数,App启动的代码的入口就在这里:
    public static void main(String[] args) {
        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");

        // Install selective syscall interception
        AndroidOs.install();

        // CloseGuard defaults to true and can be quite spammy.  We
        // disable it here, but selectively enable it later (via
        // StrictMode) on debug builds, but using DropBox, not logs.
        CloseGuard.setEnabled(false);

        Environment.initForCurrentUser();

        // Make sure TrustedCertificateStore looks in the right place for CA certificates
        final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
        TrustedCertificateStore.setDefaultUserDirectory(configDir);

        Process.setArgV0("<pre-initialized>");
        //重点1:
        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 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);
        //重点2:
        Looper.loop();

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

以上的入口函数main方法里边可以看到执行了Looper.prepareMainLooper();,我们进入看下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();
        }
    }

执行了prepare

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

可以看到prepare方法里边有一个threadLocal存储了一个Looper对象(ThreadLocal其实就是线程内存储变量的一个数据结构,具体可以自行了解),接着调用myLooper()方法从threadLocal中取出Looper对象。存储Looper对象时,在Looper的构造方法里边实现了MessageQueue和一个线程,从而将当前的Looper对象绑定到了当前线程,并且可以知道一个线程只有一个Looper对象和一个MessageQueue。
Looper构造方法的源码:

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

接着我们看下MessageQueue的构造方法里边:

  // True if the message queue can be quit.
  //如果推出消息队列则为真
    @UnsupportedAppUsage
    private final boolean mQuitAllowed;

  @UnsupportedAppUsage
    Message mMessages;//message对象

    @UnsupportedAppUsage
    @SuppressWarnings("unused")
    private long mPtr; // used by native code
    private native static long nativeInit();//native方法
    private native static void nativeDestroy(long ptr);

 MessageQueue(boolean quitAllowed) {
        mQuitAllowed = quitAllowed;
        mPtr = nativeInit();
    }

可以看到MessageQueue的构造里边执行了一个本地的native方法
回到应用的入口函数,在调用了Looper.prepare()后,然后调用了Looper.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();
        //获取绑定的MessageQueue对象
        final MessageQueue queue = me.mQueue;
      ···省略

        boolean slowDeliveryDetected = false;
        //重点1
        for (;;) {
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }

         ···省略
            if (observer != null) {
                token = observer.messageDispatchStarting();
            }
            long origWorkSource = ThreadLocalWorkSource.setUid(msg.workSourceUid);
            try {
               //重点2
                msg.target.dispatchMessage(msg);
                if (observer != null) {
                    observer.messageDispatched(token, msg);
                }
                dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
            } catch (Exception exception) {
                if (observer != null) {
                    observer.dispatchingThrewException(token, msg, exception);
                }
                throw exception;
            } finally {
                ThreadLocalWorkSource.restore(origWorkSource);
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
            ···省略
            msg.recycleUnchecked();
        }
    }

有两处重点:当调用了Looper.loop方法后,第一处是loop方法内部有一个死循环从queue队列中next获取message对象,如果queue队列取出的message对象为null表示消息队列为null,则跳出循环;否则,执行 msg.target.dispatchMessage(msg);下发消息,而dispatchMessage这个方法是在handler执行的(msg.target就是handler对象,这里是如何赋值的可以探讨下,指向下层的Binder)
dispatchMessage方法的源码:

    /**
     * Handle system messages here.
     */
    public void dispatchMessage(@NonNull Message msg) {
       if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }
//可以看到最终是调用了handleMessage的回调方法

我们看下handler做了哪些工作,handler最重要的构造方法,它其他的构造方法最终都要走这个构造方法

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

看看handler.sendMessage()

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

//经过handler.sendMessage后调用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;
    }

从上面源码可以知道,经过handler.sendMessage后调用enQueueMessage将消息加入队列

从handler构造的源码可以看到调用了 mLooper = Looper.myLooper()方法,之前我们在分析Looper的构造时候说过,Looper构造里边创建了一个MessageQueue的消息队列并且绑定了当前调用Looper.prepareMainLooper()的线程,细心点我们可以发现myLooper()是静态的,所以我们调用Looper.myLooper()得到的都是同一个Looper对象。至此,我们可以知道从Looper到Handler其实操作的都是同一块区域的对象,而Looper与Handler产生联系的媒介就是Message(msg的target就是Handler)。

在应用的入口ActivityThread的main函数,调用了Looper.

一条线程中只能存在一个Looper对象,一个Looper维护一个MessageQueue队列,Looper调用loop方法循环查询MessageQueue中是否有消息,没有则退出,并阻塞;而MessageQueue中也会不断的循环查询是否有新消息,有就调用next方法取出消息,调用dispatchMessage下发消息,最终调用handler的callBack或handleMessage方法。
  • 为什么我们在主线程中没有申明Looper,却能够之间在主线程中直接使用Handler?
    那是因为在主线程中的入口处已经调用了Looper.paperMainLooper,这里已经创建了Looper,MessageQueue。
  • 那为什么我们调用了handler.sendMessage方法后,在handleMessage方法中就能接收到消息?
    那是因为在ActivityThread的main函数中已经调用了Looper.loop方法,这个方法就是通知Looper持有的MessageQueue对象循环消息的。
  • 那能够在子线程中定义一个handler吗?
    可以,但是要手动调用Looper.paper和loop方法
 private Thread thread = new Thread(new Runnable() {
        @Override
        public void run() {
            Looper.prepare();
            Handler handler = new Handler();
            Looper.loop();
        }
    });
如果想不手动调用paper和loop方法,可以使用HandlerThread,这里边已经封装了。了解HandlerThread推荐这篇文章,同时也能帮助进一步理解handler消息机制https://blog.csdn.net/weixin_41101173/article/details/79687313

总结流程:

当调用Looper.prepare()方法时,在Looper的构造方法中会创建一个MessageQueue对象,并且绑定当前的线程。当调用handler.sendMessage()方法后,会调用队列的enqueueMessage方法将消息加入队列。主线程是默认执行Looper.loop()方法,进而会执行队列的next()方法取出message对象,如果msg不为null,会调用msg.target对象的dispatchMessage()方法下发消息(此时方法已经进入到Handler中),最终会调用handleMessage()方法;如果从message队列中取出message对象为null,则表示没有消息,队列正在退出。

链接博文:https://www.jianshu.com/p/02962454adf7
handler面试中非常有价值的一篇文章:https://www.jianshu.com/p/7e43edbe1678

相关文章

网友评论

      本文标题:认识Handler消息机制

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