昨晚任玉剛老師在時(shí)光課堂有一場(chǎng)直播秀刊侯,里面提到了這么一個(gè)問題:
在OnCreate里啟動(dòng)一個(gè)子線程更新UI為什么程序沒有拋出異常?
這個(gè)問題之前也有了解過锉走,但這一提起來我似乎又忘記了為什么滨彻,所以記錄一下。
代碼如下:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTxt = (TextView) findViewById(R.id.txt);
new Thread(new Runnable() {
@Override
public void run() {
mTxt.setText("UI Test!");
}
}).start();
按照開發(fā)規(guī)范來說挪蹭,是不建議在子線程更新UI的亭饵,這必然會(huì)引起程序閃退,但是上面在OnCreate里啟動(dòng)子線程更新UI卻沒有問題梁厉,也不拋異常辜羊。好,那我們讓子線程休眠200ms再更改UI词顾。
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTxt = (TextView) findViewById(R.id.txt);
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
mTxt.setText("UI Test!");
}
}).start();
}
這時(shí)候你會(huì)發(fā)現(xiàn)程序起不來了八秃,并且控制臺(tái)里打印出了我們非常熟悉的異常信息
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:7680)
at android.view.ViewRootImpl.requestLayout(ViewRootImpl.java:1223)
簡單解釋一下,更新UI的時(shí)候系統(tǒng)會(huì)去檢查當(dāng)前更新UI的線程是否屬于主線程肉盹,而該檢查操作是在ViewRootImpl.checkThread()中完成的昔驱,因?yàn)樵贠nCreate的時(shí)候ViewRootImpl還沒有被創(chuàng)建,所以這時(shí)候就無法檢查該更新UI的線程是否屬于主線程上忍,自然程序不會(huì)報(bào)錯(cuò)骤肛。休眠200ms后,程序會(huì)報(bào)錯(cuò)是因?yàn)檫@時(shí)候ViewRootImpl已經(jīng)初始化完成了(ViewRootImpl是在OnResume回調(diào)后完成創(chuàng)建的)窍蓝,這時(shí)候再去更新UI腋颠,自然就會(huì)報(bào)錯(cuò)。
為什么Android的設(shè)計(jì)子線程不能更新UI呢吓笙,這自然是有一定道理的淑玫。UI是用戶和程序交互的窗口,任何涉及到UI的操作更新都是要及時(shí)響應(yīng)的观蓄,如果多個(gè)線程可以同時(shí)訪問UI的話混移,這肯定是不安全的,加個(gè)線程鎖可以不可以呢侮穿?可以歌径,但是這太影響效率了,所以Google的工程師這么設(shè)計(jì)沒毛病亲茅。
如果要更加了解這個(gè)問題回铛,那就需要跟進(jìn)源碼去分析了狗准,這里就不展開了。