Handler消息传递机制(子线程中传递new Handler和主线程中new Handle传递消息)

Android消息循环机制: http://blog.csdn.net/crazy1235/article/details/51707527

Android Message.obtain() 和Handler.obtainMessage()的区别 --http://www.cnblogs.com/ZhuRenWang/p/4937785.html

Android的消息机制-Message机制,handler机制--http://blog.csdn.net/woshiwxw765/article/details/24742725

Android开发-Handler引起的内存泄漏-http://m.blog.csdn.net/article/details?id=51493352
Android Handler 避免内存泄漏的用法总结-http://www.cnblogs.com/liyiran/p/5228771.html

> 子线程中更新UI

new Thread(new Runnable() {
        @Override
        public void run() {
            Looper.prepare();
            // Toast.makeText(MainActivity.this,"一口仨馍",Toast.LENGTH_SHORT).show();
            new AlertDialog.Builder(MainActivity.this)
                    .setTitle("Game of Thrones")
                    .setMessage("winter is coming")
                    .setPositiveButton("yes,my lord", null)
                    .show();
            Looper.loop();
        }
}).start();

-- 死磕Android_Handler机制你需要知道的一切- https://blog.csdn.net/xfhy_/article/details/90347636
 ThreadLocalMap的底部其实是一个数组,它是利用hash值来计算索引,然后存储数据到该索引处的方式.
 Handler中的消息队列,也就是MessageQueue.从名字可以看出这是一个队列,但是它的底层却是单链表结构.因为链表结构比较适合插入和删除操作.这个MessageQueue的查询就是next()方法,它的查询伴随着删除.而插入的时候是插入在链表的最后面.
 ThreadLocal主要是可以在不同的线程中存储不同的数据,它是将数据存储在线程内部的,其他线程无法访问.对于同一个ThreadLocal对象,不同的线程有不同的数据,这些数据互不干扰.比如Handler机制中的Looper,Looper的作用域是线程,ThreadLocal可以将Looper存储在线程中,然后其他线程是无法访问到这个线程中的Looper的,只供当前线程自己内部使用.
 ThreadLocal在不同的线程中存储的数据,互不干扰,相互独立.
 真正导致ANR的是因为在handleMessage方法中处理消息时阻塞了主线程太久的时间.
 Looper在消息机制中主要扮演着消息循环的角色,有消息来了,Looper就取出来,分发.没有消息,Looper就阻塞在那里,直到有消息为止.

-- 安卓的消息机制
  在安卓消息机制中,ThreadLocal拿来存储Looper,而MessageQueue是存储在Looper中的.所以我们可以在子线程中通过主线程的Handler发送消息,而Looper(主线程中的)在主线程中取出消息,分发给主线程的Handler的handleMessage方法.
 

 Native MessageQueue使用名为epoll的Linux系统调用,该调用允许监视IO事件的文件描述符。 nativePollOnce在某个文件描述符上调用epoll_wait,而nativeWake写入描述符,这是IO操作之一,epoll_wait等待。然后内核从等待状态中取出epoll等待线程,并且线程继续处理新消息。如果您熟悉Java的Object.wait()和Object.notify()方法,您可以想象nativePollOnce是Object.wait()和NativeWake for Object.notify()的粗略等价物,因为它们的实现完全不同:nativePollOnce使用epoll,Object.wait()使用futex Linux调用。值得注意的是,nativePollOnce和Object.wait()都不会浪费CPU周期,因为当线程进入任一方法时,它会因线程调度而被禁用。如果这些方法实际上浪费了CPU周期,那么所有空闲应用程序将使用100%的CPU,加热并降低设备的速度。

> 那么什么情况下面我们的子线程才能看做是一个有Looper的线程呢?我们如何得到它Looper的句柄呢?
Looper.myLooper();获得当前的Looper
Looper.getMainLooper () 获得UI线程的Lopper
如 果一个线程中调用Looper.prepare(),那么系统就会自动的为该线程建立一个消息队列,然后调用 Looper.loop();

