Android - Handler 、AsyncTask(一)

android中,更新UI的操作都必须在主线程中进行,不能做阻塞线程的操作。

当我们要执行一个耗时的操作并且最终要去更新UI(比如将计算结果反映到UI上)时,我们会考虑新开一个线程,去执行这个耗时的操作,执行完毕之后,再在主线程中更新UI

为了解决这种问题,android为我们提供了很多办法。

 

                                                                   一、handlermessage机制


下边这个小Demo演示了Handler最简单常见的用法,在新开的线程里模拟一个耗时的操作timeConsumingOperation(),操作完成后利用handler.sendEmptyMessage(0)发送消息,然后在主线程中mProgressDialog.dismiss()更新UI:

 

public class MainActivity extends Activity implements OnClickListener{	
	private Button mButton;
	private ProgressDialog mProgressDialog;		
	private Handler handler = new Handler(){ 
	   	public void handleMessage(Message msg){  
	     		super.handleMessage(msg);  
	     		mProgressDialog.dismiss();  
	    	}  
	}; 
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		mButton= (Button)findViewById(R.id.button1);  
        mButton.setOnClickListener(this); 
	}
	@Override
	public void onClick(View v) {
		newThread();
	}
	private void newThread(){  
	    	mProgressDialog= ProgressDialog.show(this, "提示", "正在下载,请稍后 ……");  
        	new Thread(){  
            		public void run(){  
               	 		timeConsumingOperation();  
                		handler.sendEmptyMessage(0);  
            		}  
        	}.start();  
	} 
    	private void timeConsumingOperation(){  
        	try {  
            		Thread.sleep(5000);  
       		 } catch (InterruptedException e) {  
            		e.printStackTrace();  
        	 }  
    	}
}

 

在上述代码中,发送消息的 sendEmptyMessage() 方法是由Handler的子类对象调用的,更新UI的方法mProgressDialog.dismiss()也是在Handler的子类复写的handleMessage()方法中定义的,所以我们先从创建Handler的子类对象new Handler()讲起

Handler类概要:

 

/**
 * A Handler allows you to send and process {@link Message} and Runnable
 * objects associated with a thread's {@link MessageQueue}.  Each Handler
 * instance is associated with a single thread and that thread's message
 * queue.  When you create a new Handler, it is bound to the thread /
 * message queue of the thread that is creating it -- from that point on,
 * it will deliver messages and runnables to that message queue and execute
 * them as they come out of the message queue.
 * <p>There are two main uses for a Handler: (1) to schedule messages and
 * runnables to be executed as some point in the future; and (2) to enqueue
 * an action to be performed on a different thread than your own.     
	... ...
//一个Handler允许你发送和处理和一个线程的Message Queue相关联的Message和Runnable对象,
//每一个Handler和一个线程及线程的Message Queue相关联... ...
public class Handler {
	......

 

(注释太多不全列出了)通常我们执行new Handler()时,以下构造函数会被调用:

 

public Handler(Callback callback, boolean async) { 
	... ...  
	final MessageQueue mQueue;
	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 = callback;
	...	...

 

其中mLooper是Handler的成员变量final Looper mLooper, Looper类的作用是管理此线程里的Message Queue先来看看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
  * {@link #prepare} in the thread that is to run the loop, and then
  * {@link #loop} to have it process messages until the loop is stopped.
  * 
  * <p>Most interaction with a message loop is through the
  * {@link Handler} class.
  */
public final class Looper {}

此类用来为一个线程run一个message loop,一个线程默认是没有和其关联的message loop的,如果想要创建,则调用prepare方法,然后调用loop方法来处理消息......

而myLooper()方法的注释如下:

/**
 * Return the Looper object associated with the current thread.  Returns
 * null if the calling thread is not associated with a Looper.
 */
//该方法用于获取当前线程的Looper对象,如果没有则返回null
public static Looper myLooper() {
	return sThreadLocal.get();
}

该方法用于获取当前线程的Looper对象,如果没有则返回null

