zoukankan      html  css  js  c++  java
  • android高级---->Handler的原理

      andriod提供了Handler来满足线程间的通信,上次在更新UI的时候也提到过Handler的使用,关于Handler的基本使用,参见博客(android基础---->子线程更新UI).今天我们深入Handler的源码,了解一个Handler的内部执行原理。

    目录导航

    1.   Handler简单说明
    2.   ActivityThread的说明
    3.   Handler的预备分析
    4.   Handler的原理分析
    5.   友情链接

    Handler简单说明

    一、 在Handler的原理说明之前,我们列出相关的重要的类:

    • Message:消息,其中包含了消息ID,消息处理对象以及处理的数据等,由MessageQueue统一列队,终由Handler处理。
    • Handler:处理者,负责Message的发送及处理。使用Handler时,需要实现handleMessage(Message msg)方法来对特定的Message进行处理,例如更新UI等。
    • MessageQueue:消息队列,用来存放Handler发送过来的消息,并按照FIFO规则执行。当然,存放Message并非实际意义的保存,而是将Message以链表的方式串联起来的,等待Looper的抽取。
    • Looper:消息泵,不断地从MessageQueue中抽取Message执行。因此,一个MessageQueue需要一个Looper
    • Thread:线程,负责调度整个消息循环,即消息循环的执行场所。

     

    二、 Handler主要有两种用途:

    • 合理调度安排消息和runnable对象,使它们在将来的某个点被执行。
    • 将一个动作入队安排在非当前线程执行。

    三、 Handler创建消息

      每一个消息都需要被指定的Handler处理,通过Handler创建消息便可以完成此功能。Android消息机制中引入了消息池。Handler创建消息时首先查询消息池中是否有消息存在,如果有直接从消息池中取得,如果没有则重新初始化一个消息实例。使用消息池的好处是:消息不被使用时,并不作为垃圾回收,而是放入消息池,可供下次Handler创建消息时使用。消息池提高了消息对象的复用,减少系统垃圾回收的次数。

    四、 Handler发送消息

      UI主线程初始化第一个Handler时会通过ThreadLocal创建一个Looper,该Looper与UI主线程一一对应。使用ThreadLocal的目的是保证每一个线程只创建唯一一个Looper。之后其他Handler初始化的时候直接获取第一个Handler创建的Looper。Looper初始化的时候会创建一个消息队列MessageQueue。至此,主线程、消息循环、消息队列之间的关系是1:1:1。

    五、 Handler处理消息

      UI主线程通过Looper循环查询消息队列UI_MQ,当发现有消息存在时会将消息从消息队列中取出。首先分析消息,通过消息的参数判断该消息对应的Handler,然后将消息分发到指定的Handler进行处理。注意handler是主线程中的类。

    ActivityThread的说明

    一、 Android的一个apk在打开时,使用到的第一个类就是这个类。

    这里我们不深入Android应用程序的启动原理了,ActivityThread是一个final类,不是一个线程类:

    public final class ActivityThread 

    二、 ActivityThread在创建的时候同时也启动了一个线程,这个线程就是一个异步线程。

    他创建出了MessageQueue,并同时进行消息的轮 询,因此当这个应用程序在运行时,这个线程是一直都在的。这个线程就是应用程序的主线程,UI的处理等都是在这个线程处理的。在AcitivityThread这个类的是有一个main方法的。我们知道java应用程序的入口就是main方法。这就是程序的入口。我们来看ActivityThread的源代码:

    public static void main(String[] args) {
        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
        SamplingProfilerIntegration.start();
    
        // 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();
    
        // Set the reporter for event logging in libcore
        EventLogger.setReporter(new EventLoggingReporter());
    
        AndroidKeyStoreProvider.install();
    
        // 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>");
    
        Looper.prepareMainLooper();
    
        ActivityThread thread = new ActivityThread();
        thread.attach(false);
    
        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();
    
        throw new RuntimeException("Main thread loop unexpectedly exited");
    }

      这里我们重点关注两点,这个在Handler的原理分析中会做详细的说明:

    • Looper.prepareMainLooper();
    • Looper.loop();

    Handler的预备分析

    一、 首先程序在启动的时候,会执行ActivityThread类的main方法,如上我们提到两个重要的代码: Looper.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方法中,创建了一个Looper,并把它放在了ThreadLocal中:

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

    二、 ActivityThread中的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; // 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(); for (;;) {
         // 从队列中取出消息 Message msg
    = queue.next(); // might block if (msg == null) { // No message indicates that the message queue is quitting. return; } // This must be in a local variable, in case a UI event sets the logger Printer logging = me.mLogging; if (logging != null) { logging.println(">>>>> Dispatching to " + msg.target + " " + msg.callback + ": " + msg.what); }
        // 分发消息 msg.target.dispatchMessage(msg);
    if (logging != null) { logging.println("<<<<< Finished to " + msg.target + " " + msg.callback); } // Make sure that during the course of dispatching the // identity of the thread wasn't corrupted. final long newIdent = Binder.clearCallingIdentity(); if (ident != newIdent) { Log.wtf(TAG, "Thread identity changed from 0x" + Long.toHexString(ident) + " to 0x" + Long.toHexString(newIdent) + " while dispatching to " + msg.target.getClass().getName() + " " + msg.callback + " what=" + msg.what); } msg.recycleUnchecked(); } }

      在msg.target.dispatchMessage(msg);方法中msg.target这是分发消息的Handler

    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
         // 首先,处理Message自己的callback,调用其run方法 handleCallback(msg); }
    else { if (mCallback != null) {
           // 其次,调用Handler自留的接口对象
    if (mCallback.handleMessage(msg)) { return; } }
         // 最后,调用handleMessage方法处理消息,Handler类中这个方法为空,子类可以重写这个方法 handleMessage(msg); } }

    Handler的原理分析

    我们仍旧用一个例子来加以说明Handler的原理,例子代码具体可以参见我的博客:(android基础---->子线程更新UI)。以下的分析我会帖出有关例子的重要代码:

    一、 创建一个Handler,处理消息:

    public static final int UPDATE_TEXT = 1;
    private Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case UPDATE_TEXT:
                    textView.setText("I Love you.");
                    break;
                default:
                    break;
            }
        }
    }

    二、 发送消息:

    // 用handler处理上述问题
    public void handlerUpdate(View view) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                Message message = new Message();
                message.what = UPDATE_TEXT;
                handler.sendMessage(message); // 将Message对象发送出去
            }
        }).start();
    }

    三、 首先我们要创建一个handler,具体构造方法代码如下,这里由于是new Handler():callback=null,async=false

    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());
            }
        }
    
        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.sendMessage(message),源代码如下:(中间我们省略了一些过程)

    public boolean sendMessageAtTime(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);
    }
      enqueueMessage(queue, msg, uptimeMillis)的代码如下:
    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }
    • 所谓发送消息就是把消息放入消息队列中的合适位置,并且把消息的target设置为本Handler对象。

    五、 在上述Handler的预备分析当中,我们提到过Looper的loop方法,它负责从队列中取出消息,并且分发消息。

    而且我们在Handler的dispatchMessage方法也了解到,分发的消息会由Handler的handleMessage方法处理:也就是我们创建的Handler的重写方法:

    public void handleMessage(Message msg) {
        switch (msg.what) {
            case UPDATE_TEXT:
                textView.setText("I Love you.");
                break;
            default:
                break;
        }
    }
    • 当队列中的消息处理的时候,也会找到当时送它来的Handler对象,
    • 调用其相应的dispatchMessage()方法
    • 调用其中的handleMessage()方法或者mCallback成员的handleMessage()方法来进行处理。

    友情链接

  • 相关阅读:
    题解 P5320
    Codeforces 1500F
    三个 AGC D(AGC037D、AGC043D、AGC050D)
    Atcoder Regular Contst 084 D
    DG-基础知识点整理
    MySQL-数据恢复场景实验
    MySQL-查看Galera集群状态
    MySQL-运行日志切割
    MySQL-生产环境删除大表或大量binlog策略
    MySQL-基于(MySQL 5.7)NDB中启用共享权限表
  • 原文地址:https://www.cnblogs.com/huhx/p/handlerTheory.html
Copyright © 2011-2022 走看看