> Android异步消息机制中主要涉及到:Thread、Handler、MessageQueue、Looper,在整个机制中它们扮演着不同的角色也承担着各自的不同责任。
深入探讨Android异步精髓Handler - http://blog.csdn.net/lfdfhl/article/details/53332936
    Google官方建议开发人员使用Handler实现异步刷新UI,我们在平常的工作中也很好地采纳了这个提议:首先在主线程中建立Handler,然后在子线程中利用
handler.sendMessage(message)发送消息至主线程,最终消息在handleMessage(Message msg) {}得到相应的处理。

> HandlerThread 继承自Thread,内部封装了Looper。
  首先Handler和HandlerThread的主要区别是:Handler与Activity在同一个线程中,HandlerThread与Activity不在同一个线程,而是别外新的线程中(Handler中不能做耗时的操作)。
import android.app.Activity;  
import android.os.Bundle;  
import android.os.Handler;  
import android.os.Handler.Callback;  
import android.os.HandlerThread;  
import android.os.Message;  
public class MainActivity extends Activity {  
    HandlerThread handlerThread = new HandlerThread("test");  
    Handler handler;    
    @Override  
    protected void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.activity_main);  
       
System.out.println("cur_id="+Thread.currentThread().getId());   
        handlerThread.start();  
        System.out.println("handlerThread.id=" + handlerThread.getId());  
        //post(runnable),只是直接运行了run(),run()内ThreadId与UIThread是一样的  
//      handler = new Handler();   
        //post(runnable),将runnable运行在handlerThread中,这是非UIThread的  
        handler = new Handler(handlerThread.getLooper(), new Callback() {         
            @Override  
            public boolean handleMessage(Message msg) {  
                System.out.println("receive message.whatA=" + msg.what);  
                if (msg.what == 1) {  
                    return true;//不再向外层传递  
                } else {  
                    return false; //外层的handleMessage() 继续执行  
                }   
            }  
        }) {  
            @Override  
            public void handleMessage(Message msg) {  
                // TODO Auto-generated method stub  
                super.handleMessage(msg);  
                System.out.println("receive message.whatB=" + msg.what);  
            }  
        };  
        handler.post(new Runnable() {  
            @Override  
            public void run() {  
                System.out.println("handler_post_cur_id="+Thread.currentThread().getId());  
                handler.sendEmptyMessage(1);  
                handler.sendEmptyMessage(2);  
            }  
        });  
          
    }  
  
}  
  运行结果:
01-23 07:12:31.590: I/System.out(12386): cur_id=1  
01-23 07:12:31.590: I/System.out(12386): handlerThread.id=1866  
01-23 07:12:31.590: I/System.out(12386): handler_post_cur_id=1866  
01-23 07:12:31.600: I/System.out(12386): receive message.whatA=1  
01-23 07:12:31.600: I/System.out(12386): receive message.whatA=2  
01-23 07:12:31.600: I/System.out(12386): receive message.whatB=2 

》》》在android中,Activity,Service属于主线程,在主线程中才能更新UI,如toast等。其他线程中不能直接使用更新UI,这时可以使用Handler来处理,Handler可以在Activity和Service中定义。

    熟悉Windows编程的朋友可能知道Windows程序是消息驱动的,并且有全局的消息循环系统。而Android应用程序也是消息驱动的,按道理来说也应该提供消息循环机制。实际上谷歌参考了Windows的消息循环机制,也在Android系统中实现了消息循环机制。Android通过 Looper、Handler来实现消息循环机制,Android消息循环是针对线程的(每个线程都可以有自己的消息队列和消息循环)。本文深入介绍一下 Android消息处理系统原理。

    Android系统中Looper负责管理线程的消息队列和消息循环,具体实现请参考上面的Looper的源码。 可以通过Loop.myLooper()得到当前线程的Looper对象(这是我们一再强调的重点),通过调用Loop.getMainLooper()可以获得当前进程的主线程的Looper对象。

    前面提到Android系统的消息队列和消息循环都是针对具体线程的,一个线程可以存在(当然也可以不存在)一个消息队列和一个消息循环(Looper)。特定线程的消息只能分发给本线程,不能进行跨线程,跨进程通讯。但是创建的工作线程默认是没有消息循环和消息队列的,如果想让该线程具有消息队列和消息循环功能,需要在线程中首先调用Looper.prepare()来创建消息队列,然后调用Looper.loop()进入消息循环。

    如下例 所示:

