Fig. 1 ANR Dialog |
為了避免上述的狀況,過於花時的運算和事件等待應避免在UI 執行緒(Activity裡的Main Thread)執行,換句話說可以透過建立額外的Non UI 執行緒來處理,不過Android不允許Non UI 執行緒 (即UI 執行緒下的子執行緒)直接更新UI 執行緒的畫面,所以Non UI 執行緒必須得將自己的資料回傳給UI 執行緒由它負責處理,在這篇文章介紹一個簡單的方法,使用 - runOnUiThread。 runOnUiThread(Runnable action)是Activity Class裡的一個方法 (Method),當Non UI 執行緒呼叫此方法時,會將引數action回傳到UI 執行緒的Event Queue裡等待被執行,所以可以根據實際的設計需求實作特定的action,將Non UI 執行緒的資料傳遞給UI 執行緒也得以實踐。下面的範例程式碼實作了一個忽略時間誤差的簡單時鐘程式,此範例程式透過Non UI 執行緒取得系統時間後傳給UI 執行緒更新畫面。此範例的UI介面有兩個TextView,第一個TextView顯示"A Message form the Non-UI Threads:",用來提示此APP的功能,第二個msgTextView用來顯示Non UI執行緒傳給UI執行緒的資料。關於程式碼的部份,MainActivity class (APP執行時的UI執行緒)裡宣告了一個繼承Thread class的inner class - NonUIThread (APP執行時UI執行緒下的Non UI執行緒),第64和65行是它的成員物件,nonUIThreadName是Non UI執行緒的名字,用來識別是哪一個執行緒,第67到71行的default constructor設定"Unknown Non-UI Thread"為預設的名字,或是透過呼叫第73到76行的constructor賦與不同的名字,另一個成員calendar則是用來獲取裝置所設定的系統年分、日期和時間的日曆物件,第79到90行覆寫了Thread class的run方法,run方法會不斷的去呼叫sayHelloToUIThread方法,第83行的sleep方法設定了讓執行緒下次再繼續執行的時間(即1秒),第91到103行是sayHelloToUIThread方法的實做部分,此方法每隔一秒會被呼叫,並透過Calendar.getInstance()擷取出目前的時間,第93到96行將這些資訊轉換成字串(所有的JAVA calss均會繼承Object class,Object class裡有一個toString的方法,所以calendar.get()回傳的時間會靠此方法轉成字串),第97到102行就是本範例要講的重點,inner class - NonUIThread呼叫了MainActivity的runOnUiThread,傳入引數的Runnable物件一樣覆寫了run方法,run方法會呼叫MainActivity裡的receiveMsgFromNonUIThread方法並,傳入的引數toUIStr即為Non UI執行緒想要傳給UI執行緒的資料 ,第59到61行的receiveMsgFromNonUIThread方法實做則是再呼叫msgTextView的setText方法將目前的時間更新到UI執行緒上。MainActivity的onCreate方法建立了UI畫面和一個Non UI執行緒的物件,在onStart方法裡呼叫nonUIThread1.start()讓Non UI執行緒開始執行獲取系統時間並傳回給UI,Fig.2顯示了此範例APP的執行結果。
package com.example.threadcommunicationex1; import java.util.Calendar; import android.support.v7.app.ActionBarActivity; import android.os.Bundle; import android.view.Menu; import android.view.MenuItem; import android.widget.TextView; public class MainActivity extends ActionBarActivity { private TextView msgTextView; private NonUIThread nonUIThread1; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); getViewComponent(); CreateThread(); } @Override protected void onStart(){ super.onStart(); nonUIThread1.start(); } private void getViewComponent() { msgTextView = (TextView) findViewById(R.id.msg); msgTextView.setText(""); } private void CreateThread(){ nonUIThread1 = new NonUIThread("Non-UI Thread 1"); } private void receiveMsgFromNonUIThread(String msg){ msgTextView.setText(msg); } class NonUIThread extends Thread{ private String nonUIThreadName; private Calendar calendar; NonUIThread(){ nonUIThreadName = "Unknown Non-UI Thread"; //get a default instance of this class for general use calendar = Calendar.getInstance(); } NonUIThread(String name){ this(); nonUIThreadName = name; } @Override public void run(){ while(true){ sayHelloToUIThread(); try { sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } private void sayHelloToUIThread(){ calendar = Calendar.getInstance(); final String toUIStr = "This is " + nonUIThreadName + ", " + "current time is " + calendar.get(Calendar.HOUR_OF_DAY) + ":" + calendar.get(Calendar.MINUTE) + ":" + calendar.get(Calendar.SECOND) + "\n\n"; runOnUiThread(new Runnable(){ @Override public void run(){ receiveMsgFromNonUIThread(toUIStr); } }); } } }
程式執行的畫面如下:
Fig. 2. 程式執行結果 |
對JAVA執行緒不熟的話可以參考此網站的講解:
沒有留言:
張貼留言