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();      }  }; @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的相互关系理清,接下来,分析在子线程中创建消息、发送消息和在主线程中处理消息的过程及在这些过程中的常用方法。



更多相关文章

  1. Android 导入项目时报Android.jar包丢失解决方法
  2. 干货 | 聊聊这些年总结的一些学习方法
  3. Android手机访问Django测试服务器方法
  4. Android多线程下载远程图片
  5. android Map 使用方法
  6. Android开发实践:自定义带消息循环(Looper)的工作线程
  7. Android实现屏幕旋转方法总结
  8. #菜鸟之旅#Android Studio开发JNI工程——Native调用Java方法获
  9. Android中线程形态AsyncTask、HandlerThread 和 IntentService简

随机推荐

  1. Android面试系列文章2018之Android部分We
  2. Android(安卓)面试题总结之Android(安卓)
  3. Android(安卓)的Margin和Padding属性以及
  4. Android——文件存储
  5. 修改android框架代码
  6. 【安卓学习之开发工具】 Android(安卓)St
  7. 《精通Android(安卓)2》书评
  8. Android(安卓)Studio 工程视图&项目构成
  9. android2.x中android:layout_marginRight
  10. Android中TextVIew一些属性