class LooperThread extends Thread {

    public HandlermHandler;

    public void run() {

        Looper.prepare();

        mHandler = newHandler(Looper.myLooper()) {

            public voidhandleMessage(Message msg) {

            // processincoming messages here

            }

        };

        Looper.loop();

    }

}

    这样你的线程就具有了消息处理机制了,在Handler中进行消息处理。

    Activity是一个UI线程,运行于主线程中,Android系统在启动的时候会为Activity创建一个消息队列和消息循环(Looper)。详细实现请参考ActivityThread.java文件。 Handler的作用是把消息加入特定的(Looper)消息队列中,并分发和处理该消息队列中的消息。构造Handler的时候可以指定一个Looper对象,如果不指定则利用当前线程的Looper创建。

    一个Activity中可以创建多个工作线程或者其他的组件,如果这些线程或者组件把他们的消息放入Activity的主线程消息队列,那么该消息就会在主线程中处理了。因为主线程一般负责界面的更新操作,并且Android系统中的weget不是线程安全的,所以这种方式可以很好的实现 Android界面更新。在Android系统中这种方式有着广泛的运用。

    那么另外一个线程怎样把消息放入主线程的消息队列呢?答案是通过Handle对象,只要Handler对象以主线程中创建,并用主线程的的Looper创建,那么调用 Handler的sendMessage等接口,将会把消息放入队列都将是放入主线程的消息队列(这是handler与looper共用一个MessageQueue的结果)。并且将会在Handler主线程中调用该handler 的handleMessage接口来处理消息。

    这里面涉及到线程同步问题,请先参考如下例子来理解Handler对象的线程模型:

(1)首先创建MyHandler工程。

(2)在MyHandler.java中加入如下的代码:

package com.simon;

import android.app.Activity;

import android.os.Bundle;

import android.os.Message;

import android.util.Log;

import android.os.Handler;

public class MyHandler extends Activity {

    static final StringTAG = "Handler";

    Handler h = newHandler(){ //创建处理对象,并定义handleMessage()函数

        public voidhandleMessage (Message msg) 
        { 
            switch(msg.what) 
            { 
                case HANDLER_TEST: 
                Log.d(TAG, "Thehandler thread id = " + Thread.currentThread().getId() + "\n"); 
                break; 
            } 
        } 
    };

static final int HANDLER_TEST = 1; 
/** Called when the activity is first created. */ 
@Override 
public void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
    Log.d(TAG, "The main thread id =" + Thread.currentThread().getId() + "\n");

    newmyThread().start(); //创建子线程对象,并启动子线程

    setContentView(R.layout.main);
}

class myThread extends Thread

{

//这个线程计较简单,Run()函数只是调用了在主线程中生成的处理类对象的一个发送消息的函数,没有单独生成looper进入循环

