Chinaunix首页 | 论坛 | 博客
  • 博客访问: 543146
  • 博文数量: 119
  • 博客积分: 3167
  • 博客等级: 中校
  • 技术积分: 1215
  • 用 户 组: 普通用户
  • 注册时间: 2005-12-20 21:21
文章分类

全部博文(119)

文章存档

2015年(21)

2012年(4)

2011年(1)

2007年(11)

2006年(50)

2005年(32)

分类: Android平台

2015-10-18 00:10:13

Looper 类, 参考os.Looper.java

ThreadLocal 相当于是一个令牌

使用Looper首先调用的是prepare方法而不是直接调用其构造函数


点击(此处)折叠或打开

  1. private static void prepare(boolean quitAllowed) {
  2.         if (sThreadLocal.get() != null) {
  3.             throw new RuntimeException("Only one Looper may be created per thread");
  4.         }
  5.         sThreadLocal.set(new Looper(quitAllowed));
  6.     }
prepare 方法里首先判断令牌ThreadLocal是否已经存在(非空), 那说明当前线程已经存在一个对应的Looper对象


如果令牌不存在, 那么首先创建当前线程的Looper对象, 并设置ThreadLocal这个令牌为非空;
而调用的Looper对象的构造函数,  new Looper(quitAllowed) 其实就是生成一个消息队列对象mQueue , 并关联当前线程mThread
也就是说, 运行了Looper的prepare方法后, 当前线程就会关联一个MessageQueue消息队列


点击(此处)折叠或打开

  1. private Looper(boolean quitAllowed) {
  2.         mQueue = new MessageQueue(quitAllowed);
  3.         mThread = Thread.currentThread();
  4.     }

ThreadLocal 的令牌功能的实现,  static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
首先ThreadLocal是静态的, 和类相关的有且只有一个,
ThreadLocal<Looper> 表示存储Looper对象
然后通过get() set(obj) 方法判断是否已经存在Looper对象


Handler 参考os.Handler.java
handler 构造函数调用 public Handler(Callback callback, boolean async) 内主要代码


点击(此处)折叠或打开

  1. mLooper = Looper.myLooper();
  2.         if (mLooper == null) {
  3.             throw new RuntimeException(
  4.                 "Can't create handler inside thread that has not called Looper.prepare()");
  5.         }
  6.         mQueue = mLooper.mQueue;
  7.         mCallback = callback;
  8.         mAsynchronous = async;
先看看myLooper() , 调用后返回的就是当前线程对应的Looper对象, 这个Looper对象创建时生成的mQueue作为当前线程(生成handler对象)的消息队列

点击(此处)折叠或打开

  1. public static Looper myLooper() {
  2.         return sThreadLocal.get();
  3.     }

于是Handler, Looper 都对应起来了, 只要是生成了Handler对象, 就会有对应的Looper对象, 并且这个Looper对象有唯一的消息队列


默认情况下worker线程是没有Looper对象的, 当然也就没有消息队列了, 于是worker线程如果要处理消息的话, 就必须先创建looper对象


