标题:在Android中使用Handler和Thread线程执行后台操作

作者:CrazyPebble

时间:2011年3月23日

声明:此文在参考《解密Google Android》一书 和 Android视频教程(www.mars-droid.com)。文中存在错误之处,还请各位批评指正。若转载本文,请指明转载出处:http://www.cnblogs.com。


  大家都知道,在PC上的应用程序当需要进行一些复杂的数据操作,但不需要界面UI的时候,我们会为应用程序专门写一个线程去执行这些复杂的数据操作。通过线程,可以执行例如:数据处理、数据下载等比较耗时的操作,同时对用户的界面不会产生影响。在Android应用程序开发中,同样会遇到这样的问题。当我们需要访问网络,从网上下载数据并显示在我们的UI上时,就会启动后台线程去下载数据,下载线程执行完成后将结果返回给主用户界面线程。

  对于线程的控制,我们将介绍一个Handler类,使用该类可以对运行在不同线程中的多个任务进行排队,并使用Message和Runnable对象安排这些任务。在javadoc中,对Handler是这样解释的:Handler可以发送和处理消息对象或Runnable对象,这些消息对象和Runnable对象与一个线程相关联。每个Handler的实例都关联了一个线程和线程的消息队列。当创建了一个Handler对象时,一个线程或消息队列同时也被创建,该Handler对象将发送和处理这些消息或Runnable对象。

  下面有几种对Handler对象的构造方法需要了解一下:

a、如果new一个无参构造函数的Handler对象,那么这个Handler将自动与当前运行线程相关联,也就是说这个Handler将与当前运行的线程使用同一个消息队列,并且可以处理该队列中的消息。

              private         Handler handler         =                 new         Handler();      

  我们做这样一个实验,在主用户界面中创建一个带有无参构造函数的Handler对象,该Handler对象向消息队列推送一个Runnable对象,在Runnable对象的run函数中打印当前线程Id,我们比较主用户界面线程ID和Runnable线程ID是否相同。具体代码如下:

HandlerTest01
                  public                     class           HandlerTest01           extends           Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super .onCreate(savedInstanceState);
setContentView(R.layout.main);

System.out.println(
" Activity ---> " + Thread.currentThread().getId());
handler.post(r);
}

private Handler handler = new Handler();
private Runnable r = new Runnable() {
@Override
public void run() {
try {
Thread.sleep(
2000 );
}
catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(
" Runnalbe ---> " + Thread.currentThread().getId());
}
};
}

  通过这个例子的输出可以发现,Runnable对象和主用户界面线程的ID是相同。在这个例子中,我们直接利用handler对象post了一个runnable对象,相当于直接调用了Runnable对象的run函数,也就说没有经过start函数调用run(),那么就不会创建一个新线程,而是在原有线程内部直接调用run()方法,因此输出的线程Id是相同的。

b、如果new一个带参构造函数的Handler对象,那么这个Handler对象将与参数所表示的Looper相关联。注意:此时线程类应该是一个特殊类HandlerThread类,一个Looper类的Thread类,它继承自Thread类。

              HandlerThread handlerthread         =                 new         HandlerThread(        "        MyThread        "        );                    handlerthread.start();
private MyHandler handler = new MyHandler(handlerthread.getLooper());

class MyHandler extends Handler {
public MyHandler() {

}

public MyHandler(Looper looper) {
super (looper);
}
}

  下面这个例子,将介绍如何开启一个新的线程,并通过Handler处理消息。

HandlerTest02.java
                  public                     class           HandlerTest02           extends           Activity {

private MyHandler myhandler = null ;
@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super .onCreate(savedInstanceState);
this .setContentView(R.layout.main);
System.out.println(
" Activity ---> " + Thread.currentThread().getId());

// 生成一个HandlerThread对象,使用Looper来处理消息队列
HandlerThread thread = new HandlerThread( " MyThread " );
// 必须启动这个线程
thread.start();
// 将一个线程绑定到Handler对象上,则该Handler对象就可以处理线程的消息队列
myhandler = new MyHandler(thread.getLooper());
// 从Handler中获取消息对象
Message msg = myhandler.obtainMessage();
// 将msg对象发送给目标对象Handler
msg.sendToTarget();
}