    public void run() 
    { 
        Message msg = new Message();

        msg.what =HANDLER_TEST; 
        h.sendMessage(msg); 
        Log.d(TAG, "The worker threadid = " + Thread.currentThread().getId() + "\n"); 
    } 

}

    在这个例子中我们主要是打印,这种处理机制各个模块的所处的线程情况。如下是机器运行结果:

    DEBUG/Handler(302):The main thread id = 1 
    DEBUG/Handler(302): The worker threadid = 8 
    DEBUG/Handler(302): The handler threadid = 1

    我们可以看出消息处理是在主线程中处理的,在消息处理函数中可以安全的调用主线程中的任何资源,包括刷新界面。工作线程和主线程运行在不同的线程中,所以必须要注意这两个线程间的竞争关系。

    上例中,你可能注意到在工作线程中访问了主线程handler对象,并在调用handler的对象向消息队列加入了一个消息。这个过程中会不会出现消息队列数据不一致问题呢?答案是handler对象不会出问题,因为handler对象管理的Looper对象是线程安全的,不管是加入消息到消息队列 和从队列读出消息都是有同步对象保护的。上例中没有修改handler对象,所以handler对象不可能会出现数据不一致的问题。

    通过上面的分析,我们可以得出如下结论:

    1、如果通过工作线程刷新界面,推荐使用handler对象来实现。

    2、注意工作线程和主线程之间的竞争关系。推荐handler对象在主线程中构造完成(并且启动工作线程之后不要再修改之,否则会出现数据不一致),然后在工作线程中可以放心的调用发送消息SendMessage等接口。

    3、除了2所述的hanlder对象之外的任何主线程的成员变量如果在工作线程中调用,仔细考虑线程同步问题。如果有必要需要加入同步对象保护该变量。

    4、handler对象的handleMessage接口将会在主线程中调用。在这个函数可以放心的调用主线程中任何变量和函数,进而完成更新UI的任务。

    5、Android很多API也利用Handler这种线程特性,作为一种回调函数的变种,来通知调用者。这样Android框架就可以在其线程中将消息发送到调用者的线程消息队列之中,不用担心线程同步的问题。

    6. 下面再从线程的角度看一下,消息处理过程中参与的线程,以及这些线程之间的同步。显然的,这里有线程HandlerThread的参 与,而且Looper::loop()就是执行在HandlerThread的run()方法里,也就是在HandlerThread里执行,这也就是说 消息的分发处理和执行是在HandlerThread的线程上下文中。另外,还有至少一个线程存在,也就是创建了HandlerThread的线程B,以 及执行消息发送的线程C,B和C有可能是同一线程。

    消息的发送是在另外一个线程里,就是因为有了多个线程的存在,才有了线程的同步操作。可再次关注一下实现线程同步的Java原语wait()/notify()。

 

》1.在子线程中new Handler(),需使用Looper.prepare()  , Looper.loop()。

 

public class LoopThread implements Runnable {  
    public Handler mHandler = null;  
    @Override  
    public void run() {  
        Looper.prepare();  
        mHandler = new Handler() {     //在Handler线程中 ,多个Handler都可以共享同一Looper和MessageQueue了
            public void handleMessage(Message msg) {  
                String result = msg ;  
                //完成了获取北京天气的操作;  
                Log.i("test", "handler"+result);  
            }  
        };  
        Looper.loop();  
    }  

如果线程中使用Looper.prepare()和Looper.loop()创建了消息队列就可以让消息处理在该线程中完成。

---》子线程中不能更新UI,Toast是不算UI更新的,但是会报错Only the original thread that created a view hierarchy can touch its views.

Android中的Looper类,是用来封装消息循环和消息队列的一个类,用于在android线程中进行消息处理。handler其实可以看做是一个工具类,用来向消息队列中插入消息的。

 

    (1) Looper类用来为一个线程开启一个消息循环。     默认情况下android中新诞生的线程是没有开启消息循环的。(主线程除外,主线程系统会自动为其创建Looper对象,开启消息循环。)     Looper对象通过MessageQueue来存放消息和事件。一个线程只能有一个Looper,对应一个MessageQueue。 

 

(2) 通常是通过Handler对象来与Looper进行交互的。Handler可看做是Looper的一个接口,用来向指定的Looper发送消息及定义处理方法。     默认情况下Handler会与其被定义时所在线程的Looper绑定,比如,Handler在主线程中定义,那么它是与主线程的Looper绑定。 mainHandler = new Handler() 等价于new Handler(Looper.myLooper()). Looper.myLooper():获取当前进程的looper对象,类似的 Looper.getMainLooper() 用于获取主线程的Looper对象。 

 

(3) 在非主线程中直接new Handler() 会报如下的错误: E/AndroidRuntime( 6173): Uncaught handler: thread Thread-8 exiting due to uncaught exception E/AndroidRuntime( 6173): java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare() 原因是非主线程中默认没有创建Looper对象,需要先调用Looper.prepare()启用Looper。 

 

(4) Looper.loop(); 让Looper开始工作,从消息队列里取消息,处理消息。 

 

 

    注意:写在Looper.loop()之后的代码不会被执行,这个函数内部应该是一个循环,当调用mHandler.getLooper().quit()后,loop才会中止,其后的代码才能得以运行。
(5) 基于以上知识,可实现主线程给子线程(非主线程)发送消息。 
    把下面例子中的mHandler声明成类成员,在主线程通过mHandler发送消息即可。         Android官方文档中Looper的介绍: 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.

 

