HardBirch

Looper源码解析

时间:12-11-04 栏目:安卓源码解析与小应用 作者:张飞不张,文采横飞 评论:0 点击: 2,030 次

          前面写了两篇源码解析了,Handler源码解析MessageQueue源码解析,其中MessageQueue源码解析里面情况解释的不是太清晰,随着以后对代码的理解,会有后续内容的添加。

Class used to run a message loop for a thread. Threads by default do not have a message loop associated with them; to create one, call prepare() in the thread that is to run the loop, and then loop() to have it process messages until the loop is stopped.

Most interaction with a message loop is through the Handler class.

This is a typical example of the implementation of a Looper thread, using the separation of prepare() and loop() to create an initial Handler to communicate with the Looper.

类用于为一个线程运行一个消息循环。默认情况下线程没有相关联的消息循环;可以通过在线程里调用looper的prepare()方法创建一个looper,然后就会调用loop()处理消息之道loop被停止。

大多数消息是通过handler来处理。

下面是一个典型的Looper thread的实现,使用分离的准备()和循环()来创建一个Handler与Looper进行通信。

class LooperThread extends Thread {
      public Handler mHandler;

      public void run() {
          Looper.prepare();

          mHandler = new Handler() {
              public void handleMessage(Message msg) {
                  // process incoming messages here
              }
          };

          Looper.loop();
      }
  }

先看一下类中的变量:

// sThreadLocal.get() will return null unless you've called prepare().
    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

    final MessageQueue mQueue;
    final Thread mThread;
    volatile boolean mRun;

    private Printer mLogging = null;
    private static Looper mMainLooper = null;  // guarded by Looper.class

  1. ThreadLocal主要是用于存放looper,如果没有调用prepare()方法的话,使用sThreadLocal的get方法会返回空。具体原因会在后面代码里具体说明。关于ThreadLocal,在这多说两句:ThreadLocal很容易让人望文生义,想当然地认为是一个“本地线程”。其实,ThreadLocal并不是一个Thread,而是Thread的局部变量,也许把它命名为ThreadLocalVariable更容易让人理解一些。

      当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。

      从线程的角度看,目标变量就像是线程的本地变量,这也是类名中“Local”所要表达的意思。

  2. 一个MessageQueue,用于存放消息,详情请看:MessageQueue源码解析
  3. 和这个looper相关的线程
  4. looper是否开启,注意他的关键字:volatile:Volatile修饰的成员变量在每次被线程访问时,都强迫从共享内存中重读该成员变量的值。而且,当成员变量发生变化时,强迫线程将变化值回写到共享内存。这样在任何时刻,两个不同的线程总是看到某个成员变量的同一个值。Java语言规范中指出:为了获得最佳速度,允许线程保存共享成员变量的私有拷贝,而且只当线程进入或者离开同步代码块时才与共享成员变量的原始值对比。这样当多个线程同时与某个对象交互时,就必须要注意到要让线程及时的得到共享成员变量的变化。而volatile关键字就是提示VM:对于这个成员变量不能保存它的私有拷贝,而应直接与共享成员变量交互。使用建议:在两个或者更多的线程访问的成员变量上使用volatile。当要访问的变量已在synchronized代码块中,或者为常量时,不必使用。由于使用volatile屏蔽掉了VM中必要的代码优化,所以在效率上比较低,因此一定在必要时才使用此关键字。
  5. 一个printer类型的mLogging。看下printer:一个用于打印信息的简单接口。
    Simple interface for printing text, allowing redirection to various targets. Standard implementations are android.util.LogPrinter, android.util.StringBuilderPrinter, and android.util.PrintWriterPrinter.
    

  6. 主线程(UI线程)looper。(在UI线程中系统会自动创建looper,其原理会在下面代码中进行解析)         

看完变量,再看构造方法:

private Looper() {
        mQueue = new MessageQueue();
        mRun = true;
        mThread = Thread.currentThread();
    }

初始化变量。                                       


     /** 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());
    }

        看一下prepare类,首先它是一个静态类。为当前线程初始化一个looper,在正式启动这个loop的之前,你有机会创建一个handler和这个looper关联。在调用loop方法之前,一定要确认调用prepare方法,在最后结束的时候调用quit方法。如果这个线程已经设过了looper的话就会报错这说明,一个线程只能设一个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.  See also: {@link #prepare()}
     */
    public static void prepareMainLooper() {
        prepare();
        setMainLooper(myLooper());
        myLooper().mQueue.mQuitAllowed = false;
    }

接下来是prepareMainLooper方法,为应用UI线程初始化一个looper,这个主looper是由android环境自动创建,所以不需要你调用这个方法。方法内部调用了prepare方法为线程初始化一个looper。最后那句代码是把queue的允许退出设为false。

然后调用setMainLooper这个方法为mMainLooper赋值

private synchronized static void setMainLooper(Looper looper) {
        mMainLooper = looper;
    }

而myLooper方法:

/**
     * Return the Looper object associated with the current thread.  Returns
     * null if the calling thread is not associated with a Looper.
     */
    public static Looper myLooper() {
        return sThreadLocal.get();
    }

