UI线程及Android的单线程模型原则
当应用启动,系统会创建一个主线程。
这个主线程负责向UI组件分发事件(包括绘制事件),也是在这个主线程里,你的应用和Android的UI组件发生交互。
所以主线程也叫UI线程。
系统不会为每个组件单独创建线程,在同一个进程里的UI组件都会在UI线程里实例化,系统对每一个组件的调用都是从UI线程分发出去的。结果就是,响应系统回调的方法永远都是在UI线程里进行的。
当App在做一些比较繁重的操作的时候,比如网络请求、数据库操作等相关操作。如果这些工作都在UI线程里进行,就会阻塞UI线程,导致时间停止分发。对于用户来说,应用看起来就像卡住了,更坏的情况是,如果UI线程阻塞时间过长,用户就会看到应用无响应提示。
另外,Android UI toolkit并不是线程安全的,所以你不能从非UI线程来操作UI组件。必须把所有的UI操作放在UI线程里。
所以android单线程模型有两条原则:
1. 不要阻塞UI线程。
2. 不要再UI线程外操作UI组件。
使用worker线程
根据单线程模型的两条原则,首先,要保证应用的响应性,不能阻塞UI线程,所以当你的操作十分耗时,你应该把他们放进另外的单独线程中(叫做background或者叫worker线程)。
比如点击按钮后,下载一个图片然后在ImageView中展示:
public void onClick(View v) {
new Thread(new Runnable() {
public void run() {
Bitmap b = loadImageFromNetwork("http://example.com/image.png");
mImageView.setImageBitmap(b);
}
}).start();
}
这段代码用新的线程来处理网络操作,但是它违反了第二条原则:不能从非UI线程访问UI组件。
为了解决这个问题,Android提供了一些方法,从其他线程访问UI线程:
- Activity.runOnUiThread(Runnable)
这个函数是Activity的成员函数,它的源码为:
public final void runOnUiThread(Runnable action) {
if(Thread.currentThread() != mUiThread) {
mHandler.post(action);
} else {
action.run();
}
}
利用Activity.runOnUiThread(Runnable)把更新ui的代码创建在Runnable中,然后在需要更新ui时,把这个Runnable对象传给Activity.runOnUiThread(Runnable)。 Runnable对像就能在ui程序中被调用。如果当前线程是UI线程,那么行动是立即执行。如果当前线程不是UI线程,操作是发布到事件队列的UI线程。
new Thread(new Runnable() {
@Override
public void run() {
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(MainActivity.this, "hah", Toast.LENGTH_SHORT).show();
}
});
}
}).start();
- View.post(Runnable)
在post(Runnable action)方法里,View获得当前线程(即UI线程)的Handler,然后将action对象post到Handler里。在Handler里,它将传递过来的action对象包装成一个Message(Message的callback为action),然后将其投入UI线程的消息循环中。在Handler再次处理该Message时,有一条分支(未解释的那条)就是为它所设,直接调用runnable的run方法。而此时,已经路由到UI线程里,因此,我们可以毫无顾虑的来更新UI。
由于不是在新的线程中使用,所以千万别做复杂的计算逻辑。
参考自清沁
public void onClick(View v) {
new Thread(new Runnable() {
public void run() {
final Bitmap b = loadImageFromNetwork();
mImageView.post(new Runnable() {
public void run() {
mImageView.setImageBitmap(b);
}
});
}
}).start();
}
- View.postDelayed(Runnable, long)
但是,当操作变得复杂的时候,这种代码会变得非常复杂,为了处理非UI线程和UI线程之间更加复杂的交互,可以考虑在worker线程中使用一个Handler,来处理UI线程中传来的消息。