 >>> android HandlerThread使用小例

之前研究过handler 和 looper 消息队列,不过android里的handler不是另外开启线程来执行的,还是在主UI线程中,如果想另启线程的话需要用到HandlerThread来实现。在使用HandlerThread的时候需要实现CallBack接口以重写handlerMessage方法,在handlerMessage方法中来处理自己的逻辑。下来给出一个小例子程序。

layout文件很简单,就一个按钮来启动HanlderTread线程

[html] view plain copy

  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     android:layout_width="fill_parent"  
  4.     android:layout_height="fill_parent"  
  5.     android:orientation="vertical" >  
  6.   
  7.     <TextView  
  8.         android:layout_width="fill_parent"  
  9.         android:layout_height="wrap_content"  
  10.         android:text="@string/hello" />  
  11.   
  12.     <Button  
  13.         android:id="@+id/handlerThreadBtn"  
  14.         android:layout_width="wrap_content"  
  15.         android:layout_height="wrap_content"  
  16.         android:text="startHandlerThread" />  
  17.   
  18. </LinearLayout>  

Activity代码如下:

 

[java] view plain copy

  1. package com.tayue;  
  2.   
  3. import android.app.Activity;  
  4. import android.os.Bundle;  
  5. import android.os.Handler;  
  6. import android.os.Handler.Callback;  
  7. import android.os.HandlerThread;  
  8. import android.os.Message;  
  9. import android.view.View;  
  10. import android.view.View.OnClickListener;  
  11. import android.widget.Button;  
  12. /** 
  13.  *  
  14.  * @author xionglei 
  15.  * 
  16.  */  
  17. public class TestHandlerActivity extends Activity implements OnClickListener{  
  18.       
  19.     public Button handlerThreadBTN;   
  20.     MyHandlerThread handlerThread;  
  21.     Handler handler;  
  22.       
  23.     /** Called when the activity is first created. */  
  24.     @Override  
  25.     public void onCreate(Bundle savedInstanceState) {  
  26.         super.onCreate(savedInstanceState);  
  27.         //打印UI线程的名称  
  28.         System.out.println("onCreate  CurrentThread = " + Thread.currentThread().getName());  
  29.           
  30.         setContentView(R.layout.main);  
  31.           
  32.         handlerThreadBTN = (Button) findViewById(R.id.handlerThreadBtn);  
  33.         handlerThreadBTN.setOnClickListener(this);  
  34.           
  35.         handlerThread = new MyHandlerThread("myHanler");  
  36.         handlerThread.start();  
  37.         //注意: 这里必须用到handler的这个构造器,因为需要把callback传进去,从而使自己的HandlerThread的handlerMessage来替换掉Handler原生的handlerThread  
  38.         handler = new Handler(handlerThread.getLooper(), handlerThread);         
  39.     }  
  40.   
  41.     @Override  
  42.     public void onClick(View v) {  
  43.         //点击按钮后来开启线程  
  44.         handler.sendEmptyMessage(1);  
  45.     }      
  46.       
  47.     private class MyHandlerThread extends HandlerThread implements Callback {  
  48.   
  49.         public MyHandlerThread(String name) {  
  50.             super(name);  
  51.         }  
  52.   
  53.         @Override  
  54.         public boolean handleMessage(Message msg) {  
  55.             //打印线程的名称  
  56.             System.out.println(" handleMessage CurrentThread = " + Thread.currentThread().getName());  
  57.             return true;  
  58.         }              
  59.     }           
  60. }  

点击按钮,打印的日志如下(这里点击了3次) 

07-06 09:32:48.776: I/System.out(780): onCreate  CurrentThread = main ,在主线程中

07-06 09:32:55.076: I/System.out(780):  handleMessage CurrentThread = myHanler ,在Handler线程中

07-06 09:32:58.669: I/System.out(780):  handleMessage CurrentThread = myHanler 

07-06 09:33:03.476: I/System.out(780):  handleMessage CurrentThread = myHanler

HandlerThread就这么简单。

>>>> 当然 android自己也有异步线程的handler,就是AsyncTask,这个类就是封装了HandlerThread 和handler来实现异步多线程的操作的。

同样可以这样使用:

[java] view plain copy

