当应用程序启动,创建了一个叫“main”的线程,用于管理UI相关,又叫UI线程。其他线程叫工作线程(Work Thread)。
Single Thread Model
- 一个组件的创建并不会新建一个线程,他们的创建都在UI线程中进行,包括他们的回调方法,如onKeyDown()。
- 当在UI线程中进行某些耗时的操作时,将会阻塞UI线程,一般阻塞超过5秒就会显示一个ANR对话框。
-
UI线程是非线程安全的,所以,不能在工作线程中操作UI元素。
两个原则
- Do not block the UI thread (不要阻塞UI线程)
- Do not access the Android UI toolkit from outside the UI thread (不要在工作线程中操作UI元素)
在工作线程更新UI方法
- Activity.runOnUiThread(Runnable)
-
Handler
- sendMessage(Message)
- post(Runnable)
-
AsyncTask
- execute()
- doInBackground()
- onPostExecute()
例子程序
-
HandlerActivity01
- 在工作线程中进行UI操作。
-
HandlerActivity02
- Handler的两个重要方法:sendMessage和post。
-
HandlerActivity03
- 官方推荐最佳方法。
HandlerActivity01主要代码:
Java代码-
btnEnd.setOnClickListener(new
View.OnClickListener() { -
-
@Override -
public void onClick(View v) { -
new Thread(new Runnable() { -
-
@Override -
public void run() -
{ -
//在新建的线程(工作线程)中改变Button的文字 -
btnEnd.setText("Text Changed );in Sub Thread" -
} -
-
}).start(); -
} -
});
这是一种错误的做法,运行程序,会报错误: Java代码-
android.view.ViewRootImpl$CalledFromWrongThreadExc
eption: Only the original thread that created a view hierarchy can touch its views.
HandlerActivity02主要代码: Java代码-
public
class HandlerActivity02 extends Activity -
{
-
-
private int title = 0; -
Button btnStart,btnEnd; -
-
private Handler mHandler = new Handler() -
{ -
public void handleMessage(Message msg) -
{ -
//更新UI -
switch (msg.what) -
{ -
case 1: -
updateTitle(); -
break; -
} -
}; -
}; -
-
public void onCreate(Bundle savedInstanceState) -
{ -
super.onCreate(savedInstanceState); -
setContentView(R.layout.main); -
-
btnStart = (Button)findViewById(R.id.start); -
btnEnd = (Button)findViewById(R.id.end); -
//新启动一个线程,进行耗时操作 -
Timer timer = new Timer(); -
//每六秒执行一次MyTask的run方法 -
timer.scheduleAtFixedRate(new MyTask(this), 1, 6000); -
} -
-
private class MyTask extends TimerTask -
{ -
private Activity context; -
MyTask(Activity context) -
{ -
this.context = context; -
} -
-
@Override -
public void run() -
{ -
//耗时操作略.... -
-
//更新UI方法 1 -
Message message = new Message(); -
message.what = 1; -
mHandler.sendMessage(message); -
-
//更新UI方法 2 -
mHandler.post(updateThread); -
-
//更新UI方法 3 -
context.runOnUiThread(updateThread); -
} -
} -
-
public void updateTitle() -
{ -
setTitle("Welcome to Mr Wei's blog " + title); -
title++; -
} -
-
Runnable updateThread = new Runnable() -
{ -
-
@Override -
public void run() -
{ -
//更新UI -
btnStart.setText(String.valueOf(title)); -
btnEnd.setText(String.valueOf(title)); -
} -
-
}; -
}
这里有个容易出错的地方,在更新UI方法2和3中,我们传入的参数是一个Runnable对象,一般认为这就会启动一个新的线程,而且常有人在这个Runnable对象的run方法中进行耗时操作。看过这块的源码就会知道,其实,android只是调用了这个Runnable对象的run方法而已,并没有启动新的线程,而且我们不应该在run方法中进行耗时操作,因为这个run方法最终是在UI线程里面执行的。也就是说,run方法里面只应该放更新UI的代码,handleMessage方法也一样。 如果你要看这部分源代码的话,相信这个图对你会有帮助:
HandlerActivity03主要代码:
Java代码-
public
class HandlerActivity03 extends Activity -
{
-
Button btnStart; -
@Override -
protected void onCreate(Bundle savedInstanceState) -
{ -
// TODO Auto-generated method stub -
super.onCreate(savedInstanceState); -
setContentView(R.layout.main); -
-
btnStart = (Button)findViewById(R.id.start); -
btnStart.setOnClickListener(new View.OnClickListener() { -
-
@Override -
public void onClick(View v) { -
//开始执行AsyncTask,并传入某些数据 -
new LongTimeTask().execute("New Text" ); -
} -
}); -
} -
-
private class LongTimeTask extends AsyncTask -
{ -
-
@Override -
protected String doInBackground(String... params) -
{ -
try -
{ -
//线程睡眠5秒,模拟耗时操作,这里面的内容Android系统会自动为你启动一个新的线程执行 -
Thread.sleep(5000); -
} -
catch (InterruptedException e) -
{ -
e.printStackTrace(); -
} -
return params[0]; -
} -
-
@Override -
protected void onPostExecute(String result) -
{ -
//更新UI的操作,这里面的内容是在UI线程里面执行的 -
btnStart.setText(result); -
} -
-
} -
-
}
这个方法确实挺好,因为它为你封装了许多操作,你只需要记住在doInBackground方法中写耗时操作的代码,在onPostExecute方法中写更新UI的方法就行了。
作者:chaoyu168 发表于2017/2/4 9:25:25 原文链接
阅读:27 评论:0 查看评论