学习Android,有一条程序设计中比较重要的原则,就是永远不要阻塞UI线程。(以下观点是个人看法,不到之处望大家指出互相学习)
一、什么叫UI线程的阻塞?
UI线程的阻塞是指,由于在activity所在的线程中执行耗时操作,导致UI线程无法正常的运作。
二、UI线程的阻塞会导致什么?
会导致程序发生假死状态,导致用户提前关闭程序,丧失用户数量。或者在过了5秒之后Android系统出来干预,弹出提示框类似”XX程序无响应,是否关闭?“这类的提示框,极大地降低用户的体验。
三、如何防止UI线程的阻塞?
当然是将耗时操作放到其他的线程中。但是话虽然这么说,但是关于这个还值得讨论一下,也是本篇重点。
先让我们看看官方的两条规则:
1、不要阻塞UI线程
2、不要在UI线程之外的线程对视图(view)进行任何的设置。
第一条没什么可深究的,主要是在第二条。如果你违反了第二条规则,程序就会抛出异常。也许就这个问题让很多人都很蛋疼。那你肯定会问,如果我想在UI线程之外的执行耗时操作的线程中对视图进行设置怎么办?这两条规则那不就矛盾了吗?
官方也意识到了这一点,所以给我们提供了具体的解决这个问题的两个方法。
方法一、使用view.post()方法看下面的示例代码。
bu.setOnClickListener(new OnClickListener() {
@Override
public void onClick(final View view) {
new Thread(new Runnable() {
@Override
public void run() {
//模拟耗时操作
try {
Thread.sleep(8000);
} catch (InterruptedException e) {
e.printStackTrace();
}
view.post(
new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
//模拟对view的设置操作
view.setBackgroundColor(Color.BLACK);
}
}
);
}
}).start();
}
});
}
此方法是将对button的背景颜色的设置放到了post()方法中。此时就解决了前面提到的“矛盾”问题。具体的原理就是在UI线程和耗时线程之间放一个任务队列,UI线程在执行的过程之中会不断地轮询这个任务队列,如果里面有任务就拿出来执行(是在主线程里面执行,也即是说上面例子中对button的设置最终是在主线程里面执行的)。当然如果耗时线程里面有任务,就抛出到消息队列里面。这就形成了一个”生产——消费“模型。从而解决了这个问题。
但是你会发现虽然问题解决了,但是新的问题也就随之产生。这段代码可读性特别差,在程序维护方面做得很不好。别急还有第二个方法。
方法二、应用AsyncTask.废话不多说直接上代码
btn=(Button) findViewById(R.id.button1);
btn.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
new MyAsyn().execute();
}
});
}
//睡眠8s,然后传出一个“done”到onPostExecute,在这里设置按钮的名字为“done”
private class MyAsyn extends AsyncTask<String, Void, String>{
protected String doInBackground(String... s) {
// TODO Auto-generated method stub
try {
Thread.sleep(8000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return "done";
}
protected void onPostExecute(String s) {
btn.setText(s);
}
}
两种犯法其实原理是一样的,都是将耗时操作和对View的设置分开,将耗时操作放在doInBackground,将对view的设置放在onPostExecute。