Android Loop&Handle学习总结

来源:互联网 发布:gif调色软件 编辑:程序博客网 时间:2024/05/15 00:23

       昨晚偷懒,这篇博客只写了一个标题,今天早晨一看,还有15的阅读量。实在是对不起那些同学.......换了是我,也会BS这样的LZ吧!sorry 啦

-------------------------------------------------------------------------------------------------------------------------------------------------------------

       菜鸟我刚刚接触android源码的时候,当时连java语言都不太熟悉(菜鸟我一直是学C/C++),看到android某个应用的源码的时候,曾为这样的代码迷惑过。


private class MainHandler extends Handler {        @Override        public void handleMessage(Message msg) {            switch (msg.what) {                case RESTART_PREVIEW: {                    restartPreview();                    if (mJpegPictureCallbackTime != 0) {                        long now = System.currentTimeMillis();                        mJpegCallbackFinishTime = now - mJpegPictureCallbackTime;                        Log.v(TAG, "mJpegCallbackFinishTime = "                                + mJpegCallbackFinishTime + "ms");                        mJpegPictureCallbackTime = 0;                    }                    break;                }                ...

        在同一个文件中不就是想调用restartPreview()函数吗?为什么不直接调用就好,还要发个消息然后在这个handleMessage()中来调用呢?初学菜鸟有没有同样的困惑?还是只有LZ一人太白痴了。

     那么到底为什么要这么做呢。是为了异步,是为了多线程,将一些耗时的操作放到一个子线程里面去,大家应该知道如果在android的应用程序主线程,也就是UI线程里面如果执行耗时操作超过5s就会报ANR(application not respon)错误。所以,为了避免ANR,android应用程序将一些耗时操作放到一个独立的工作线程中去。

      那么android是如何做到这点的呢?那个新的工作线程又是如何建立的?这就是android中Looper和Handler类的功劳。下面菜鸟来分析下首先看个类图:

      

       从这个图看到Handler和HandlerThread类都有一个Looper成员变量。而且,Handler和Looper都有成员变量MessageQueue,下面我们看看Looper类和Handler类的作用。

Looper:

       Looper类,来实现消息循环,内部有一个消息队列。看到Looper类的构造函数是private的,这样外部没法实例化Looper对象,Looper对象的构造只能通过Looper的内部接口,来看一段函数: 

/** 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 final void prepare() {        if (sThreadLocal.get() != null) {            throw new RuntimeException("Only one Looper may be created per thread");        }        sThreadLocal.set(new Looper());    }        /** 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.     * {@link #prepare()}     */         public static final void prepareMainLooper() {        prepare();        setMainLooper(myLooper());        if (Process.supportsProcesses()) {            myLooper().mQueue.mQuitAllowed = false;        }    }
        其中函数prepareMainLooper()函数只在ActivityThread.java的main()函数中被调用过。这个prepareMainLooper()的作用保证每个调用线程都有一个Looper对象,这样应用程序的主线程就有一个Looper对象了,so应用程序主线程的消息循环建立了。

        看看,perpare()函数中,有这样一句话 sThreadLocal.set(new Looper());其中sThreadLocal是一个ThreadLocal类型的变量。ThreadLocal表示这是一个线程的局部变量,google之看到下面这段解释。

        Implements a thread-local storage, that is, a variable for which each thread has its own value. All threads share the sameThreadLocal object, but each sees a different value when accessing it, and changes made by one thread do not affect the other threads. The implementation supportsnull values.

       菜鸟斗胆来翻译下,ThreadLocal实现了一种线程的局部存储,ThreadLocal代表一种线程局部变量,对于这种变量来说ThreadLocal保证每个线程都有一个独立的值(value),所有线程都共有一个ThreadLocal对象,但是每个线程在访问这些变量的时候能得到不同的值,每个线程可以更改这些变量并且不会影响其他的线程。ThreadLocal支持NULL值。/*那位大牛有更好的翻译,分享下呗*/

       其中ThreadLocal开放了两个接口:

            public Tget():获取调用线程的局部变量

       public void set(T value) :设置调用线程的局部变量

       perpare()函数的这种处理方式保证在每个调用该函数的线程都有一个独立的Looper对象。

      

Handler:    