点击(此处)折叠或打开

  1. * Class used to run a message loop for a thread. Threads by default do
  2.   * not have a message loop associated with them; to create one, call
  3.   * {@link #prepare} in the thread that is to run the loop, and then
  4.   * {@link #loop} to have it process messages until the loop is stopped.
  5.   *
  6.   * <p>Most interaction with a message loop is through the
  7.   * {@link Handler} class.
  8.   *
  9.   * <p>This is a typical example of the implementation of a Looper thread,
  10.   * using the separation of {@link #prepare} and {@link #loop} to create an
  11.   * initial Handler to communicate with the Looper.
  12.   *
  13.   * <pre>
  14.   * class LooperThread extends Thread {
  15.   * public Handler mHandler;
  16.   *
  17.   * public void run() {
  18.   * Looper.prepare();
  19.   *
  20.   * mHandler = new Handler() {
  21.   * public void handleMessage(Message msg) {
  22.   * // process incoming messages here
  23.   * }
  24.   * };
  25.   *
  26.   * Looper.loop();
  27.   * }
  28.   * }</pre>

   //另外需要注意的是 Activity的MainUI线程默认是有消息队列的。所以在Activity中新建Handler时,不需要先调用Looper.prepare() 也不需要调用Looper.loop()。
Looper 对象的loop函数主要代码:


点击(此处)折叠或打开

  1. public static void loop() {
  2.         final Looper me = myLooper();
  3.         if (me == null) {
  4.             throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
  5.         }
  6.         final MessageQueue queue = me.mQueue;

  7.         // Make sure the identity of this thread is that of the local process,
  8.         // and keep track of what that identity token actually is.
  9.         Binder.clearCallingIdentity();
  10.         final long ident = Binder.clearCallingIdentity();

  11.         for (;;) {
  12.             Message msg = queue.next(); // might block
  13.             if (msg == null) {
  14.                 // No message indicates that the message queue is quitting.
  15.                 return;
  16.             }

  17.             // This must be in a local variable, in case a UI event sets the logger
  18.             Printer logging = me.mLogging;
  19.             if (logging != null) {
  20.                 logging.println(">>>>> Dispatching to " + msg.target + " " +
  21.                         msg.callback + ": " + msg.what);
  22.             }

  23.             msg.target.dispatchMessage(msg);

先通过myLooper得到当前线程的looper对象从而获得消息队列
然后一个for无限循环, 从队列里取出next msg , queue.next() 实际是从消息池里取出消息, 而且可能阻塞 . 
如果当前没有msg则退出, 正常获取消息后进行了一些其他操作
然后执行msg.target.dispatchMessage(msg);

那么target是什么呢?我们查看

Message.java 中的定义


点击(此处)折叠或打开

  1. /*package*/ Handler target;
可见target就是Handler, 那么Handler和msg的target是怎么联系起来的呢?
Message msg = handler.obtainMessage();

//Handler类obtainMessage 方法如下, 它间接帮我们调用Message的构造函数(通过Message.obtain()方法), 并且传递了参数this对象

我们生成消息的时候不是直接调用Message的构造函数而是通过

Message msg = handler.obtainMessage();

//Handler类obtainMessage 方法如下, 它间接帮我们调用Message的构造函数(通过Message.obtain()方法), 并且传递了参数this对象


点击(此处)折叠或打开

  1. public final Message obtainMessage()
  2.     {
  3.         return Message.obtain(this);
  4.     }




  5. //Message类 obtain(Handler h)


  6.     public static Message obtain(Handler h) {
  7.         Message m = obtain();
  8.         m.target = h;

  9.         return m;
  10.     }
// 可见handler的obtainMessage 在调用Message.obtain(this);方法时, 把this对象,也就是本身handler对象传递给了msg
// 于是msg对象的target赋值为handler, 也就是生成msg的这个handler对象
// handler 可以生成多个msg , 一个handler可以对应多个msg




// 消息池中查看是否有msg没有就生成新的msg


点击(此处)折叠或打开

  1. public static Message obtain() {
  2.         synchronized (sPoolSync) {
  3.             if (sPool != null) {
  4.                 Message m = sPool;
  5.                 sPool = m.next;
  6.                 m.next = null;
  7.                 sPoolSize--;
  8.                 return m;
  9.             }
  10.         }
  11.         return new Message();
  12.     }

点击(此处)折叠或打开

  1. msg和handler建立了对应关系后, 我们继续看looper对象的loop方法里面 msg.target.dispatchMessage(msg);
  2. 也就是handler的dispatchMessage方法

  3.     public void dispatchMessage(Message msg) {
  4.         if (msg.callback != null) {
  5.             handleCallback(msg);
  6.         } else {
  7.             if (mCallback != null) {
  8.                 if (mCallback.handleMessage(msg)) {
  9.                     return;
  10.                 }
  11.             }
  12.             handleMessage(msg);
  13.         }
  14.     }

首先判断msg是否有回调函数, 有的话就执行msg的回调函数
没有msg回调函数的话, 就判断是否有handler的回调函数, 有就执行
msg,handler 的回调函数都没有的话, 就执行handleMessage方法 
所以我们生成handler类时, 要求  Override handleMessage方法, 这样就保证了msg一定被处理




纵观整个的过程, 为了实现worker线程的消息循环,


需要有消息队列, 消息循环, 消息处理


looper 和消息队列是直接关系, looper里面生成线程对应的唯一的消息队列对象


handler 和looper是直接关系, handler的构造函数里面直接赋值looper对象为一个成员变量,
handler获取到looper对象后, 也就获得了looper对象所生成的线程的消息队列, handler在哪创建, looper就在哪个线程?


message 和Handler是直接关系,  因为message是通过handler的 obtainMessage()方法得到的, msg的target成员变量就是生成msg的handler


于是流程如下 , 


1. looper 生成mQueue 也就是线程消息队列, 确保当前线程只有一个消息队列, 只有一个Looper对象


2. 创建handler , 对象生成时, 绑定已经生成的looper , 获取到looper的消息队列mQueue 


3. 有了上面两步就已经有了消息队列了, 紧接着就由handler创建msg, 并让msg绑定本身handler, 然后通过sendMessage把msg放入looper的消息队列, 


4. looper 执行消息循环loop() 取出mQueue里的消息, 并按msg的回调函数,handler的回调函数,handler类handleMessage方法的次序去处理消息








比如在主线程中创建了handler,   存在默认的looper, 不需要去调用prepare.


handler创建后就会自动绑定main线程的looper, 


然后我们启动了子线程, 在子线程中进行了一些操作得到数据等等, 这时就可以在子线程里调用handler.obtainMessage(), 来生成消息, 然后给消息赋值, 并sendMessage(msg)


handler在子线程里对消息的操作, 最终都是把消息放到了主线程的消息队列中, 因为handler是绑定了其所在的线程的looper,


然后主线程会自动去调用loop()方法取出消息, 并去执行主线程中handler定义的handleMessage方法


于是实现了子线程和主线程之间的通讯


//
// handler 的 post方法

线程的实现, 一个是继承Thread类 , 另外是实现Runnable接口,,


Thread类就直接代表一个线程 , 而实现Runnable接口 生成Runnable对象的话,


Runnable 对象不是线程, 是一个线程体, 不能单独直接运行,  只能传递给线程对象, 通过线程对象来启动运行 比如


Thread t = new Thread(r);
t.start();



点击(此处)折叠或打开

  1. //Hander.java post(Runnable r) 方法:


  2.     public final boolean post(Runnable r)
  3.     {
  4.        return sendMessageDelayed(getPostMessage(r), 0);
  5.     }


点击(此处)折叠或打开

  1. //Hander.java getPostMessage方法:

  2.     private static Message getPostMessage(Runnable r) {
  3.         Message m = Message.obtain();
  4.         m.callback = r;
  5.         return m;
  6.     }



  7. //Hander.java sendMessageDelayed 方法:
  8.     public final boolean sendMessageDelayed(Message msg, long delayMillis)
  9.     {
  10.         if (delayMillis < 0) {
  11.             delayMillis = 0;
  12.         }
  13.         return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
  14.     }


  15. //Hander.java

  16.     private static void handleCallback(Message message) {
  17.         message.callback.run();
  18.     }

post(Runnable r) 方法实际上, 首先是调用obtain生成一个message , 然后把给这个msg赋值回调函数 m.callback = r; 最后把带有Runnable对象的msg放入Looper的mQueue消息队列


为什么要这么做呢? 常规的手段我们是通过msg来让worker线程和main线程通讯, 那么就存在一个数据传递, 那么能不能在子线程里, 在一些数据处理完成后直接对main线程进行操作呢?


于是就可以通过这个带有Runnable对象的Message来实现,  把需要对main线程的操作放在runnable对象r里,  主线程里创建handler对象, 绑定主线程的looper, 然后在子线程里, 数据操作完成后,


通过handler的post(r)方法, 生成一个回调函数为r的msg, 并放到handler对应的消息队列中, 也就是主线程中, 然后main线程会调用loop() 并执行这个消息对象的回调函数




比如下面的这个例子, main线程里直接定义了一个handler变量, 然后通过按钮去启动一个TestThread , TestThread里面的run方法里面执行一些worker thread的数据操作后, 生成一个匿名Runnable对象
这个runnable对象最后通过handler.post(r)方法传递给消息队列,然后这个r对象虽然是在子线程里面创建的,  但是实际上这个r对象的run方法是在main线程里被执行, 因为消息都是被handler操作来操作去,
而这个handler是在main线程里创建的, handler绑定的消息队列是main的消息队列, 因此, 消息循环也就在main线程 并按msg的回调函数,handler的回调函数,handler类handleMessage方法的次序去处理消息
    



点击(此处)折叠或打开

  1. private Handler handler = new Handler();


  2.     class TestThread extends Thread {
  3.         @Override
  4.         public void run() {
  5.             
  6.             //子线程中一些数据操作
  7.             
  8.             Runnable r = new Runnable() {
  9.                 @Override
  10.                 public void run() {
  11.                     String currentThreadName = Thread.currentThread().getName();
  12.                     System.out.println("runnable 对象当前运行的线程名称是 ====>" + currentThreadName);
  13.                     //runnable对象通过handler的post方法赋值给了msg的callback
  14.                     //于是 runnable对象被放在主线程里通过msg的回调函数被执行 , 因此可以直接修改UI
  15.                     tv.setText("子线程已运行");
  16.                 }
  17.             };
  18.             handler.post(r);
  19.         }
  20.     }







阅读(1238) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~