Android - Handler 、AsyncTask(一)
在android中,更新UI的操作都必须在主线程中进行,不能做阻塞主线程的操作。
当我们要执行一个耗时的操作并且最终要去更新UI(比如将计算结果反映到UI上)时,我们会考虑新开一个线程,去执行这个耗时的操作,执行完毕之后,再在主线程中更新UI。
为了解决这种问题,android为我们提供了很多办法。
一、handler和message机制
下边这个小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(); } }; @Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);mButton= (Button)findViewById(R.id.button1); mButton.setOnClickListener(this); }@Overridepublic 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对象,如果没有则返回nullpublic 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 itpublic 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 looppublic 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的相互关系理清,接下来,分析在子线程中创建消息、发送消息和在主线程中处理消息的过程及在这些过程中的常用方法。
更多相关文章
- Android 导入项目时报Android.jar包丢失解决方法
- 干货 | 聊聊这些年总结的一些学习方法
- Android手机访问Django测试服务器方法
- Android多线程下载远程图片
- android Map 使用方法
- Android开发实践:自定义带消息循环(Looper)的工作线程
- Android实现屏幕旋转方法总结
- #菜鸟之旅#Android Studio开发JNI工程——Native调用Java方法获
- Android中线程形态AsyncTask、HandlerThread 和 IntentService简