線程
當一個應用被啓動,系統創建一個執行線程,叫做"main"。這個線程是十分重要的,因爲它主管向用戶界面控件派發事件。其中包含繪圖事件。它也是你的應用與界面工具包(android.widget和android.view包中的組件)交互的地方。於是main線程也被稱爲界面線程。
系統不會爲每個組件的實例分別創建線程。所有運行於一個進程的組件都在界面線程中被實例化,並且系統對每個組件的調用都在這個線程中派發。 結果,響應系統調用的方法(比如報告用戶動作的onKeyDown()或一個生命週期回調方法)永遠在界面線程中進程。
例如,當用戶觸摸屏幕上的一個按鈕時,你的應用的界面線程把觸摸事件派發給控件,然後控件設置它的按下狀態再向事件隊列發出一個自己界面變得無效的請求,界面線程從隊列中取出這個請求並通知這個控件重繪它自己。
當你的應用在響應用戶交互時需執行大量運算時,這種單線程的模式會帶來低性能,除非你能正確的優化你的程序。特別的,如果所有事情都在界面線程中發生,執行比如網絡連接或數據庫請求這樣的耗時操作,將會阻止整個界面的響應。當線程被阻塞時,就不能派發事件了,包括繪圖事件。從用戶的角度看,程序反應太慢了。甚至更糟的是,如果界面線程被阻塞幾秒鐘(大5秒鐘吧),用戶就戶抱怨說程序沒反應了,用戶可能因而退出並刪掉你的應用。
此外,Andoid界面不是線程安全的。所以你絕不能在一個工作線程中操作你的界面—你只能在界面線程中管理的你的界面。所以,對於單線程模式有兩個簡單的規則:
1不要阻塞界面線程
2不要在界面線程之外操作界面。
工作線程
由於上述的單線程模式,不要阻塞你的界面線程以使你的應用的界面保持響應是非常重要的,那麼如果你有不能很快完成的任務,你應把它們放在另一個線程中執行(後臺線程或工作線程)。
例如,下面是的代碼是響應click事件,在另外一個線程中下載一個圖片並在一個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();
- }
第一眼,這看起來能很好的工作,因爲它創建了一個新線程來進行網絡操作。然而它違反了第二條規則:不要在界面線程之外操作界面—它簡單的在工作線程中修改了ImageView。這會導至未定義的異常出現,並且難以調試追蹤。
爲了能改正這個問題,Android提供了很多從其它線程來操作界面的方法。下面是可用的方法們:
1 Activity.runOnUiThread(Runnable)
2 View.post(Runnable)
3 View.postDelayed(Runnable,long)
例如,你可以用View.post(Runnable)來修正上面的問題:
- public void onClick(View v) {
- new Thread(new Runnable() {
- public void run() {
- final Bitmap bitmap = loadImageFromNetwork("http://example.com/image.png");
- mImageView.post(new Runnable() {
- public void run() {
- mImageView.setImageBitmap(bitmap);
- }
- });
- }
- }).start();
- }
現在這個實現終於是線程安全的了:網絡操作在另一個線程中並且ImageView在界面線程中改變。