子線程更新UI會發生android.view.ViewRoot$CalledFromWrongThreadException異常的解決方案

來源:互聯網
上載者:User

子線程更新UI 


    顯然假如你的程式需要執行耗時的操作的話,假如像上例一樣由主線程來負責執行該操作是錯誤的。所以我們需要在onClick方法中建立一個新的子線程來負責調用GOOGLE API來獲得天氣資料。剛接觸Android的開發人員最輕易想到的方式就是如下: 


    public void onClick(View v) {       //建立一個子線程執行耗時的從網路上獲得天氣資訊的操作       new Thread() {           @Override           public void run() {              //獲得使用者輸入的城市名稱              String city = editText.getText().toString();              //調用Google 天氣API查詢指定城市的當日天氣情況              String weather = getWetherByCity(city);              //把天氣資訊顯示在title上              setTitle(weather);           }       }.start();    }

但是很不幸,你會發現Android會提示程式由於異常而終止。為什麼在其他平台上看起來很簡單的代碼在Android上啟動並執行時候依然會出錯呢?假如你觀察LogCat中列印的日誌資訊就會發現這樣的錯誤記錄檔: 

android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views. 

從錯誤資訊不難看出Android禁止其他子線程來更新由UI thread建立的試圖。本例中顯示天氣資訊的title實際是就是一個由UI thread所建立的TextView,所以參試在一個子線程中去更改TextView的時候就出錯了。這顯示違反了單執行緒模式的原則:Android UI操作並不是安全執行緒的並且這些操作必須在UI線程中執行 

2.2 Message Queue 

在單執行緒模式下,為瞭解決類似的問題,Android設計了一個Message Queue(訊息佇列),線程間可以通過該Message Queue並結合Handler和Looper組件進行資訊交換。下面將對它們進行分別介紹: 

l Message Queue 

Message Queue是一個訊息佇列,用來存放通過Handler發布的訊息。訊息佇列通常附屬於某一個建立它的線程,可以通過Looper.myQueue()得到當前線程的訊息佇列。Android在第一啟動程式時會預設會為UI thread建立一個關聯的訊息佇列,用來管理程式的一些上層組件,activities,broadcast receivers 等等。你可以在自己的子線程中建立Handler與UI thread通訊。 

l Handler 

通過Handler你發行就緒或者處理一個訊息或者是一個Runnable的執行個體。沒個Handler都會與唯一的一個線程以及該線程的訊息佇列管理。當你建立一個新的Handler時候,預設情況下,它將關聯到建立它的這個線程和該線程的訊息佇列。也就是說,假如你通過Handler發布訊息的話,訊息將只會發送到與它關聯的這個訊息佇列,當然也只能處理該訊息佇列中的訊息。 

主要的方法有: 

1)   public final boolean sendMessage(Message msg) 

把訊息放入該Handler所關聯的訊息佇列,放置在所有目前時間前未被處理的訊息後。 

2)   public void handleMessage(Message msg) 

關聯該訊息佇列的線程將通過調用Handler的handleMessage方法來接收和處理訊息,通常需要子類化Handler來實現handleMessage。 

l Looper 

Looper扮演著一個Handler和訊息佇列之間通訊橋樑的角色。程式組件首先通過Handler把訊息傳送給Looper,Looper把訊息放入隊列。Looper也把訊息佇列裡的訊息廣播給所有的Handler,Handler接受到訊息後調用handleMessage進行處理。 

1)   可以通過Looper類的靜態方法Looper.myLooper得到當前線程的Looper執行個體,假如當前線程未關聯一個Looper執行個體,該方法將返回空。 

2)   可以通過靜態方法Looper. getMainLooper方法得到主線程的Looper執行個體 

線程,訊息佇列,Handler,Looper之間的關係可以通過一個圖來展現: 

在瞭解了訊息佇列及其相關組件的設計思想後,我們將把天氣預告的案例通過訊息佇列來重新實現: 


private EditText editText;    private Handler messageHandler;    @Override    public void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.main);        editText = (EditText) findViewById(R.id.weather_city_edit);        Button button = (Button) findViewById(R.id.goQuery);        button.setOnClickListener(this);        //得到當前線程的Looper執行個體,由於當前線程是UI線程也可以通過Looper.getMainLooper()得到        Looper looper = Looper.myLooper();        //此處甚至可以不需要設定Looper,因為 Handler預設就使用當前線程的Looper        messageHandler = new MessageHandler(looper);    }    @Override    public void onClick(View v) {        //建立一個子線程去做耗時的網路連接工作        new Thread() {            @Override            public void run() {                //活動使用者輸入的城市名稱                String city = editText.getText().toString();                //調用Google 天氣API查詢指定城市的當日天氣情況                String weather = getWetherByCity(city);                //建立一個Message對象,並把得到的天氣資訊賦值給Message對象                Message message = Message.obtain();                message.obj = weather;                //通過Handler發布攜帶有天氣情況的訊息                messageHandler.sendMessage(message);            }        }.start();    }    //子類化一個Handler    class MessageHandler extends Handler {        public MessageHandler(Looper looper) {            super(looper);        }        @Override        public void handleMessage(Message msg) {            //處理收到的訊息,把天氣資訊顯示在title上            setTitle((String) msg.obj);        }    }

通過訊息佇列改寫過後的天氣預告程式已經可以成功運行,因為Handler的handleMessage方法實際是由關聯有該訊息佇列的UI thread調用,而在UI thread中更新title並沒有違反Android的單執行緒模式的原則。 



相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.