1.Handler简介
handler是Android系统封装的用于线程更新UI,消息处理的机制。
[说明]
查看Android Framework源码可以看到,常见的Activity的生命周期onCreate(), onStart(), onResume(), onPause(), onStop(), onDestroy()都是通过handler发送不同Message,AMS(Activity Manager Service)通过Handler向ActivityThread发送消息,在ActivityThread中执行不同的Activity的周期。
2.Handler的用法
// Google文档的地址 http://developer.android.com/reference/android/os/Handler.html // Google对Handler的介绍 A Handler allows you to send and process Message and Runnable objects associated with a thread's 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.
[说明]
查看Google文档可以看出,Handler允许我们绑定(associate with)一个线程的MessageQueue,并通过该handler向该线程发送Message或者Runnable对象,一个Handler绑定了一个线程以及这个线程的MessageQueue,当你创建一个Handler的时候,该Handler就会和Thread以及该Thread的MessageQueue绑定。
首先,下面的程序,在子线程中更新UI:
public class MainActivity extends Activity { TextView tv; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); tv = (TextView) findViewById(R.id.tv); new Thread() { public void run() { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } tv.setText("update UI"); }; }.start(); } }
运行之后,程序奔溃,logcat中打印信息:
[说明]
上面信息的主要内容是,不允许在子线程中更新UI线程,Android自身设定的机制则是只能在子线程中处理耗时的操作,处理完毕之后,在UIThread中更新UI,而子线程是不能更新UI的。
Handler主要提供了下面API来实现子线程与UIThread之间的通信问题:
- sendMessage
- sendMessageDelayed
- post(Runnable)
- postDelayed(Runnable, long)
(1). 使用sendMessage进行线程通信:
public class MainActivity extends Activity { @SuppressLint("HandlerLeak") Handler handler = new Handler() { public void handleMessage(android.os.Message msg) { if (msg.what == 0x1) { tv.setText("update UI"); } }; }; TextView tv; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); tv = (TextView) findViewById(R.id.tv); new Thread() { public void run() { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } handler.sendEmptyMessage(0x1); }; }.start(); } }
sendMessageDelayed(Message, long)是延迟long毫秒之后,再发送Message。
(2).post(Runnable)
public class MainActivity extends Activity { @SuppressLint("HandlerLeak") Handler handler = new Handler() { public void handleMessage(android.os.Message msg) { }; }; TextView tv; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); tv = (TextView) findViewById(R.id.tv); new Thread() { public void run() { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } handler.post(new Runnable() { @Override public void run() { tv.setText("update UI"); } }); }; }.start(); } }
下面利用Handler实现一个简单的图片轮换的功能,每隔1s循环替换ImageView显示的图片:
public class MainActivity extends Activity { Handler handler = new Handler(); ImageView img; int[] images = // 存放循环播放的图片 { R.drawable.weather_0, R.drawable.weather_1, R.drawable.weather_2, R.drawable.weather_3, R.drawable.weather_4 }; private int imgCount = images.length; private int index = 0; private long delayMillis = 1000L; // 每隔1s循环一次 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); img = (ImageView) findViewById(R.id.img); handler.post(imgRunnable); } private Runnable imgRunnable = new Runnable() { public void run() { img.setBackgroundResource(images[index % imgCount]); index++; handler.postDelayed(imgRunnable, delayMillis); } }; }
对应的xml文件中只有一个<ImageView />:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" > <ImageView android:id="@+id/img" android:layout_width="200.0dip" android:layout_height="200.0dip" android:layout_centerInParent="true" android:contentDescription="@string/app_name" /> </RelativeLayout>
[注意]若要终止上面的循环,可以使用下面的方法:
handler.removeCallbacks(imgRunnable);
下面使用Handler演示消息截获的用法:
[说明]
Handler的创建有一个接受一个Handler.Callback参数的构造方法,此方法内部的handlerMessage(Message msg)方法有boolean类型的返回值,return true可以实现类似消息截获的效果:
public class SecondActivity extends Activity { @SuppressLint("HandlerLeak") Handler handler = new Handler(new Handler.Callback() { @Override public boolean handleMessage(Message msg) { if (msg.what == 0x1) { Log.v(LOG, "返回true,消息被截获"); return true; } else if (msg.what == 0x2) { Log.v(LOG, "返回false,不截获"); return false; } return false; } }) { @Override public void handleMessage(Message msg) { Log.v(LOG, "收到消息...."); } }; Button btn1, btn2; private final String LOG = "SecondActivity"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_second); btn1 = (Button) findViewById(R.id.btn1); btn1.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { handler.sendEmptyMessage(0x1); } }); btn2 = (Button) findViewById(R.id.btn2); btn2.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { handler.sendEmptyMessage(0x2); } }); } }
界面效果如下:
点击“消息截获”按键,会向handler对象传递0x1的Message,在Handler.Callback的boolean handlerMessage()方法中返回true,此时该Message不会再被void handlerMessage()方法接收到:
相反,当点击“消息不截获”按键,会发送0x2消息,Handler.Callback的boolean handlerMessage()返回false,此时消息仍然会被void handlerMessage()方法所获的:
查看Handler的源码可以看到,消息的传递是通过dispatchMessage(Message msg)来发送的,而dispatchMessage函数如下:
当使用了Handler包含Handler.Callback参数的构造函数时,mCallback != null,此时会执行mCallback.handlerMessage(msg)方法,若此方法返回true,则直接执行return返回,不再执行下面的handlerMessage(msg),若mCallback.handlerMessage(msg)返回false,mCallback.handlerMessage(msg)方法被调用之后,还会继续调用handlerMessage(msg)方法。