Android主线程的消息系统(Handler\Looper)

前言:

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

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

Android是支持多线程的,通常应用程序中与用户相关的UI事件都是运行在主线程中,比如点击屏幕、按钮等,为了保持主线程顺畅相应用户事件不被阻塞就需要把耗时的操作(主要是联网、操作大文件等)放到子线程中,这个时候你可能会想到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,大牛在线为您提供服务。

时间: 2024-08-02 18:22:00

Android主线程的消息系统(Handler\Looper)的相关文章

Android:子线程向UI主线程发送消息

在Android里,UI线程是不允许被阻塞的,因此我们要将耗时的工作放到子线程中去处理. 那么子线程耗时处理后要怎样通知UI线程呢? 我们可以在UI主线程中创建一个handler对象,然后通过重写其handleMessage(Message msg)的方法,该方法会接收到子线程中的handler对象的sendMessage((Message msg)发回来的消息.这样一发一收就完成工作: 而关于主线程向子线程发送消息的内容可以看我的上一篇博客,其中讲到了Looper类及其两个重要方法和实现原理.

Android:主线程如何向子线程发送消息

今天讲一下,在Android中主线程如何向子线程中发送消息的问题. 或许回想无非就是创建一个Handler对象,然后一个线程发消息,另一个接收消息嘛-- 原理确实是这样,但是我们平时,是从子线程向主线程发消息,而主线程默认已经帮我们完成了Looper的操作,所以我们只需要简单的"创建一个Handler对象,然后一个线程发消息,另一个接收消息"-- 我们先说一下这个Looper是神马吧. 它就像一个消息队列(MessageQueue)的管家(Looper),一个消息队列只有一个管家,并且

Android主线程、子线程通信(Thread+handler)

Android是基于Java的,所以也分主线程,子线程! 主线程:实现业务逻辑.UI绘制更新.各子线程串连,类似于将军: 子线程:完成耗时(联网取数据.SD卡数据加载.后台长时间运行)操作,类似于小兵: 一.子线程向主线程发消息(Thread+handler): 1.主线程中定义Handler: Java代码   Handler mHandler = new Handler(){ @Override public void handleMessage(Message msg) { super.h

Handler一定要在主线程实例化吗?new Handler()和new Handler(Looper.getMainLooper())的区别?

一个帖子的整理: Handler一定要在主线程实例化吗?new Handler()和new Handler(Looper.getMainLooper())的区别如果你不带参数的实例化:Handler handler = new Handler();那么这个会默认用当前线程的looper一般而言,如果你的Handler是要来刷新操作UI的,那么就需要在主线程下跑.情况:1.要刷新UI,handler要用到主线程的looper.那么在主线程 Handler handler = new Handler

android 主线程和子线程之间的消息传递

从主线程发送消息到子线程(准确地说应该是非UI线程) package com.zhuozhuo;import android.app.Activity;import android.os.Bundle;import android.os.Handler;import android.os.Looper;import android.os.Message;import android.util.Log;import android.view.View;import android.view.Vie

Android 主线程和子线程通信问题

Android 现在不支持View在子线程中创建及调用其方法.如果要实现子线程内容更新之后,将结果及时反馈到主线程中,该如何出来呢? 可以在主线程中创建Handler来实现. 这样子线程的结果,可以通过发消息的形式,通知主线程,然后主线程中去及时更新View控件. Handler的使用方式: mHandler = new Handler(){ @Override public void handleMessage(Message msg) { super.handleMessage(msg);

Android主线程不能访问网络异常解决办法

从两个方面说下这个问题: 1. 不让访问网络的原因 2. 解决该问题的办法 不让访问网络的原因: 由于对于网络状况的不可预见性,很有可能在网络访问的时候造成阻塞,那么这样一来我们的主线程UI线程 就会出现假死的现象,产生很不好的用户体验.所以,默认的情况下如果直接在主线程中访问就报出了这个异常,名字是NetworkOnMainThreadException 解决该问题的办法 1. 独立线程 2. 异步线程AsyncTask 3. StrictMode修改默认的策略 1) 独立线程的办法 启动一个

libjingle主线程的消息响应

trunk\talk\app\webrtc\peerconnectionproxy.h文件中定义了PeerConnection的代理类,class PeerConnection : public PeerConnectionInterface,而#define BEGIN_PROXY_MAP(c) \  class c##Proxy : public c##Interface所以BEGIN_PROXY_MAP(PeerConnection)展开即为 class PeerConnection : 

android 消息系统Handler、MessageQueue、Looper源码学习

android消息系统 整体框架如图所示 在安卓的消息系统中,每个线程有一个Looper,Looper中有一个MessageQueue,Handler向这个队列中投递Message,Looper循环拿出Message再交由Handler处理.整体是一个生产者消费者模式,这四部分也就构成了android的消息系统. 先来看一个最简单的例子 //这段代码在某个Activity的onCreate中 Handler handler = new Handler(Looper.getMainLooper()