       Handler的作用是处理消息,他封装了消息的投递和对消息的处理。通过上面的图可以看到Handler类有一个Looper和MessageQueue成员变量,Handler类有4个不同的构造函数如下:


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());            }        }        /*Handler默认的构造函数,使用当前线程(调用线程)的Looper对象给Handler成员变量mLooper赋值*/         mLooper = Looper.myLooper();        if (mLooper == null) {            throw new RuntimeException(                "Can't create handler inside thread that has not called Looper.prepare()");        }        /*使用当前线程(调用线程)的消息队列给Handler的mQueue赋值*/        mQueue = mLooper.mQueue;        mCallback = null;    }    /**     * Constructor associates this handler with the queue for the     * current thread and takes a callback interface in which you can handle     * messages.     */    public Handler(Callback callback) {        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;        /*跟默认构造函数差不多,只不过带了一个callback*/        mCallback = callback;    }    /**     * Use the provided queue instead of the default one.     */    public Handler(Looper looper) {        /*使用指定的Looper来给Handler类的mLooper赋值*/        mLooper = looper;        mQueue = looper.mQueue;        mCallback = null;    }    /**     * Use the provided queue instead of the default one and take a callback     * interface in which to handle messages.     */    public Handler(Looper looper, Callback callback) {        /*使用指定的Looper和CallBack来初始化Handler*/        mLooper = looper;        mQueue = looper.mQueue;        mCallback = callback;    }
     
       可以看到Handler类的成员变量MessageQueue mQueue都会指向Looper类的的MessageQueue,Handler为什么要这么做呢?

      下面看看Handler类提供那些接口:

sendEmptyMessage

public final Message obtainMessage(int what)    {        return Message.obtain(this, what);    }/**     * 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);        }    }/**     * Sends a Message containing only the what value.     *       * @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.     */    public final boolean sendEmptyMessage(int what)    {        return sendEmptyMessageDelayed(what, 0);    }/**     * Remove any pending posts of messages with code 'what' that are in the     * message queue.     */    public final void removeMessages(int what) {        mQueue.removeMessages(this, what, null, true);    }     /**     *注意这个函数,函数将有子类实现     * Subclasses must implement this to receive messages.     */    public void handleMessage(Message msg) {    }
        其中sendMessage函数都会调用到sendMessageAtTime函数,下面看下sendMessageAtTime(Message msg, long uptimeMillis)函数的实现

public boolean sendMessageAtTime(Message msg, long uptimeMillis)    {        boolean sent = false;        MessageQueue queue = mQueue;        if (queue != null) {            /*看到Message的target变量指向当前这个Handler类*/            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;    }

       msg.target指明了这个msg将有谁来处理,在这里msg.target=this,Handler类除了封装消息添加外还封装了消息处理的接口。


Looper和Handler的联系:

           本菜鸟不才不知道上面的分析有没有错误,有没有让很多初学者犯迷糊,下面我将分析以下Looper和Handler类的联系,希望可以把上面的知识点串联起来。

           当调用Looper类的loop()函数的时候当前线程就进入到一个消息循环中去。看看这个loop()@Looper.java函数先.

/**     *  Run the message queue in this thread. Be sure to call     * {@link #quit()} to end the loop.     */    public static final void loop() {        Looper me = myLooper();        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();                while (true) {            Message msg = queue.next(); // might block            //if (!me.mRun) {            //    break;            //}            if (msg != null) {                if (msg.target == null) {                    // No target is a magic identifier for the quit message.                    return;                }                if (me.mLogging!= null) me.mLogging.println(                        ">>>>> Dispatching to " + msg.target + " "                        + msg.callback + ": " + msg.what                        );                /*下面的函数将Looper类和Handler类联系起来*/                msg.target.dispatchMessage(msg);                if (me.mLogging!= null) me.mLogging.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("Looper", "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.recycle();            }        }    }
        可以看到当前线程不断的从消息队列中取出消息,如果消息的target成员变量为null,就表示要退出消息循环了,否则的话就要调用这个target对象的dispatchMessage成员函数来处理这个消息,这个target对象的类型为Handler

           

                 其实这个Looper和Handler类中消息传递的机制还是很复杂到,用到了Linux中Pipe的相关知识,我太菜还不能做出更深的分析,以后有时间会好好学习下,在完善下这部份的知识

          好了,这篇blog就说到这里吧。下篇blog我会试着分析下,Handler和Looper的同步问题。届时会顺便学习一下HandlerThread类


              忘了说,如果哪位大牛发现我这篇blog有什么错误,敬请指正,免得误导了跟我一样的菜鸟



原创粉丝点击