  1. private boolean iscancel = false//用户手动取消登录的标志位  
  2.   
  3.     handlerThread = new HandlerThread("myHandlerThread");  
  4.                     handlerThread.start();  
  5.                     handler = new MyHandler(handlerThread.getLooper());  
  6.                 // 将要执行的线程对象添加到线程队列中  
  7.                         handler.post(new Runnable() {  
  8.                             @Override  
  9.                             public void run() {  
  10.                                 Message message = handler.obtainMessage();  
  11.                                 UserBean user = Bbs.getInstance().Login(username, password);//耗时任务  
  12.                                 Bundle b = new Bundle();  
  13.                                 b.putSerializable("user", user);  
  14.                                 message.setData(b);  
  15.                                 message.sendToTarget(); //或使用 handler.sendMessage(message);  
  16.                             }  
  17.                         });  
  18.     class MyHandler extends Handler {  
  19.   
  20.             public MyHandler(Looper looper) {  
  21.                 super(looper);  
  22.             }  
  23.   
  24.             @Override  
  25.             public void handleMessage(Message msg) {  
  26.                 if(iscancel == false){  
  27.                     // 操作UI线程的代码  
  28.                     Bundle b = msg.getData();  
  29.                     UserBean user = (UserBean)b.get("user");  
  30.                                      ......  
  31.                }  
  32.            }  
  33.     }    

》2. Looper,Handler,Message这几个类的源码

 

Looper的字面意思是“循环者”,它被设计用来使一个普通线程变成Looper线程。所谓Looper线程就是循环工作的线程。在程序开发中(尤其是GUI开发中),我们经常会需要一个线程不断循环,一旦有新任务则执行,执行完继续等待下一个任务,这就是Looper线程。使用Looper类创建Looper线程很简单:

复制代码

public class LooperThread extends Thread {
    @Override
    public void run() {
        // 将当前线程初始化为Looper线程
        Looper.prepare();
        
        // ...其他处理,如实例化handler
        
        // 开始循环处理消息队列
        Looper.loop();
    }
}

复制代码

通过上面两行核心代码,你的线程就升级为Looper线程了!!!

1)Looper.prepare()

 

 

 

现在你的线程中有一个Looper对象,它的内部维护了一个消息队列MQ。注意,一个Thread只能有一个Looper对象,为什么呢?咱们来看源码。

复制代码

public class Looper {
    // 每个线程中的Looper对象其实是一个ThreadLocal,即线程本地存储(TLS)对象
    private static final ThreadLocal sThreadLocal = new ThreadLocal();
    // Looper内的消息队列
    final MessageQueue mQueue;
    // 当前线程
    Thread mThread;
    // 。。。其他属性

    // 每个Looper对象中有它的消息队列,和它所属的线程
    private Looper() {
        mQueue = new MessageQueue();
        mRun = true;
        mThread = Thread.currentThread();
    }

    // 我们调用该方法会在调用线程的TLS中创建Looper对象
    public static final void prepare() {
        if (sThreadLocal.get() != null) {
            // 试图在有Looper的线程中再次创建Looper将抛出异常
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper());
    }
    // 其他方法
}

复制代码

通过源码,prepare()背后的工作方式一目了然,其核心就是将looper对象定义为ThreadLocal。

 

2)Looper.loop()

 

 

调用loop方法后,Looper线程就开始真正工作了,它不断从自己的MQ中取出队头的消息(也叫任务)执行。其源码分析如下:

复制代码

  public static final void loop() {
        Looper me = myLooper();  //得到当前线程Looper
        MessageQueue queue = me.mQueue;  //得到当前looper的MQ
        
        // 这两行没看懂= = 不过不影响理解
        Binder.clearCallingIdentity();
        final long ident = Binder.clearCallingIdentity();
        // 开始循环
        while (true) {
            Message msg = queue.next(); // 取出message
            if (msg != null) {
                if (msg.target == null) {
                    // message没有target为结束信号,退出循环
                    return;
                }
                // 日志。。。
                if (me.mLogging!= null) me.mLogging.println(
                        ">>>>> Dispatching to " + msg.target + " "
                        + msg.callback + ": " + msg.what
                        );
                // 非常重要!将真正的处理工作交给message的target,即后面要讲的handler
                msg.target.dispatchMessage(msg);
                // 还是日志。。。
                if (me.mLogging!= null) me.mLogging.println(
                        "<<<<< Finished to    " + msg.target + " "
                        + msg.callback);
                
                // 下面没看懂,同样不影响理解
                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);
                }
                // 回收message资源
                msg.recycle();
            }
        }
    }

