package ispring.com.testhandler;import android.app.Activity;import android.os.Bundle;import android.view.View;import android.widget.Button;import android.widget.TextView;public class MainActivity extends Activity implements Button.OnClickListener { private TextView statusTextView = null; @Override protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);statusTextView = (TextView)findViewById(R.id.statusTextView);Button btnDownload = (Button)findViewById(R.id.btnDownload);btnDownload.setOnClickListener(this); } @Override public void onClick(View v) {DownloadThread downloadThread = new DownloadThread();downloadThread.start(); } class DownloadThread extends Thread{@Overridepublic void run() { try{System.out.println("开始下载文件");//此处让线程DownloadThread休眠5秒中,模拟文件的耗时过程Thread.sleep(5000);System.out.println("文件下载完成");//文件下载完成后更新UIMainActivity.this.statusTextView.setText("文件下载完成"); }catch (InterruptedException e){e.printStackTrace(); }} }}上面的代码演示了单击”下载“按钮后会启动一个新的线程去执行实际的下载操作,执行完毕后更新UI界面。但是在实际运行到代码MainActivity.this.statusTextView.setText(“文件下载完成”)时,会报错如下,系统崩溃退出:
package ispring.com.testhandler;import android.app.Activity;import android.os.Bundle;import android.os.Handler;import android.view.View;import android.widget.Button;import android.widget.TextView;public class MainActivity extends Activity implements Button.OnClickListener { private TextView statusTextView = null; //uiHandler在主线程中创建,所以自动绑定主线程 private Handler uiHandler = new Handler(); @Override protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);statusTextView = (TextView)findViewById(R.id.statusTextView);Button btnDownload = (Button)findViewById(R.id.btnDownload);btnDownload.setOnClickListener(this);System.out.println("Main thread id " + Thread.currentThread().getId()); } @Override public void onClick(View v) {DownloadThread downloadThread = new DownloadThread();downloadThread.start(); } class DownloadThread extends Thread{@Overridepublic void run() { try{System.out.println("DownloadThread id " + Thread.currentThread().getId());System.out.println("开始下载文件");//此处让线程DownloadThread休眠5秒中,模拟文件的耗时过程Thread.sleep(5000);System.out.println("文件下载完成");//文件下载完成后更新UIRunnable runnable = new Runnable() { @Override public void run() {System.out.println("Runnable thread id " + Thread.currentThread().getId());MainActivity.this.statusTextView.setText("文件下载完成"); }};uiHandler.post(runnable); }catch (InterruptedException e){e.printStackTrace(); }} }}我们在Activity中创建了一个Handler成员变量uiHandler,Handler有个特点,在执行new Handler()的时候,默认情况下Handler会绑定当前代码执行的线程,我们在主线程中实例化了uiHandler,所以uiHandler就自动绑定了主线程,即UI线程。当我们在DownloadThread中执行完耗时代码后,我们将一个Runnable对象通过post方法传入到了Handler中,Handler会在合适的时候让主线程执行Runnable中的代码,这样Runnable就在主线程中执行了,从而正确更新了主线程中的UI。以下是输出结果:
通过输出结果可以看出,Runnable中的代码所执行的线程ID与DownloadThread的线程ID不同,而与主线程的线程ID相同,因此我们也由此看出在执行了Handler.post(Runnable)这句代码之后,运行Runnable代码的线程与Handler所绑定的线程是一致的,而与执行Handler.post(Runnable)这句代码的线程(DownloadThread)无关。
b. 使用sendMessage方法,代码如下:
package ispring.com.testhandler;import android.app.Activity;import android.os.Bundle;import android.os.Handler;import android.os.Message;import android.view.View;import android.widget.Button;import android.widget.TextView;public class MainActivity extends Activity implements Button.OnClickListener { private TextView statusTextView = null; //uiHandler在主线程中创建,所以自动绑定主线程 private Handler uiHandler = new Handler(){@Overridepublic void handleMessage(Message msg) { switch (msg.what){case 1: System.out.println("handleMessage thread id " + Thread.currentThread().getId()); System.out.println("msg.arg1:" + msg.arg1); System.out.println("msg.arg2:" + msg.arg2); MainActivity.this.statusTextView.setText("文件下载完成"); break; }} }; @Override protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);statusTextView = (TextView)findViewById(R.id.statusTextView);Button btnDownload = (Button)findViewById(R.id.btnDownload);btnDownload.setOnClickListener(this);System.out.println("Main thread id " + Thread.currentThread().getId()); } @Override public void onClick(View v) {DownloadThread downloadThread = new DownloadThread();downloadThread.start(); } class DownloadThread extends Thread{@Overridepublic void run() { try{System.out.println("DownloadThread id " + Thread.currentThread().getId());System.out.println("开始下载文件");//此处让线程DownloadThread休眠5秒中,模拟文件的耗时过程Thread.sleep(5000);System.out.println("文件下载完成");//文件下载完成后更新UIMessage msg = new Message();//虽然Message的构造函数式public的,我们也可以通过以下两种方式通过循环对象获取Message//msg = Message.obtain(uiHandler);//msg = uiHandler.obtainMessage();//what是我们自定义的一个Message的识别码,以便于在Handler的handleMessage方法中根据what识别//出不同的Message,以便我们做出不同的处理操作msg.what = 1;//我们可以通过arg1和arg2给Message传入简单的数据msg.arg1 = 123;msg.arg2 = 321;//我们也可以通过给obj赋值Object类型传递向Message传入任意数据//msg.obj = null;//我们还可以通过setData方法和getData方法向Message中写入和读取Bundle类型的数据//msg.setData(null);//Bundle data = msg.getData();//将该Message发送给对应的HandleruiHandler.sendMessage(msg); }catch (InterruptedException e){e.printStackTrace(); }} }}通过Message与Handler进行通信的步骤是:
由上我们可以看出,执行handleMessage的线程与创建Handler的线程是同一线程,在本示例中都是主线程。执行handleMessage的线程与执行uiHandler.sendMessage(msg)的线程没有关系。
本文主要是对Android中Handler的作用于如何使用进行了初步介绍,如果大家想了解Handler的内部实现原理,可以参见下一篇博文《深入源码解析Android中的Handler,Message,MessageQueue,Looper》。