就是为了获取前面prepare方法初始化的looper。这个方法返回与当前线程想关联的Looper实体,如果调用这个方法的线程没有与looper相关联,则返回空。

下面一个方法是获取MainLooper:

 /** Returns the application's main looper, which lives in the main thread of the application.
     */
    public synchronized static Looper getMainLooper() {
        return mMainLooper;
    }

返回在主线程里存在的主looper。

下面是本篇代码之重点:loop方法:

/**
     * Run the message queue in this thread. Be sure to call
     * {@link #quit()} to end the loop.
     */
    public static void loop() {
        Looper me = myLooper();//获取当前looper
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }//如果为空,则抛异常
        MessageQueue queue = me.mQueue;//把当前looper的queue赋值给局部变量queue

        // 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();//确保当前线程属于当前进程,并且记录真实的token。
        final long ident = Binder.clearCallingIdentity();

        while (true) {
            Message msg = queue.next(); // might block有可能会阻塞
            if (msg != null) {
                if (msg.target == null) {
                    // No target is a magic identifier for the quit message.退出消息的标示就是target为空
                    return;
                }

                long wallStart = 0;
                long threadStart = 0;

                // This must be in a local variable, in case a UI event sets the logger 一个局部变量,为ui事件设置log记录。
                Printer logging = me.mLogging;
                if (logging != null) {
                    logging.println(">>>>> Dispatching to " + msg.target + " " +
                            msg.callback + ": " + msg.what);
                    wallStart = SystemClock.currentTimeMicro();
                    threadStart = SystemClock.currentThreadTimeMicro();
                }
                 //handler处理消息
                msg.target.dispatchMessage(msg);

                if (logging != null) {
                    long wallTime = SystemClock.currentTimeMicro() - wallStart;
                    long threadTime = SystemClock.currentThreadTimeMicro() - threadStart;

                    logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
                    if (logging instanceof Profiler) {
                        ((Profiler) logging).profile(msg, wallStart, wallTime,
                                threadStart, threadTime);
                    }
                }

                // 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);
                }
                //处理完成后,调用Message.recycle()将其放入Message Pool中。 
                msg.recycle();
            }
        }
    }

这个方法,主要讲解在注释中。

/**
     * Control logging of messages as they are processed by this Looper.  If
     * enabled, a log message will be written to <var>printer</var>
     * at the beginning and ending of each message dispatch, identifying the
     * target Handler and message contents.
     *
     * @param printer A Printer object that will receive log messages, or
     * null to disable message logging.
     */
    public void setMessageLogging(Printer printer) {
        mLogging = printer;
    }

设置使用looper处理消息时的log,如果启用,每条消息分派开始和结束的日志消息将被写入到Printer类,以确定目标Handler和消息内容。

 /**
     * Return the {@link MessageQueue} object associated with the current
     * thread.  This must be called from a thread running a Looper, or a
     * NullPointerException will be thrown.
     */
    public static MessageQueue myQueue() {
        return myLooper().mQueue;
    }

返回与当前线程相关联的MessageQueue。当Looper运行时,该方法一定会被调用。

    public void quit() {
        Message msg = Message.obtain();
        // NOTE: By enqueueing directly into the message queue, the
        // message is left with a null target.  This is how we know it is
        // a quit message.
        mQueue.enqueueMessage(msg, 0);
    }

退出方法,往消息队列中加一个空msg。这就是约定的退出消息,在处理该消息时,looper就会退出。

返回当前线程和返回当前Messagequeue:

    /**
     * Return the Thread associated with this Looper.
     */
    public Thread getThread() {
        return mThread;
    }

    /** @hide */
    public MessageQueue getQueue() {
        return mQueue;
    }

在往后就是打印打印,异常定义的dump方法:

public void dump(Printer pw, String prefix) {
        pw = PrefixPrinter.create(pw, prefix);
        pw.println(this.toString());
        pw.println("mRun=" + mRun);
        pw.println("mThread=" + mThread);
        pw.println("mQueue=" + ((mQueue != null) ? mQueue : "(null"));
        if (mQueue != null) {
            synchronized (mQueue) {
                long now = SystemClock.uptimeMillis();
                Message msg = mQueue.mMessages;
                int n = 0;
                while (msg != null) {
                    pw.println("  Message " + n + ": " + msg.toString(now));
                    n++;
                    msg = msg.next;
                }
                pw.println("(Total messages: " + n + ")");
            }
        }
    }

最后面还有一个有关log的接口:

/**
     * @hide
     */
    public static interface Profiler {
        void profile(Message message, long wallStart, long wallTime,
                long threadStart, long threadTime);
    }

好,结束,可以做饭,然后重温最爱的《肖申克的救赎》了。

声明: 本文由( 张飞不张,文采横飞 )原创编译,转载请保留链接: Looper源码解析

Looper源码解析:等您坐沙发呢!

发表评论


QQ群互动

Linux系统与内核学习群:194051772

WP建站技术学习交流群:194062106

魔豆之路QR

魔豆的Linux内核之路

魔豆的Linux内核之路

优秀工程师当看优秀书籍

优秀程序员,要看优秀书!

赞助商广告

友荐云推荐