zoukankan      html  css  js  c++  java
  • Handler、Looper、MessageQueue、Message的联系

    一般线程间的消息传递,有许多方法,有Activity.runOnUiThread、Handler.post、View.post等方法,这些方法本质上还是通过Handler传递一个Message来实现的。举个常见的场景:在子线程进行任务的下载,需要实时显示进度在UI上,但是AndroidUI是线程不安全的,直接在子线程刷新UI是会导致应用崩溃的。一般的做法是创建一个Handler对象,通过在子线程中发送message,在handleMessage中进行刷新界面就不会有问题了。这种处理方式被称为异步消息处理模型。

    一、异步消息处理模型

    该模型主要由以下几个构成:

    1)Handler:负责消息的发送,接收;

    2)Looper:工作线程,不断地从消息队列中取出message,不断地回调;

    3)MessageQueue:消息队列,消息存放的地方,需要同步机制,保证在多个不同线程插入消息的有序性;

    4)Message:消息

    二、源码分析

    1)创建Handler

    通常,我们创建Handler话,是通过new一个对象,那么在主线程创建和子线程创建有什么不同吗?

    public class MainActivity extends Activity {  
          
        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();  
        }  
      
    }  

    如果运行上面的代码,结果是handler1创建没有问题,handler2创建就导致程序崩溃了,提示的错误信息为 Can't create handler inside thread that has not called Looper.prepare() ,意思是不能在线程中创建一个没有调用Looper.prepare方法的Handler。也许你会有疑问了,为什么在UI线程就可以呢?我们来看下Handler的构造方法:

    public Handler() {  
        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 = null;  
    }  
    

    关键的是这句:mLooper = Looper.myLooper();  获取一个Looper对象,如果返回null,就会抛出异常。我们来看看myLooper方法,

    public static final Looper myLooper() {  
        return (Looper)sThreadLocal.get();  
    } 

    该方法中是从sThreadLocal中获取Looper对象,既然有get方法,显然会有与之对应的set方法,在该方法中创建一个Looper对象,分析源码,我们发现在prepare方法中调用了set方法,

    public static final void prepare() {  
        if (sThreadLocal.get() != null) {  
            throw new RuntimeException("Only one Looper may be created per thread");  
        }  
        sThreadLocal.set(new Looper());  
    } 

    如果sThreadLocal.get()返回的不为空,就会抛出异常,提示Looper只能创建一个,所以prepare方法只能调用1次;如果没有Looper对象,就创建一个,设置到sThreadLocal中。所以这也能解释为什么需要先调用prepare才能创建Handler对象了,而且每个Handler只有一个对应的Looper。

    说到这,你可能要说了,这个也没有解释为什么在主线程中创建Handler就可以呀。别急,我们接着分析。这个是因为程序启动时 ,系统主动帮我们调用Looper.prepare方法了。查看ActivityThread类,我们发现了它的调用位置。

    public static void main(String[] args) {  
        SamplingProfilerIntegration.start();  
        CloseGuard.setEnabled(false);  
        Environment.initForCurrentUser();  
        EventLogger.setReporter(new EventLoggingReporter());  
        Process.setArgV0("<pre-initialized>");  
        Looper.prepareMainLooper();  
        ActivityThread thread = new ActivityThread();  
        thread.attach(false);  
        if (sMainThreadHandler == null) {  
            sMainThreadHandler = thread.getHandler();  
        }  
        AsyncTask.init();  
        if (false) {  
            Looper.myLooper().setMessageLogging(new LogPrinter(Log.DEBUG, "ActivityThread"));  
        }  
        Looper.loop();  
        throw new RuntimeException("Main thread loop unexpectedly exited");  
    } 
    从Looper.prepareMainLooper();  从这个方法名来看,我们不难猜出是在这个方法中调用了prepare方法。
    public static final void prepareMainLooper() {  
        prepare();  
        setMainLooper(myLooper());  
        if (Process.supportsProcesses()) {  
            myLooper().mQueue.mQuitAllowed = false;  
        }  
    }  
    

    到这里,总算明白为什么主线程中创建Handler可以了,而想在子线程创建Handler,必须先调用Looper.prepare方法才可以。  

    2)发送message

    创建完Handler后,一般我们通过创建Message对象,使用sendMessage(Message msg)等方法发送消息,然后在Handler的handlerMessage方法中接收消息。那么消息是发送到哪里?为什么Handler能接收到消息呢?我们继续往下分析。

    通过阅读源码,我们发现发送消息的方法最终都会调用到sendMessageAtTime方法,代码如下:

    public boolean sendMessageAtTime(Message msg, long uptimeMillis)  
    {  
        boolean sent = false;  
        MessageQueue queue = mQueue;  
        if (queue != null) {  
            msg.target = this;  
            sent = queue.enqueueMessage(msg, uptimeMillis);  
        }  
        else {  
            RuntimeException e = new RuntimeException(  
                this + " sendMessageAtTime() called with no mQueue");  
            Log.w("Looper", e.getMessage(), e);  
        }  
        return sent;  
    }  
    

    从代码我们可以看到,第一个参数就是我们传递进来的message,第二个参数uptimeMillis是指系统自启动到当前时间的毫秒数加上延迟的时间,然后将这两个参数设置到enqueueMessage方法中。代码中的MessageQueue,就是消息队列的意思,消息都存放在MessageQueue中。MessageQueue是在Looper的构造函数中创建的,因此一个Looper对应一个MessageQueue。它有提供消息入列和出列的方法。enqueueMessage就是入列的方法。我们来下这个方法:

    final boolean enqueueMessage(Message msg, long when) {  
        if (msg.when != 0) {  
            throw new AndroidRuntimeException(msg + " This message is already in use.");  
        }  
        if (msg.target == null && !mQuitAllowed) {  
            throw new RuntimeException("Main thread not allowed to quit");  
        }  
        synchronized (this) {  
            if (mQuiting) {  
                RuntimeException e = new RuntimeException(msg.target + " sending message to a Handler on a dead thread");  
                Log.w("MessageQueue", e.getMessage(), e);  
                return false;  
            } else if (msg.target == null) {  
                mQuiting = true;  
            }  
            msg.when = when;  
            Message p = mMessages;  
            if (p == null || when == 0 || when < p.when) {  
                msg.next = p;  
                mMessages = msg;  
                this.notify();  
            } else {  
                Message prev = null;  
                while (p != null && p.when <= when) {  
                    prev = p;  
                    p = p.next;  
                }  
                msg.next = prev.next;  
                prev.next = msg;  
                this.notify();  
            }  
        }  
        return true;  
    } 
    

    首先你要知道,MessageQueue并没有使用一个集合把所有的消息都保存起来,它只使用了一个mMessages对象表示当前待处理的消息。然后观察上面的代码的16~31行我们就可以看出,所谓的入队其实就是将所有的消息按时间来进行排序,这个时间当然就是我们刚才介绍的uptimeMillis参数。具体的操作方法就根据时间的顺序调用msg.next,从而为每一个消息指定它的下一个消息是什么。当然如果你是通过sendMessageAtFrontOfQueue()方法来发送消息的,它也会调用enqueueMessage()来让消息入队,只不过时间为0,这时会把mMessages赋值为新入队的这条消息,然后将这条消息的next指定为刚才的mMessages,这样也就完成了添加消息到队列头部的操作。

    接下来我们看下消息出列方法。这个方法在Looper.loop方法中调用:

    public static final void loop() {  
        Looper me = myLooper();  
        MessageQueue queue = me.mQueue;  
        while (true) {  
            Message msg = queue.next(); // might block  
            if (msg != null) {  
                if (msg.target == null) {  
                    return;  
                }  
                if (me.mLogging!= null) me.mLogging.println(  
                        ">>>>> Dispatching to " + msg.target + " "  
                        + msg.callback + ": " + msg.what  
                        );  
                msg.target.dispatchMessage(msg);  
                if (me.mLogging!= null) me.mLogging.println(  
                        "<<<<< Finished to    " + msg.target + " "  
                        + msg.callback);  
                msg.recycle();  
            }  
        }  
    }  

    从第4行可以看出,它进入一个死循环,不断地取出消息,如果当前的消息队列中存在待处理消息,就将这个消息出列,然后让下一条消息成为待处理,否则就进入堵塞,直至新消息入列。不难看出msg.targer就是指发送消息的Handler,每当有一个消息出列,就会传递到dispatchMessage方法。我们来下这个方法:

    public void dispatchMessage(Message msg) {  
        if (msg.callback != null) {  
            handleCallback(msg);  
        } else {  
            if (mCallback != null) {  
                if (mCallback.handleMessage(msg)) {  
                    return;  
                }  
            }  
            handleMessage(msg);  
        }  
    }  
    

    如果callback不为空,就返回回调;如果为空,就返回handlerMessage。到这里,相信大家明白为什么Handler能够接收到之前发送出去的消息了。

    三、总结

    一个完整的异步消息处理模型的流程图如下:

    创建Looper,它在构造方法中会创建一个MessageQueue,所以一个Looper对应一个MessageQueue;

    Looper调用prepare方法,再次调用的话会报错,所以一个Handler对应一个Looper;

    创建Handler对象,再调用loop方法,让队列无限循环,之后通过Handler的方法,将message插入到MessageQueue中,它会根据target属性找到对应的Handler,执行dispatchMessage方法,决定是调用callback还是handler方法,这样就消费了一个message。

    参考链接:

    Android异步消息处理机制完全分析

    Handler源码分析

  • 相关阅读:
    SQL SERVER之居然连计算机管理员都无法访问
    用户控件中动态加入脚本引用
    DIV中的对象错位问题
    IIS备份
    下载防盗链图片的关键
    DNS失效导致邮件发送不出去
    自定义ASP.NET服务器控件与用户控件
    生成SQL SERVER数据库脚本
    数据库的自动备份
    服务器的备份
  • 原文地址:https://www.cnblogs.com/hacjy/p/6909209.html
Copyright © 2011-2022 走看看