需要注意的是:系统默认为主线程而没有为子线程创建looper对象,所以在子线程中直接new Handler()会报如下错误(参考public Handler(Callback callback, boolean async)方法):                                                          

"Can't create handler inside thread that has not called Looper.prepare()");

我们来看看系统在主线程中创建looper对象的相关代码,从ActivityThread.java中我们可以看到:

public static void main(String[] args) {
	... ...
	Looper.prepareMainLooper();
	... ...
	Looper.loop();
	... ...
}

其中,prepareMainLooper()方法的注释如下:

/**
 * 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()}
 */
//初始化当前的线程为一个looper线程,标记它为一个应用的main looper,应用的main looper为系统自动创建,你不需要自己调用该方法
public static void prepareMainLooper() {
        prepare(false);
	... ...
}

prepare()方法的注释如下:

/** 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()}.
 */
//初始化当前线程为一个looper线程,这使得你有机会去创建一个handler,在调用这个方法之后,在想正式开始loop之前
//记得调用loop方法,最后调用quit方法end it
public static void prepare() {
        prepare(true);
}

loop()方法的注释如下:

/**
 * Run the message queue in this thread. Be sure to call
 * {@link #quit()} to end the loop.
 */
//让这个线程里的message queue run起来,确保调用quit方法来end the loop
public static void loop() {
	... ... 

同样,我们如果需要在子线程中new Handler,也需要调用Looper.prepare()和Looper.loop()两个方法,正如API中对looper类的介绍一样:

/**
 * <p>This is a typical example of the implementation of a Looper thread,
 * using the separation of {@link #prepare} and {@link #loop} to create an
 * initial Handler to communicate with the Looper.
 *
 * <pre>
 *  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();
 *      }
 *  }</pre>
 */
 public final class Looper {}

接下来,我们看prepare()方法的主要逻辑:

public final class Looper {
	final MessageQueue mQueue;
	public static void prepare() {
        	prepare(true);
    	}
    	private static void prepare(boolean quitAllowed) {
		... ...
        	sThreadLocal.set(new Looper(quitAllowed));
		... ... 
	}
	private Looper(boolean quitAllowed) {
        	mQueue = new MessageQueue(quitAllowed);
		... ...
	}
}

可以看到,无论是在主线程的prepareMainLooper()方法中还是我们手动调用prepare()方法,都会在创建Looper对象的同时创建一个Message Queue对象赋给Looper的成员变量final MessageQueue mQueue

现在来看用来存放消息的Message Queue的继承关系及类概要:

/**
 * Low-level class holding the list of messages to be dispatched by a
 * {@link Looper}.  Messages are not added directly to a MessageQueue,
 * but rather through {@link Handler} objects associated with the Looper.
 * 
 * <p>You can retrieve the MessageQueue for the current thread with
 * {@link Looper#myQueue() Looper.myQueue()}.
 */
public final class MessageQueue {

而我们new Handler 时,在public Handler(Callback callback, boolean async)方法中,在通过Looper.myLooper()方法得到线程对应的Looper对象后,将该Looper对象的成员变量mQueue再赋给Handler的final MessageQueue mQueue,所以,一个Handler对象对应一个线程对应一个Looper对象对应一个MessageQueue 。


至此,我们 从Handler对象的创建开始,大致将Handler、Looper和MessageQueue 的相互关系理清了。接下来,我们分析在子线程中发送消息和在主线程中处理消息的过程。

在新开的线程中完成耗时的操作后,需要发送消息,那我们就 从消息的创建开始

在Handler机制中,创建一个消息可以用以下两种方法:
第一种,Message mMessage = new Message();
第二种,Message mMessage = mHandler.obtainMessage();

这两种方法的区别,我认为主要有以下两方面:
一、效率上的区别。obtainMessage()方法直接从message pool中返回一个Message对象,省去了新创建对象的开销,提高了效率。我们来看Handler类中的注释:
/**
  * Returns a new {@link android.os.Message Message} from the global message pool. More efficient than
  * creating and allocating new instances. The retrieved message has its handler set to this instance (Message.target == this).
  *  If you don't want that facility, just call Message.obtain() instead.
  */
//直接从message pool中返回一个message,这将比直接创建一个message更有效率,返回的message已经有了和它关联的handler(这是
//在Message类中的obtain(Handler h)方法中通过m.target = h实现的),你也可以直接调用Message.obtain()方法来创建消息
public final Message obtainMessage(){
	return Message.obtain(this);
}

二、灵活性上的区别

通过查看Message和Handler的源码,我们发现,Handler类中obtainMessage()方法最终会调用Message 类中的obtain()方法,并且这两个方法都有若干个不同参数的一 一对应的方法可供选择,我们就以Message类中参数最多的obtain(Handler h, int what, int arg1, int arg2, Object obj)方法为例来讲吧,直接上源码:
/**
 * Same as {@link #obtain()}, but sets the values of the <em>target</em>, <em>what</em>, 
 * <em>arg1</em>, <em>arg2</em>, and <em>obj</em> members.
 */
public static Message obtain(Handler h, int what, int arg1, int arg2, Object obj) {
	Message m = obtain();
    m.target = h;
    m.what = what;
    m.arg1 = arg1;
    m.arg2 = arg2;
    m.obj = obj;
    return m;
}

第一句,Message m = obtain();我们来看obtain()方法的注释:

/**
  * Return a new Message instance from the global pool. Allows us to
  * avoid allocating new objects in many cases.
  */
//从global pool中返回一个新的message对象,能避免我们在很多情况下去新建对象
public static Message obtain() {
	... ...
}

在获得一个Message对象之后,接着为它的成员变量赋值:m.what = what 、m.arg1 = arg1 ,等等。

接下来我们看看,Message类都有哪些成员变量,它们的作用都是什么。依然是源码:
public final class Message implements Parcelable {
    /**
     * User-defined message code so that the recipient can identify 
     * what this message is about. Each {@link Handler} has its own name-space
     * for message codes, so you do not need to worry about yours conflicting
     * with other handlers.
     */
    //可以理解为一个用于区分消息的标示符
    public int what;
    /**
     * arg1 and arg2 are lower-cost alternatives to using
     * {@link #setData(Bundle) setData()} if you only need to store a
     * few integer values.
     */
    //如果你只是想携带一些比较简单的integer数据的话,相对于setData(Bundle)方法来讲
    //arg1和arg2是一种比较低耗的选择
    public int arg1; 
    /**
     * arg1 and arg2 are lower-cost alternatives to using
     * {@link #setData(Bundle) setData()} if you only need to store a
     * few integer values.
     */
    public int arg2;
    Bundle data;
    Handler target;
    Runnable callback;  ... ...
}

1、最常用的what,可以理解为是一个对message进行区分的标示符。

2、arg1和arg2可以让message携带简单的整数,它相对于setData(Bundle data)使用Bundle来传送数据,更简单轻便。
3、data变量在需要传送的数据比较复杂的时候使用,比如:
Bundle mBundle = new Bundle();
mBundle.putBoolean("myKey1", true);
mBundle.putString("myKey2", "myValue");
Message mMessage = Message.obtain(handler, 5, 111, 112, mBundle);
//或者:
Bundle mBundle = new Bundle();
mBundle.putBoolean("myKey1", true);
mBundle.putString("myKey2", "myValue");
Message mMessage = new Message();
mMessage.setData(mBundle);
//在接收到message时,可以通过Message的getData()方法获取mBundle对象

在这里,我们也可以感受到 为获得Message对象的obtain()方法的参数中 加入Object obj的作用,它 让message的创建有了更好的扩展性

4、Handler target ,Message的成员变量
5、Runnable callback ,这个放到后边来讲

通过上边的讲解,我们可以看到,在创建一个Message对象时,我们需要考虑 :
为它的哪些成员变量赋值,需要让它携带什么样的数据,有没有对应的obtain()方法可供使用,是使用obtain()方法呢,还是直接new一个Message再为它的成员变量赋值?
—— 这就是创建一个Message对象的两种方法在灵活性上的区别

消息创建完成了,接着讲发送消息
Handler类中发送消息的主要是send和post两类方法,先看send相关方法
比较常用的有:sendMessage和sendEmptyMessage,它们的注释如下:
/**
 * Pushes a message onto the end of the message queue after all pending messages
 * before the current time. It will be received in {@link #handleMessage},
 * in the thread attached to this handler.
 *  
 * @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 sendMessage(Message msg){
	return sendMessageDelayed(msg, 0);
}
/**
 * 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.
 */
 //发送一条仅仅含有what值得消息,返回true(false)如果(没有)成功将消息发送到消息队列
public final boolean sendEmptyMessage(int what){
       return sendEmptyMessageDelayed(what, 0);
}

sendMessage()方法是将一个(不管通过什么方式)已经构建好的Message对象放到消息队列中,而sendEmptyMessage(int what)方法,则是先通过Message.obtain()获得一个Message,将what值赋给Message的what变量:msg.what = what,再将消息发送到消息队列。

所以,如果需要发送一个仅仅包含what值的消息,那么Handler类的sendEmptyMessage(int what)方法是首选(优化效率)。

然后看post相关的方法,以post(Runnable r)为例,先看源码:
/**
 * Causes the Runnable r to be added to the message queue.
 * The runnable will be run on the thread to which this handler is 
 * attached. 
 * @param r The Runnable that will be executed.
 * @return Returns true if the Runnable 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 post(Runnable r){
	return  sendMessageDelayed(getPostMessage(r), 0);
}

需要说明的是,post(Runnable r)方法和sendMessage(Message msg)方法都调用了sendMessageDelayed(Message msg, long delayMillis)方法,只不过post(Runnable r)方法中,先通过getPostMessage(r)获取了 一个成员变量Runnable callback的值为Runnable r 的Message对象,下面是getPostMessage(Runnable r)的源码:

private static Message getPostMessage(Runnable r) {
    Message m = Message.obtain();
    m.callback = r;
    return m;
}

在讲消息处理之前,还需要提到的是,我们可以从Handler.java中看到,无论调用哪个send和post方法来发送message,最终都会调用enqueueMessage()方法,我们来看源码:

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    msg.target = this;
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis);
}

在enqueueMessage()方法的第一行,先执行msg.target = this,将发送消息的这个handler赋值给Message的成员变量Handler target,然后再将消息发送到消息队列

现在,来看处理消息的逻辑:
在Looper类的概要中,我们提到过,调用loop方法之后,就能循环从消息队列中取出并处理消息了
loop()方法:
public static void loop() {
    for (;;) {
	    Message msg = queue.next();	
	    msg.target.dispatchMessage(msg);
    }
}

loop()方法中,循环从MessageQueue中获取Message对象(Message msg = queue.next();,然后调用 msg.target.dispatchMessage(msg)方法进行处理,哪个handler发送的消息就由哪个handler来处理,而我们在讲发送消息时也做过铺垫了。来看dispatchMessage()方法的定义:

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

首先判断,如果(msg.callback != null),则执行handleCallback(msg),所以由post发送过来的Runnable对象,在处理的时候执行的就是handleCallback(msg)方法,来看该方法的定义:

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

如果(msg.callback == null),则执行handleMessage(msg)方法,来看该方法在Handler类中的定义:

/**
 * Subclasses must implement this to receive messages.
 */
  //子类必须实现该方法来处理消息
public void handleMessage(Message msg) {
  //空的
}

我们从一个简单常见的例子入手,先以Handler对象的创建为出发点,大致将Handler、Looper和MessageQueue 的相互关系理清,接下来,分析在子线程中创建消息、发送消息和在主线程中处理消息的过程及在这些过程中的常用方法。



posted on 2015-03-14 11:43  快乐的码农  阅读(386)  评论(0)    收藏  举报