• Android主线程的消息系统(HandlerLooper)



    前言:

    之前的文章写的都是关于Bitmap和内存的优化技术。这一篇文章给大家谈谈Handler。

    Handler是Android系统中比較重要的一个知识。在Android多线程面试常常会被问到,在实际项目中的确也常常常使用到。当然也比較复杂,知识比較多。牵扯到的类有Thread、Looper、Message、MessageQueue。

    Android是支持多线程的,通常应用程序中与用户相关的UI事件都是执行在主线程中。比方点击屏幕、button等。为了保持主线程顺畅对应用户事件不被堵塞就须要把耗时的操作(主要是联网、操作大文件等)放到子线程中,这个时候你可能会想到Handler(当然还你能够用其它的比方:异步任务,,这个以后再讲)。可是Handler又是怎么和Thread联系起来的呢?这个咱们来看一下Android主线程是怎么创建的。

    ActivityThread:

    在ActivityThread.java中有一个main()函数,这个函数就是在一个应用启动的入口,调用关系是:ActivityManagerService.java中的startProcessLocked函数调用例如以下代码:

    // Start the process.  It will either succeed and return a result containing
    // the PID of the new process, or else throw a RuntimeException.
    Process.ProcessStartResult startResult = Process.start("android.app.ActivityThread",
                        app.processName, uid, uid, gids, debugFlags,
                        app.info.targetSdkVersion, null);

    Process.start又做了例如以下的操作,仅仅看方法凝视即可,如今不须要知道详细做了什么:

    /**
         * Start a new process.
         * 
         * <p>If processes are enabled, a new process is created and the
         * static main() function of a <var>processClass</var> is executed there.
         * The process will continue running after this function returns.
         * 
         * <p>If processes are not enabled, a new thread in the caller's
         * process is created and main() of <var>processClass</var> called there.
         * 
         * <p>The niceName parameter, if not an empty string, is a custom name to
         * give to the process instead of using processClass.  This allows you to
         * make easily identifyable processes even if you are using the same base
         * <var>processClass</var> to start them.
         * 
         * @param processClass The class to use as the process's main entry
         *                     point.
         * @param niceName A more readable name to use for the process.
         * @param uid The user-id under which the process will run.
         * @param gid The group-id under which the process will run.
         * @param gids Additional group-ids associated with the process.
         * @param debugFlags Additional flags.
         * @param targetSdkVersion The target SDK version for the app.
         * @param zygoteArgs Additional arguments to supply to the zygote process.
         * 
         * @return An object that describes the result of the attempt to start the process.
         * @throws RuntimeException on fatal start failure
         * 
         * {@hide}
         */
        public static final ProcessStartResult start(final String processClass,
                                      final String niceName,
                                      int uid, int gid, int[] gids,
                                      int debugFlags, int targetSdkVersion,
                                      String[] zygoteArgs) {
            try {
                return startViaZygote(processClass, niceName, uid, gid, gids,
                        debugFlags, targetSdkVersion, zygoteArgs);
            } catch (ZygoteStartFailedEx ex) {
                Log.e(LOG_TAG,
                        "Starting VM process through Zygote failed");
                throw new RuntimeException(
                        "Starting VM process through Zygote failed", ex);
            }
        }

    通过凝视也能看到上面的函数会找到ActivityThread的main函数而且运行。main函数中创建了Looper,Looper的作用就是利用线程创建一个消息处理队列,而且维护这个消息队列:

     public static void main(String[] args) {
            Looper.prepareMainLooper();//创建Looper
            if (sMainThreadHandler == null) {
                sMainThreadHandler = new Handler();
            }
            ActivityThread thread = new ActivityThread();
            thread.attach(false);//应用全部的逻辑都在这种方法中
            Looper.loop();//开启一个消息循环。不断的读取MessageQueue中的Message。
        }

    Looper:

    Looper.prepareMainLooper()的代码例如以下:

    /**
         * 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();
            setMainLooper(myLooper());
            myLooper().mQueue.mQuitAllowed = false;
        }
    

    上面的方法凝视已经说的非常明确,创建了主线程的Looper,这段代码是系统调用的。

    先看prepare方法做了什么操作。

    /** 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() {
            if (sThreadLocal.get() != null) {
                throw new RuntimeException("Only one Looper may be created per thread");
            }
            sThreadLocal.set(new Looper());
        }
        private Looper() {
            mQueue = new MessageQueue();
            mRun = true;
            mThread = Thread.currentThread();//获取当前线程
        }
    

    创建主线程的Looper,每个Looper相应一个Thread、一个MessageQueue,创建Looper的时候会创建一个MessageQueue。到眼下位置创建了应用的主线程(Thread)、Looper、MessageQueue,调用Looper.loop(),開始不断的从MessageQueue中读取Message并处理。假设没有消息则等待。

    如今有了消息循环,有了管理消息循环的Looper就差发送消息和处理消息的Handler了。

    Handler:

    这个时候你在你的应用中创建一个Handler,一般都是以下的代码:

     private static final Handler mHandler = new Handler() {
            public void handleMessage(Message msg) {
                ..........
                }
            }
        };

    这个Handler是在主线程中创建的,Handler的构造函数例如以下:

     /**
         * Default constructor associates this handler with the queue for the
         * current thread.
         *
         * If there isn't one, this handler won't be able to receive messages.
         */
        public Handler() {
            mLooper = Looper.myLooper();//获取上面在主线程创建的Looper
            if (mLooper == null) {
                throw new RuntimeException(
                    "Can't create handler inside thread that has not called Looper.prepare()");
            }
            mQueue = mLooper.mQueue;//获取Looper的MessageQueue
            mCallback = null;//默觉得null在后面处理msg时会即可检查
        }

    创建完Handler你就能够用了,比方你发一个消息:

    mHandler.sendEmptyMessage(MSG_WHAT);

    在系统中会走终于走到Handler.java以下的方法:

    /**
         * Enqueue a message into the message queue after all pending messages
         * before the absolute time (in milliseconds) <var>uptimeMillis</var>.
         * <b>The time-base is {@link android.os.SystemClock#uptimeMillis}.</b>
         * You will receive it in {@link #handleMessage}, in the thread attached
         * to this handler.
         * 
         * @param uptimeMillis The absolute time at which the message should be
         *         delivered, using the
         *         {@link android.os.SystemClock#uptimeMillis} time-base.
         *         
         * @return Returns true if the message was successfully placed in to the 
         *         message queue.  Returns false on failure, usually because the
         *         looper processing the message queue is exiting.  Note that a
         *         result of true does not mean the message will be processed -- if
         *         the looper is quit before the delivery time of the message
         *         occurs then the message will be dropped.
         */
        public boolean sendMessageAtTime(Message msg, long uptimeMillis)
        {
            boolean sent = false;
            MessageQueue queue = mQueue;
            if (queue != null) {
                msg.target = this;//注意这行代码后面会用,把Handler赋值给Msg的target对象
                sent = queue.enqueueMessage(msg, uptimeMillis);//把msg放到MsgQueue中
            }
            else {
                RuntimeException e = new RuntimeException(
                    this + " sendMessageAtTime() called with no mQueue");
                Log.w("Looper", e.getMessage(), e);
            }
            return sent;
        }

    上面的方法第二个是延时毫秒数,queue.enqueueMessage把消息发送到MessageQueue后剩下的就是等待消息被处理,前面不是说了Looper.loop()方法開始轮询消息队列吗,你发送的消息就是在loop方法中读取到的,读取到后谁去处理呢?在loop()方法中有一句代码:

    msg.target.dispatchMessage(msg);

    msg就是你发送到MessageQueue的消息,被读取后调用target.dispatchMessage(),这个target就是上面Handler发送消息是赋值的。就是发送消息的Handler本身,然后Handler调用自己的以下方法即可消息处理:

     /**
         * Handle system messages here.
         */
        public void dispatchMessage(Message msg) {
            if (msg.callback != null) {
                handleCallback(msg);
            } else {
                if (mCallback != null) {
                    if (mCallback.handleMessage(msg)) {
                        return;
                    }
                }
                handleMessage(msg);//在这会调用到上面重写的handleMessage方法。
            }
        }

    由于在new Message的时候callback为空。而且Handler的mCallback = null,所以会调用到你上面new Handler时重写的handleMessage方法。

    总结:

    每个线程中都相应一个Looper,每个Looper都相应一个MessageQueue。这个Looper是用来管理消息队列的。主要是读取消息队列和把消息发送给Message的target去处理。

    到这你应该清除Thread、Handler、Message、MessageQueue和Looper他们之间的关系了吧。


    大家假设对编程感兴趣,想了解很多其它的编程知识。解决编程问题,想要系统学习某一种开发知识,我们这里有java高手。C++/C高手,windows/Linux高手,android/ios高手,请大家关注我的微信公众号:程序猿互动联盟or coder_online,大牛在线为您提供服务。

    【C语言基础】聊聊C语言(7)-我的地盘我做主


  • 相关阅读:
    如何在域和域之间建立信任域?关键是配置域和域之间的DNS服务器
    Client Recevier 命令安装 AllUsers=1
    在XD 4.0 设置Policy
    更改linux系统中键盘设置
    如何在DNS中添加另一DNS
    CMD命令
    KMS激活OS(系统)
    谁抢了我的IP!!!~
    关于域证书的发布CA和CRL的内容 (Windows 2008 Server R2 SP1)
    XenServer假死状态
  • 原文地址:https://www.cnblogs.com/wgwyanfs/p/7258706.html
Copyright © 2020-2023  润新知