复制代码

除了prepare()和loop()方法,Looper类还提供了一些有用的方法,比如

Looper.myLooper()得到当前线程looper对象:

public static final Looper myLooper() {
        // 在任意线程调用Looper.myLooper()返回的都是那个线程的looper
        return (Looper)sThreadLocal.get();
    }

getThread()得到looper对象所属线程:

    public Thread getThread() {
        return mThread;
    }

quit()方法结束looper循环:

    public void quit() {
        // 创建一个空的message,它的target为NULL,表示结束循环消息
        Message msg = Message.obtain();
        // 发出消息
        mQueue.enqueueMessage(msg, 0);
    }

到此为止,你应该对Looper有了基本的了解,总结几点:

1.每个线程有且最多只能有一个Looper对象,它是一个ThreadLocal

2.Looper内部有一个消息队列,loop()方法调用后线程开始不断从队列中取出消息执行

3.Looper使一个线程变成Looper线程。

异步处理大师 Handler

什么是handler?handler扮演了往MQ上添加消息和处理消息的角色(只处理由自己发出的消息),即通知MQ它要执行一个任务(sendMessage),并在loop到自己的时候执行该任务(handleMessage),整个过程是异步的。handler创建时会关联一个looper,默认的构造方法将关联当前线程的looper,不过这也是可以set的。默认的构造方法:

复制代码

public class handler {

    final MessageQueue mQueue;  // 关联的MQ
    final Looper mLooper;  // 关联的looper
    final Callback mCallback; 
    // 其他属性

    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());
            }
        }
        // 默认将关联当前线程的looper
        mLooper = Looper.myLooper();
        // looper不能为空,即该默认的构造方法只能在looper线程中使用
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
        // 重要!!!直接把关联looper的MQ作为自己的MQ,因此它的消息将发送到关联looper的MQ上
        mQueue = mLooper.mQueue;
        mCallback = null;
    }
    
    // 其他方法
}

复制代码

下面我们就可以为之前的LooperThread类加入Handler:

复制代码

public class LooperThread extends Thread {
    private Handler handler1;
    private Handler handler2;

    @Override
    public void run() {
        // 将当前线程初始化为Looper线程
        Looper.prepare();
        
        // 实例化两个handler
        handler1 = new Handler();
        handler2 = new Handler();
        
        // 开始循环处理消息队列
        Looper.loop();
    }
}

一个线程可以有多个Handler,但是只能有一个Looper!

Handler发送消息

有了handler之后,我们就可以使用 post(Runnable)postAtTime(Runnable, long)postDelayed(Runnable, long)sendEmptyMessage(int),sendMessage(Message)sendMessageAtTime(Message, long)和 sendMessageDelayed(Message, long)这些方法向MQ上发送消息了。光看这些API你可能会觉得handler能发两种消息,一种是Runnable对象,一种是message对象,这是直观的理解,但其实post发出的Runnable对象最后都被封装成message对象了,见源码:

 // 此方法用于向关联的MQ上发送Runnable对象,它的run方法将在handler关联的looper线程中执行
    public final boolean post(Runnable r)
    {
       // 注意getPostMessage(r)将runnable封装成message
       return  sendMessageDelayed(getPostMessage(r), 0);
    }

    private final Message getPostMessage(Runnable r) {
        Message m = Message.obtain();  //得到空的message
        m.callback = r;  //将runnable设为message的callback,
        return m;
    }

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

总之通过handler发出的message有如下特点:

1.message.target为该handler对象,这确保了looper执行到该message时能找到处理它的handler,即loop()方法中的关键代码

msg.target.dispatchMessage(msg);

