Android Handler的深入剖析

【Handler的简单用法】


Handler类的作用之一:

  • 在新启动的线程中发送消息。
  • 在主线程中获取、处理消息。

     为了让主程序能“适时”地处理新启动的线程所发送的消息,显然只能通过回调的方式来实现——开发者只要重写Handler类中处理消息的方法,当新启动的线程发送消息时,消息会发送到与之关联的MessageQueue,而Handler会不断地从MessageQueue中获取并处理消息——这将导致Handler类中处理消息的方法被回调。

     Handler类包含如下方法用于发送、处理消息。

  • void handleMessage(Message msg):处理消息的方法。该方法通常用于被重写。
  • final boolean  hasMessages(int what):检查消息队列中是否包含what属性为指定值的消息。
  • final boolean  hasMessages(int what,Object object):检查消息队列中是否包含what属性为指定值且object属性为指定对象的消息。
  • sendEmptyMessage(int what):发送空消息。
  • final boolean sendEmptyMessageDelayed(int what,long delayMillis):指定多少毫秒之后发送空消息。
  • final boolean sendMessage(Message msg):立即发送消息。
  • final boolean sendMessageDelayed(Message msg,long delayMillis):指定多少毫秒之后发送消息。

     借助于上面这些方法,程序可以方便地利用Handler来进行消息传递。

      实例:自动播放动画

      上面的程序通过一个新线程来周期性地修改ImageView所显示的图片,通过这种方式来开发一个动画效果。该程序的界面布局代码非常简单,程序只是在界面布局中定义了ImageView组件,界面布局文件如下。

       

复制代码
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical"
    android:gravity="center_horizontal">
<!-- 定义ImageView组件 -->
    <ImageView
        android:id="@+id/show"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:scaleType="center"/>

</LinearLayout>
复制代码

 

      接下来主程序使用java.util.Timer来周期性地执行指定任务,程序代码如下。

       

复制代码
package com.example.studyevent;

import java.util.Timer;
import java.util.TimerTask;

import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.app.Activity;
import android.view.Menu;
import android.widget.ImageView;

public class HandlerTest extends Activity {
  //定义周期性显示的图片的ID
    int[] imageIds=new int[]{
            R.drawable.java,
            R.drawable.ee,
            R.drawable.ajax,
            R.drawable.xml,
            R.drawable.classic
    };
    int currentImageId=0;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_handler_test);
        final ImageView show=(ImageView)findViewById(R.id.show);
        final Handler myHandler=new Handler(){

            @Override
            public void handleMessage(Message msg) {
                // TODO Auto-generated method stub
                //如果该消息是本程序所发送的
                if(msg.what==0x1233)
                {
                    //动态地修改所显示的图片
                    show.setImageResource(imageIds[currentImageId++%imageIds.length]);
                }
            }
        };
        //定义一个计时器,该计时器周期性的执行指定任务
        new Timer().schedule(new TimerTask(){
            @Override
            public void run() {
                // TODO Auto-generated method stub
                //发送空消息
                myHandler.sendEmptyMessage(0x1233);
            }
        }, 0,1200);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.handler_test, menu);
        return true;
    }

}
复制代码

      上面的程序中下面的粗体字代码通过Timer周期性地执行指定任务,Timer对象可调度TimerTask对象,TimerTask对象的本质就是启动一条新线程,由于Android不允许在新线程中访问Activity里面的界面组件,因此程序只能在新线程里发送一条消息,通知系统更新ImageView组件。

 

【Handler的深入剖析 一 】


1、Looper:每个线程只能拥有一个Looper。它的loop方法负责读取MessageQueue中的消息,读取到消息之后就把消息交给发送该消息的Handler进行处理。创建好一个Looper的同时,就会在Looper内部自动的创建好一个MessageQueue队列,只有Looper和MessageQueue都创建好了之后才能够创建和使用Handler

2、主UI线程中,系统已经初始化了一个Looper对象,因此程序直接创建Handler即可,然后就可通过Handler来发送消息、处理消息。

3、如果想要在子线程中使用Handler的话,那么我们就必须在子线程中首先创建Looper,在子线程中创建Looper和创建Handler的结构是:

Looper.prepare();

.......................

Handler handler = new Handler()
{
 @Override
 public void handleMessage(Message msg) {
}
} ....................... Looper.loop() ;


4、实例:

 1 package com.test.looper;
 2 
 3 import android.app.Activity;
 4 import android.os.Bundle;
 5 import android.os.Handler;
 6 import android.os.Looper;
 7 import android.os.Message;
 8 import android.util.Log;
 9 
10 public class MainActivity extends Activity {
11     private LooperThread  looperThread;
12 
13     @Override
14     public void onCreate(Bundle savedInstanceState) {
15         super.onCreate(savedInstanceState);
16         setContentView(R.layout.main);
17         looperThread = new LooperThread();
18         looperThread.start();
19         looperThread.getHandler().sendEmptyMessage(1);
20     }
21     
22     
23     class LooperThread extends Thread {
24         private Handler mHandler;
25         private final Object mSync = new Object();
26 
27         public void run() {
28             Looper.prepare();
29             synchronized (mSync) {
30                 mHandler = new Handler(){
31                     @Override
32                     public void handleMessage(Message msg) {
33                         Log.d("CYQ", "--->" + msg);
34                     }
35                 };
36                 mSync.notifyAll();
37             }
38             Looper.loop();
39         }
40         
41         public Handler getHandler() {
42             synchronized (mSync) {
43                 if (mHandler == null) {
44                     try {
45                         mSync.wait();
46                     } catch (InterruptedException e) {
47                     }
48                 }
49                 return mHandler;
50             }
51         }
52         public void exit() {
53             getHandler().post(new Runnable(){
54                 public void run() {
55                     Looper.myLooper().quit();
56                 }});
57         }
58     }
59 }

