Android 后台任务(二)Thread And Handler
翻译自:http://blog.stylingandroid.com/archives/833
转载请注明:http://blog.csdn.net/liaoqianchuan00/article/details/23949545
在之前的文章中我们讲解了为什么我们尽量不要在UI线程中处理一些很慢的事情来阻塞UI线程。这篇文章我们关注如何来做。
最简单的处理办法就是使用java线程。将任务处理放在一个线程中是这样的简单。
new Thread( new Runnable()
{
@Override
public void run()
{
// Dosomething
}
} ).start();
将任务放在Runnable中得run方法中,这样就能在一个新线程中执行,并且当任务完成后自动结束线程。
我们很快就会遇到问题了。比如下面这个例子:
new Thread( new Runnable()
{
@Override
public void run()
{
// Dosomething
TextViewtextView =
(TextView)findViewById( R.id.textview );
textView.setText("Hello World" );
}
} ).start();
我们在线程中处理一个耗时操作,在耗时操作结束时,我们改变TextView中的文字。这看起来很好,但是当我们运行他得时候出现了runtime exception:
E/AndroidRuntime(9129): android.view.ViewRootImpl$CalledFromWrongThreadException:Only the original thread that created a view hierarchy can touch its views.
这个log提示我们如果我们想要去修改一些View(比如说我们例子中得TextView),需要在创建这个View的线程中去做----即我们的老朋友UI线程。这里看起来由点困惑:我们在UI线程之外处理了一些事情,但是我们又需要去在UI线程更新我们的UI。这好像是告诉我们要在UI线程中处理所有的事情,但是我们前面已经讨论了,这不是个好办法。
幸运的是,已经有机制让我们回到UI线程去执行代码了。Activity有一个方法叫做runOnUiThread,这可以让我们在UI线程中执行代码:
new Thread( new Runnable()
{
@Override
public void run()
{
// Dosomething
finalTextView textView =
(TextView)findViewById( R.id.textview );
runOnUiThread(new Runnable()
{
@Override
publicvoid run()
{
textView.setText("Hello World" );
}
} );
}
} ).start();
在Android中,我们还有一种方法:
new Thread( new Runnable()
{
@Override
public void run()
{
finalTextView textView =
(TextView)findViewById( R.id.textview );
textView.post(new Runnable()
{
@Override
publicvoid run()
{
textView.setText("Hello World" );
}
} );
}
} ).start();
这里我们使用post方法也可以得到和runOnUiThread相同的结果,这个方式一般用在我们可以得到TextView,但是不能得到Activity。每个View都有一个在UI线程中处理的messagequeue。Post允许我们向这个message queue发送一个新的message。
Message queue机制是通过Handler来实现的,这也就引入了我们要讲的另外一种处理这种耗时操作然后更新UI的方法。在UI线程中创建一个我们自己的Handler实例,然后我们可以向这个Handler发送message,这些message会在创建这个Handler的线程中执行:
final Handler handler = new Handler();
new Thread( new Runnable()
{
@Override
public void run()
{
// Dosomething
finalTextView textView =
(TextView)findViewById( R.id.textview );
handler.post(new Runnable()
{
@Override
publicvoid run()
{
textView.setText("Hello World" );
}
} );
}
} ).start();
注意:Handler在构造的时候会指定一个looper对象,若未指定,就用本线程的。所以当我们在UI线程中创建handler的时候,looper其实是主线程的looper,而一般的工作线程其实没有looper,所以如果我们要在工作线程中去newHandler,我们需要在线程中加入的looper。
而如果我们要不时的更新我们的UI,比如更新ProgressBar,这就意味着我们要不断的切换到UI线程中去更新UI,这会让我们的代码很难看。Android提供了AsyncTask来完成这个工作,我们下节将会讲到这个的用法。