2.post发出的message,其callback为Runnable对象

Handler处理消息

说完了消息的发送,再来看下handler如何处理消息。消息的处理是通过核心方法dispatchMessage(Message msg)与钩子方法handleMessage(Messagemsg)完成的,见源码

    // 处理消息,该方法由looper调用
    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            // 如果message设置了callback,即runnable消息,处理callback!
            handleCallback(msg);
        } else {
            // 如果handler本身设置了callback,则执行callback
            if (mCallback != null) {
                 /* 这种方法允许让activity等来实现Handler.Callback接口,避免了自己编写handler重写handleMessage方法。见http://alex-yang-xiansoftware-com.iteye.com/blog/850865 */
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            // 如果message没有callback,则调用handler的钩子方法handleMessage
            handleMessage(msg);
        }
    }
    
    // 处理runnable消息
    private final void handleCallback(Message message) {
        message.callback.run();  //直接调用run方法!
    }
    // 由子类实现的钩子方法
    public void handleMessage(Message msg) {
    }

可以看到,除了handleMessage(Message msg)和Runnable对象的run方法由开发者实现外(实现具体逻辑),handler的内部工作机制对开发者是透明的。这正是handler API设计的精妙之处!

 

 

 

Handler的用处

我在小标题中将handler描述为“异步处理大师”,这归功于Handler拥有下面两个重要的特点:

1.handler可以在任意线程发送消息,这些消息会被添加到关联的MQ上。

              

2.handler是在它关联的looper线程中处理消息的。

这就解决了android最经典的不能在其他非主线程中更新UI的问题。android的主线程也是一个looper线程(looper在android中运用很广),我们在其中创建的handler默认将关联主线程MQ。因此,利用handler的一个solution就是在activity中创建handler并将其引用传递给worker thread,

worker thread执行完任务后使用handler发送消息通知activity更新UI。(过程如图)

下面给出sample代码,仅供参考:

public class TestDriverActivity extends Activity {
    private TextView textview;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        textview = (TextView) findViewById(R.id.textview);
        // 创建并启动工作线程
        Thread workerThread = new Thread(new SampleTask(new MyHandler()));
        workerThread.start();
    }
    
    public void appendText(String msg) {
        textview.setText(textview.getText() + "\n" + msg);
    }
    
    class MyHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            String result = msg.getData().getString("message");
            // 更新UI
            appendText(result);
        }
    }
}

public class SampleTask implements Runnable {
    private static final String TAG = SampleTask.class.getSimpleName();
    Handler handler;
    
    public SampleTask(Handler handler) {
        super();
        this.handler = handler;
    }

    @Override
    public void run() {
        try {  // 模拟执行某项任务,下载等
            Thread.sleep(5000);
            // 任务完成后通知activity更新UI
            Message msg = prepareMessage("task completed!");
            // message将被添加到主线程的MQ中
            handler.sendMessage(msg);
        } catch (InterruptedException e) {
            Log.d(TAG, "interrupted!");
        }

    }

    private Message prepareMessage(String str) {
        Message result = handler.obtainMessage();
        Bundle data = new Bundle();
        data.putString("message", str);
        result.setData(data);
        return result;
    }
}

当然,handler能做的远远不仅如此,由于它能post Runnable对象,它还能与Looper配合实现经典的Pipeline Thread(流水线线程)模式。请参考此文《Android Guts: Intro to Loopers and Handlers》

封装任务 Message

在整个消息处理机制中,message又叫task,封装了任务携带的信息和处理该任务的handler。message的用法比较简单,这里不做总结了。但是有这么几点需要注意(待补充):

1.尽管Message有public的默认构造方法,但是你应该通过Message.obtain()来从消息池中获得空消息对象,以节省资源。

2.如果你的message只需要携带简单的int信息,请优先使用Message.arg1和Message.arg2来传递信息,这比用Bundle更省内存

3.擅用message.what来标识信息,以便用不同方式处理message.

· Handler的处理过程运行在创建Handler的线程里
· 一个Looper对应一个MessageQueue
· 一个线程对应一个Looper
· 一个Looper可以对应多个Handler
· 不确定当前线程时,更新UI时尽量调用post方法

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值