Android Handler
最近经常用到Handler,今天复习了一下,知识还是要经常复习的哈,温故知新!
Handler主要接受子线程数据,并用此数据配合主线程更新UI。
Handler的作用:
(1)发送和处理消息(Message)。
(2)发送和处理Runnable对象。
对于Android里的消息处理,涉及到Handler,Looper,Message,MessageQueue等概念,先理顺这些概念。
Message:消息,其中包含了消息ID,消息处理对象以及处理的数据等,由MessageQueue统一列队,终由Handler处理。
Handler:处理者,负责Message的发送及处理。使用Handler时,需要实现handleMessage(Message msg)方法来对特定的Message进行处理,例如更新UI等。
MessageQueue:消息队列,用来存放Handler发送过来的消息,并按照FIFO(队列先进先出)规则执行。当然,存放Message并非实际意义的保存,而是将Message以链表的方式串联起来的,等待Looper的抽取。
Looper:一个线程可以产生一个Looper对象,用来管理MessageQueue,它就像一个消息泵,不断地从MessageQueue中抽取Message执行。因此,一个MessageQueue需要一个Looper。
一、发送和处理消息(Message)
Handler myHandler = new Handler() { @Override public void handleMessage(Message msg) { super.handleMessage(msg); if (msg.what == 1) { } } }; // 第一种 调用myHandler.obtainMessage() 获得的Message已经跟myHandler绑定 Message msg1 = myHandler.obtainMessage(); // 直接调用 sendToTarget() 就可以把消息送入队列,等待Handler执行 msg1.sendToTarget(); // 第二种 直接用Message的构造函数 Message msg2 = new Message(); // 调用Handler 实例的 sendMessage()方法把消息送入队列,等待Handler执行 myHandler.sendMessage(msg2);// 即时send myHandler.sendEmptyMessageAtTime(1, uptimeMillis);// 在uptimeMillis时间点,send属性what值为1的Message myHandler.sendEmptyMessage(1);// send属性what值为1的Message //其他以此类推
DEMO1
在Activity中,定义一个Button,TextView,我们点击Button来实时的更新这个TextView的内容。
package com.dimos.handler; import android.app.Activity; import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.view.View; import android.widget.Button; import android.widget.TextView; public class MainActivity extends Activity { private TextView textView; private MyHandler myHandler;// 定义一个自己的Handle类 private Button button; private MyThread m = new MyThread(); // 定义一个自己的线程类 @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); textView = (TextView) findViewById(R.id.txtShow); button = (Button) findViewById(R.id.btnOnlick); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View arg0) { myHandler = new MyHandler(); new Thread(m).start(); System.out.println("主线程运行ID:" + Thread.currentThread().getId()); } }); }// 在对UI进行更新时,执行时所在的线程为主UI线程 class MyHandler extends Handler {// 继承Handler类时,必须重写handleMessage方法 public MyHandler() { } public MyHandler(Looper looper) { super(looper); } @Override public void handleMessage(Message msg) {// 执行接收到的通知,此时执行的顺序是按照队列进行,即先进先出 super.handleMessage(msg); Bundle b = msg.getData(); String textStr1 = b.getString("textStr"); textView.setText(textStr1);// 更改TextView中的值 } }// 该线程将会在单独的线程中运行 class MyThread implements Runnable { int i = 1; @Override public void run() { while (i < 11) { System.out.println("当前运行线程ID:" + Thread.currentThread().getId()); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } Message msg = new Message(); Bundle b = new Bundle(); b.putString("textStr", "线程运行" + i + "次"); i++; msg.setData(b); myHandler.sendMessage(msg);// 通过sendMessage向Handler发送更新UI的消息 } i = 1;// 下次启动线程时重新计数。 } } }
XML文件 activity_main.xml如下:
<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" > <Button android:id="@+id/btnOnlick" android:layout_height="wrap_content" android:layout_width="fill_parent" android:text="点击"/> <TextView android:id="@+id/txtShow" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="被点前" android:layout_centerHorizontal="true" android:layout_centerVertical="true"/> </RelativeLayout>
可以看到,屏幕上的TextView在实时变化,考察线程ID可以得知是由哪一个线程来完成这项工作的。再次点击按钮后,则会触发新的线程去执行实时更新的操作。
二、发送和处理消息 Runnable
Runnable runnable = new Runnable() { @Override public void run() { } }; long time = System.currentTimeMillis(); myHandler.post(runnable);// 即使post myHandler.postAtFrontOfQueue(runnable);// 插入队头 myHandler.postAtTime(runnable, time);// 在time时间点 post myHandler.postDelayed(runnable, 3000);// 延迟3秒 post // 其他以此类推
DEMO2
package com.dimos.handler; import android.app.Activity; import android.os.Bundle; import android.os.Handler; public class HandlerMsg extends Activity { private Handler handler = new Handler(); @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); handler.post(new MyRunnable()); System.out.println("Oncreate---The Thread id is :" + Thread.currentThread().getId()); setContentView(R.layout.activity_main); } private class MyRunnable implements Runnable { public void run() { System.out.println("Runnable---The Thread is running"); System.out.println("Runnable---The Thread id is :" + Thread.currentThread().getId()); try { Thread.sleep(6000); } catch (InterruptedException e) { e.printStackTrace(); } } } }
控制台的输出结果:
: Oncreate---The Thread id is :1
: Runnable---The Thread is running
: Runnable---The Thread id is :1
程序启动后6秒,我们才看到activity_main.xml中的内容(只是显示一个TextView)。
这表明handler和主线程是同一个线程。如果这时候你做的是一个耗时的操作(比如下载),那么这样是不可行的。
android给我们提供了Looper这样一个类。其实Android中每一个Thread都跟着一个Looper,Looper可以帮助Thread维护一个消息队列。
DEMO3
package com.dimos.handler; import android.app.Activity; import android.os.Bundle; import android.os.Handler; import android.os.HandlerThread; public class HandlerMsg2 extends Activity { private Handler handler = null; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); HandlerThread handlerThread = new HandlerThread("myHandlerThread"); handlerThread.start(); handler = new Handler(handlerThread.getLooper()); handler.post(new MyRunnable()); System.out.println("Oncreate---The Thread id is :" + Thread.currentThread().getId()); setContentView(R.layout.activity_main); } private class MyRunnable implements Runnable { public void run() { System.out.println("Runnable---The Thread is running"); System.out.println("Runnable---The Thread id is :" + Thread.currentThread().getId()); try { Thread.sleep(6000); } catch (InterruptedException e) { e.printStackTrace(); } } } }
输出结果:
: Oncreate---The Thread id is :1
: Runnable---The Thread is running
: Runnable---The Thread id is :2833
程序启动后,我们立刻看到activity_main.xml中的内容,这样就达到了多线程的结果。
其实handler.post(Runnable)很强大,在很多地方都有用到,如
Gallery的onItemClick 跟onTouchEvent 不能同时用在3.0之后,但是可以用handler.post(Runnable)解决:
Create Handler作为一个全局变量
private Handler handler;
在onCreate中new Handler,所以handler在主线程
handler = new Handler();
然后在Gallery的onItemClick里面用handler.post(Runnable),like this
handler.post(new Runnable() { @Override public void run() { // Do what ever... } });