class MyHandler extends Handler {
public MyHandler() {

}

// 带有参数的构造函数
public MyHandler(Looper looper) {
super (looper);
}

@Override
public void handleMessage(Message msg) {
System.out.println(
" MyHandler ---> " + Thread.currentThread().getId());
}
}
}

  根据这个例子返回的结果,可以看出,新线程Id与主用户界面的线程Id不同。由于我们调用了thread.start()方法,真正的创建了一个新线程,与原来的线程处于不同的线程上下文中,因此打印输出的线程Id是不同的。

c、如果需要Handler对象去处理消息,那么就要重载Handler类的handleMessage函数。

              private         Handler handler         =                 new         Handler() {

@Override
public void handleMessage(Message msg) {
// TODO : Handle the msg
// Usually we update UI here.
}
}

  注意到注释部分,我们通常在handleMessage中处理更新UI界面的操作。

  前面介绍了Handler类的基本使用,但是还是没有涉及到Thread类。要想实现在后台重新开启一个新的线程,通过该线程执行一些费时的操作,我们也使用Thread类来完成这个功能。下面我们先给出一个使用Thread类的例子程序。

ThreadTest.java
                  public                     class           ThreadTest           extends           Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super .onCreate(savedInstanceState);
this .setContentView(R.layout.main);
System.out.println(
" Activity ---> " + Thread.currentThread().getId());

Thread thread
= new Thread(r);
thread.start();
try {
Thread.currentThread().sleep(
1000 );
}
catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
thread.stop();
}

Runnable r
= new Runnable() {
@Override
public void run() {
System.out.println(
" Runnable ---> " + Thread.currentThread().getId());
}
};
}

  这个程序执行的结果如下。新线程在创建对象时,传入了Runnable类的一个对象,在Runnable对象中重载了run()方法去执行耗时的操作;新的线程实例执行了start方法,开启了一个新的线程执行Runnable的run方法。


  上面这些就是我现在接触到执行线程的方法,在线程中,可以完成我们所需要的操作(比如:下载,处理数据,检测网络状态等),使其与UI界面分离,那么UI界面不会因为耗时操作导致界面被阻塞。

  在《解密Google Android》一书中,发现了这样一个启动线程的模型。利用该模型,我们可以把一些耗时的操作放到doStuff方法中去执行,同时在updateUIHere方法中进行更新UI界面的操作,就可以完成一个线程所需要的功能。其他的说明写在注释部分了。

              Handler myHandler         =                 new         Handler() {
public void handleMessage(Message msg) {
updateUIHere();
}
}

new Thread() {
public void run() {
doStuff();
// 执行耗时操作
Message msg = myHandler.obtainMessage();
Bundle b
= new Bundle();
b.putString(
" key " , " value " );
m.setData(b);
// 向消息中添加数据
myHandler.sendMessage(m); // 向Handler发送消息,更新UI
}
}.start();


更多相关文章

  1. Android webview与js 交换JSON对象数据
  2. Android消息机制——Handler、Looper、MessageQueue
  3. Android的线程模型
  4. Android 消息提示框:五种Toast详解
  5. Android 消息机制之Message
  6. Android的消息处理机制(深入源码)
  7. Android 多线程AsyncTask详解
  8. android 上 webkit js 扩展之全局本地对象实现步骤

随机推荐

  1. android 控件的使用 备注
  2. Android(安卓)UI线程和非UI线程
  3. 布局文件中的笔记
  4. Android(安卓)系统中WatchDog 日志分析
  5. android的第一天学习
  6. android 中文 api (43) ―― Chronometer
  7. Android(安卓)TextView跑马灯效果
  8. 【转】善用Android预定义样式来为我们的
  9. Android(安卓)4.4 SD卡文件读写变化
  10. 在android studio 2.1 实现简单的ndk