Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> android中子線程更新UI的方式淺析

android中子線程更新UI的方式淺析

編輯:關於Android編程

一、為何寫作此文

??你是不是經常看到很多書籍中說:不能在子線程中操作ui,不然會報錯。你是不是也遇到了如下的疑惑(見下面的代碼):

@Override  
    protected void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.activity_main);  
        tv = (TextView) findViewById(R.id.tv);  
        Thread.currentThread().setName("UIThread");  
        new LooperThread().start();  
    }  

    private class LooperThread extends Thread {  

        @Override  
        public void run() {  
            Thread.currentThread().setName("OtherThread");  
            tv.setText("other thread");  
        }  
    }  

??上面確實在子線程中操作ui了,但是他並不會報錯,為什麼呢?這不是跟書上的說法剛好相悖嗎?當時自己也是遇到了這個問題,所以有了這篇博客,感謝網絡上的那些前輩們的無私分享,現將自己的整理和思考記錄下來。

二、引入

??在Android開發過程中,常需要更新界面的UI。而更新UI是要主線程來更新的,即UI線程更新。如果在主線線程之外的線程中直接更新頁面顯示常會報錯。拋出異常:android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.怎麼解決呢?下面我會詳細列出子線程更新ui的方法:

三、子線程更新UI的方法

1、用Handler+message

??主線程中定義Handler,子線程發消息,通知Handler完成UI更新。

mHandler = new Handler() {  
   @Override  
   public void handleMessage(Message msg) {  
        //操作界面  
       myText.setText( 來自網絡的信息);  
      super.handleMessage(msg);  
   }  
  };  
public class MyThread extends Thread {  
     public void run() {  
?    ?    ?    // 耗時操作  
?    ?    ?    ?loadNetWork();  
            Message msg = new Message();  
            mHandler.sendMessage(msg);//向Handler發送消息,  
      }  
 }

handler的原理圖如下:

這裡寫圖片描述

2、用runOnUiThread更新

??這個最好用, 凡是要刷新頁面的地方,都可以按照如下方式寫。

new Thread() {
            public void run() {
                //這兒是耗時操作,完成之後更新UI;
                runOnUiThread(new Runnable(){

                    @Override
                    public void run() {
                        //更新UI
                        imageView.setImageBitmap(bitmap);
                    }

                });
            }
        }.start();

??這種方法使用比較靈活,但如果Thread定義在其他地方,需要傳遞Activity對象(通過構造函數傳遞)。

3、View.post(Runnable r)

??方法解釋:從Runnable派生你的子類,重載run()方法。然後調用View.post(myRunnableObj)即可把你的Runnable對象增加到UI線程中運行。

public void onClick( View v ) {  
        new Thread( new Runnable() {  
                public void run() {  
                       // 耗時操作  
?    ?    ?    ?    ?    ?      loadNetWork();  
?                          myText.( new Runnable() {  
                                  myText.setText( 來自網絡的信息);  
                          });  
                 }  
        }).start();  
}

??這種方法更簡單,但需要傳遞要更新的View過去。注意:post函數,裡面傳遞的是一個runnable 接口(你懂得 runnable 可不是一個線程這個你一定要和thread 區分開) 。

4、使用異步任務

//UI線程中執行  
new DownloadImageTask().execute( "www.91dota.com" );  
private class DownloadImageTask extends AsyncTask {  
    protected String doInBackground( String... url ) {  
         return loadDataFormNetwork( url[0] );//後台耗時操作  
    }  

    protected void onPostExecute( String result ) {  
          myText.setText( result ); //得到來自網絡的信息刷新頁面   

   }  
}

這裡寫圖片描述

應用場合

如果是後台任務,像是下載任務等,就需要使用AsyncTask。 如果需要傳遞狀態值等信息,像是藍牙編程中的socket連接,就需要利用狀態值來提示連接狀態以及做相應的處理,就需要使用Handler + Thread的方式; 需要另開線程處理數據以免阻塞UI線程,像是IO操作或者是循環,可以使用Activity.runOnUiThread(); 如果只是單純的想要更新UI而不涉及到多線程的話,使用View.post()就可以了;

四、在子線程中更新了UI的錯覺

??回到開頭的問題,子線程更新ui成功了,其實不然。還有另外一種錯誤的方法:在子線程中使用接口回調,在activity中實現該方法來更新ui,其實這個方法也是變相的在子線程中更新了UI。為什麼成功了呢?原因精煉點說就是:這個異常是android源碼中的檢測設定拋出的,如果檢測的方法沒有執行就不會報錯。onCreate方法裡開線程更新UI不報錯,是因為view還沒有還出來呢,沒有調用invalidate方法。

更深入的解釋請參考:

/kf/201111/111172.html
http://blog.csdn.net/imyfriend/article/details/6877959
http://doc.okbase.net/aigestudio/archive/127460.html
http://blog.csdn.net/zhaokaiqiang1992/article/details/43410351
http://blog.csdn.net/aigestudio/article/details/43449123
http://javapolo.iteye.com/blog/1343583
http://blog.csdn.net/androidzhaoxiaogang/article/details/8136222

五、綜述

??有的時候使用子線程來直接更新ui,並不會報錯,但並不推薦這麼做,google的android底層代碼中會對更新ui的線程做檢測,原因就是為了避免我們在非ui線程中直接更新ui。檢測針對兩個方面:1.是否更新了ui,更新view在android中對應的方法是invalidate。2.更新時當前線程是否是ui線程。雖然我們鑽空子,可以不報異常,但是這並不是好的方式。google這樣設計的原因就在於讓UI線程做的事情更純粹一些,都是界面方面的事情,如果在ui線程執行耗時的操作,在做UI操作的時候會有卡頓的感覺。即從更新View的角度來說,最好是UI線程,非UI線程也不是不能更新UI。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved