在默认情况下,Handler实际上和调用它的Activity是处于同一个线程的。但是初学android的朋友也许对这个道理只有一个模糊的概念认识,为什么它们在同一个线程中呢?怎么才能具体体现这一点呢。
下面我将通过一个实例来直观演示这个道理。
在附上源代码之前首先介绍Thread类的两个方法,
Thread.currentThread().getId()和Thread.currentThread().getName(),这两方法分别获取当前线程的标识(identifier)和名称(Name)。
如果两个线程的标识和名称均相同,那么可以认为这两个线程是同一个线程。
在eclipse下建立一个android项目,名称为HandlerTwo,因为这个项目是一个比较简单的小程序,布局文件采用默认的即可,故不附带main.xml的内容,下面只附上Activity的源码:
package com.android.wyt.Handler;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
public class HandlerTwoActivity extends Activity {
/** Called when the activity is first created. */
Handler handler = new Handler();
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 在设置布局文件之前先调用post方法,
// 表示在执行完线程之后才会显示布局文件中的内容,而线程中又设置了休眠10秒钟,
// 所以最终效果为,先显示应用程序主界面,等待10秒钟之后才显示布局文件中的内容
// 现将下面的代码注释掉
// handler.post(r);
// 如果将下面的两行代码注释掉则将上面的handler.post(r);
// 解注释。注意:这个是测试Handler和调用它的Activity是不是在
// 同一个线程中
Thread t = new Thread(r);
t.start();
setContentView(R.layout.main);
System.out.println("In onCreate():activity id--->"
+ Thread.currentThread().getId());
System.out.println("In onCreate():activity name--->"
+ Thread.currentThread().getName());
}
Runnable r = new Runnable() {
public void run() {
try {
Thread.sleep(16000); // 让线程休眠16秒
} catch (InterruptedException e) {
e.printStackTrace();
}
// 输出当前线程的id和name
// 如果这里输出的线程id、name与上面onCreate()方法中输出的线程id、name相同的话,
// 那么则表示,他们使用的是同一个线程
System.out.println("in Runnable():runnable_id--->"
+ Thread.currentThread().getId());
System.out.println("in Runnable():runnable_name--->"
+ Thread.currentThread().getName());
}
};
}
上面的程序所表达出来的意思就是:如果run()方法中输出的线程id和name与OnCreate()中输入的线程的id和name均相同的话,则可以认为Handler与调用它的Activity处于同一个线程当中,即UI主线程。
程序运行的结果是:
安卓虚拟器开始只显示主界面,不显示布局文件中的内容,
等过了16秒后才显示布局文件的内容。
这是因为程序中setContentView()在handler.post(r); 之后,而在后来被调用的run()方法中线程休眠了16秒,这从一个方面体现了Handler与调用它的Activity处于同一个线程,因为在同一个线程内只能是串行的,按照顺序来执行。随后在eclispe的LogCat窗口中可以看到System.out的输出。
从上图中的LogCat窗口的显示可以看出在onCreate方法额Runable()中的run()方法中的线程的ID和Name均相同,证明了Handler和Activity都在主线程中。之前提到的,在虚拟器上布局文件的内容延迟8秒钟才显示也体现了它们是在同一个线程中。
再说一点,也可以利用Thread的这两个方法来验证子线程与主线程或者Handler不在同一个线程(这是显然易见的),从下面的例子也可以从反方向找到Handler与主线程在同一个线程中的例证。
不用重新新建项目,只需要将上面代码中的
handler.post(r);
这一行代码
替换成
// 现将下面的代码注释掉
//handler.post(r);
// 如果将下面的两行代码注释掉则将上面的handler.post(r);
// 解注释。注意:这个是测试Handler和调用它的Activity是不是在
// 同一个线程中
Thread t = new Thread(r);
t.start();
而且注意程序刚刚运行起来的时候虚拟器有没有立刻显示布局文件中的内容。
程序刚刚运行起来后,可以看到布局文件很快就显示出来了
在LogCat窗口中可以看到():(注:后两行要等16秒左右的时候才出来,因为之前子线程休眠了16秒才执行到System.out)
可以发现ID和Name均不相同,这两个线程不是同一个线程。
这个道理非常容易明白,因为程序中
Thread t = new Thread(r);
t.start();
创建并启动了一个子线程,与主线程不是同一个线程,虽然这两行语句字setContentView()之前,但是因为不同线程之间是可以并发执行的,所以主线程可以通过执行setContentView()来把布局文件中的内容显示出来而不必等到子线程执行完全才执行setContentView().这从反方向证明了Handler的确和调用它的Activity都处在主线程当中。