【深入分析】
1、注意Looper.prepare() 和 Looper.loop()实际上形成了一个“死循环”,也就是说,如果不调用Looper.myLooper().quitsafely()方法,循环是不会终止的,那么自然处于Looper.loop()方法之后的代码是永远不会执行到的,这一点要非常注意

2、上面涉及到了wait()方法,当一个对象调用wait()方法的时候,那么这个对象所在的当前的线程就会等待,直到在另一个线程中用相同的对象调用notify或者notifyAll方法

3、上面的代码是考虑到稳定性的,在getHandler方法中,加入了同步,防止线程start了,但是并没有run结束,若此时直接返回mHandler会是一个null

 

【设计模式】

实际上在子线程中使用Handler的编程模式是:

1、上面的例子就是一个非常好的模式,我们将Handler的创建和处理方法放在一个子线程中,在这个处理方法中处理耗时操作

2、在主线程中首先将含有处理耗时操作的Handler的子线程启动,同时Looper循环也就启动了,这个Handler对象就能够实时的“监听”所有的处理请求

3、当我们在主线程的某个地方需要进行Handler能够处理的耗时操作时,就发送消息

4、实际上我们也可以使用程序内部广播或者Service实现这样的模式

 

【Handler的深入剖析 二 】


1、有了上面的模式分析,下面的内容就显得非常的清楚了;Android为我们提供了Handler机制,使我们能够通过:

发送请求——Handler响应请求——将请求放入队列——不断地从MessageQueue中取请求——Handler处理请求

这样一种模式来实现任务的处理,而不是硬性的通过Thread的start()方法,开始业务的处理。那么为什么不将这种方式发挥的更加完善呢?

2、何为完善?我们想过这样的一个问题没有:使用Thread的start()机制,是没有队列的,也就是说,当有多个请求到达的时候,我们是无法控制这些任务按顺序完成的,而且存在并发的可能性,这样的话,我们只能够使用synchronized同步机制,这非常的麻烦;我们为什么不利用Handler中的队列,解决这一问题呢?

3、Google不是傻瓜,它早就意识到了这个问题,因此提供了下面的Handler的种种方法:

public    final     boolean    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. 

public    final     boolean   postAtFrontOfQueue(Runnable r)                                                                          

Posts a message to an object that implements Runnable. Causes the Runnable r to executed on the next iteration through the message queue. The runnable will be run on the thread to which this handler is attached. This method is only for use in very special circumstances -- it can easily starve the message queue, cause ordering problems, or have other unexpected side-effects.

public    final    boolean  postAtTime(Runnable r, Object token, long uptimeMillis)    
Causes the Runnable r to be added to the message queue, to be run at a specific time given by uptimeMillis. The time-base is uptimeMillis(). Time spent in deep sleep will add an additional delay to execution. The runnable will be run on the thread to which this handler is attached.
 
public   final   boolean   postAtTime (Runnable r, long uptimeMillis)    
Causes the Runnable r to be added to the message queue, to be run at a specific time given by uptimeMillis. The time-base is uptimeMillis(). Time spent in deep sleep will add an additional delay to execution. The runnable will be run on the thread to which this handler is attached.
 
public    final     boolean postDelayed(Runnable r, long delayMillis)       

Causes the Runnable r to be added to the message queue, to be run after the specified amount of time elapses. The runnable will be run on the thread to which this handler is attached. The time-base is uptimeMillis(). Time spent in deep sleep will add a additional delay to execution.

 
public  final  void  removeCallbacks(Runnable r)    
Remove any pending posts of Runnable r that are in the message queue.
 
public   final  void removeCallbacks(Runnable r, Object token)    
Remove any pending posts of Runnable r with Object token that are in the message queue.  If token is null, all callbacks will be removed.
 
public   final   void removeCallbacksAndMessages(Object token)    
Remove any pending posts of callbacks and sent messages whose obj is token.  If token is null, all callbacks and messages will be removed.
 
public  final  void  removeMessages(int what)       

Remove any pending posts of messages with code 'what' that are in the message queue

 
public final void removeMessages(int what, Object object)         

Remove any pending posts of messages with code 'what' and whose obj is 'object' that are in the message queue.  If object is null, all messages will be removed.

 

【分析】

1、上面的方法一定要清楚的记住,有问题就查阅官方文档,这才是Handler的精髓;

2、具体的使用方法是非常的简单的,只要一个实例,就会非常清楚的掌握这种用法,具体的实例可以参见《 MenuDrawer 二 》博文中例子, 一定要把它掌握,以后在进行编程的时候要将这种模式使用的淋漓尽致,让别人觉得你的代码非常的清楚

 

posted @ 2014-11-30 21:34  RoperLee  阅读(343)  评论(0)    收藏  举报