Only the original&nbsp…

當應用程序啓動,創建了一個叫“main”的線程,用於管理UI相關,又叫UI線程。其他線程叫工作線程(Work Thread)。

Single Thread Model

 

  • 一個組件的創建並不會新建一個線程,他們的創建都在UI線程中進行,包括他們的回調方法,如onKeyDown()
  • 當在UI線程中進行某些耗時的操作時,將會阻塞UI線程,一般阻塞超過5秒就會顯示一個ANR對話框。
  • UI線程是非線程安全的,所以,不能在工作線程中操作UI元素。

     

    兩個原則

    • Do not block the UI thread (不要阻塞UI線程)
    • Do not access the Android UI toolkit from outside the UI thread (不要在工作線程中操作UI元素)

     

    在工作線程更新UI方法

    • Activity.runOnUiThread(Runnable)
    • Handler
      • sendMessage(Message)
      • post(Runnable)
    • AsyncTask
      • execute()
      • doInBackground()
      • onPostExecute()

     

    例子程序

    • HandlerActivity01
      • 在工作線程中進行UI操作。
    • HandlerActivity02
      • Handler的兩個重要方法:sendMessagepost
    • HandlerActivity03
      • 官方推薦最佳方法。

     

    HandlerActivity01主要代碼:

    Java代碼   收藏代碼
    1. btnEnd.setOnClickListener(new View.OnClickListener() {  
    2.       
    3.     @Override  
    4.     public void onClick(View v) {  
    5.         new Thread(new Runnable() {  
    6.   
    7.             @Override  
    8.             public void run()  
    9.             {  
    10.                 //在新建的線程(工作線程)中改變Button的文字  
    11.                 btnEnd.setText("Text Changed in Sub Thread");  
    12.             }  
    13.               
    14.         }).start();  
    15.     }  
    16. });  

     

     這是一種錯誤的做法,運行程序,會報錯誤:

    Java代碼   收藏代碼
    1. android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.   

     

     

     

     

     

     HandlerActivity02主要代碼:

    Java代碼   收藏代碼
    1. public class HandlerActivity02 extends Activity  
    2. {  
    3.   
    4.     private int title = 0;  
    5.     Button btnStart,btnEnd;  
    6.   
    7.     private Handler mHandler = new Handler()  
    8.     {  
    9.         public void handleMessage(Message msg)  
    10.         {  
    11.             //更新UI  
    12.             switch (msg.what)  
    13.             {  
    14.                 case 1:  
    15.                     updateTitle();  
    16.                     break;  
    17.             }  
    18.         };  
    19.     };  
    20.       
    21.     public void onCreate(Bundle savedInstanceState)  
    22.     {  
    23.         super.onCreate(savedInstanceState);  
    24.         setContentView(R.layout.main);  
    25.           
    26.         btnStart = (Button)findViewById(R.id.start);  
    27.         btnEnd = (Button)findViewById(R.id.end);  
    28.         //新啓動一個線程,進行耗時操作  
    29.         Timer timer = new Timer();  
    30.         //每六秒執行一次MyTask的run方法  
    31.         timer.scheduleAtFixedRate(new MyTask(this), 16000);  
    32.     }  
    33.   
    34.     private class MyTask extends TimerTask  
    35.     {  
    36.         private Activity context;  
    37.         MyTask(Activity context)  
    38.         {  
    39.             this.context = context;  
    40.         }  
    41.           
    42.         @Override  
    43.         public void run()  
    44.         {  
    45.             //耗時操作略....   
    46.               
    47.             //更新UI方法  1  
    48.             Message message = new Message();  
    49.             message.what = 1;  
    50.             mHandler.sendMessage(message);  
    51.               
    52.             //更新UI方法  2  
    53.             mHandler.post(updateThread);  
    54.               
    55.             //更新UI方法  3  
    56.             context.runOnUiThread(updateThread);  
    57.         }  
    58.     }  
    59.   
    60.     public void updateTitle()  
    61.     {  
    62.         setTitle("Welcome to Mr Wei's blog " + title);  
    63.         title++;  
    64.     }  
    65.       
    66.     Runnable updateThread = new Runnable()   
    67.     {  
    68.   
    69.         @Override  
    70.         public void run()  
    71.         {  
    72.             //更新UI  
    73.             btnStart.setText(String.valueOf(title));  
    74.             btnEnd.setText(String.valueOf(title));  
    75.         }  
    76.           
    77.     };  
    78. }  

     這裏有個容易出錯的地方,在更新UI方法2和3中,我們傳入的參數是一個Runnable對象,一般認爲這就會啓動一個新的線程,而且常有人在這個Runnable對象的run方法中進行耗時操作。看過這塊的源碼就會知道,其實,android只是調用了這個Runnable對象的run方法而已,並沒有啓動新的線程,而且我們不應該在run方法中進行耗時操作,因爲這個run方法最終是在UI線程裏面執行的。也就是說,run方法裏面只應該放更新UI的代碼,handleMessage方法也一樣。

     

    如果你要看這部分源代碼的話,相信這個圖對你會有幫助:

     

     

     

    HandlerActivity03主要代碼:

    Java代碼   收藏代碼
    1. public class HandlerActivity03 extends Activity  
    2. {  
    3.     Button btnStart;  
    4.     @Override  
    5.     protected void onCreate(Bundle savedInstanceState)  
    6.     {  
    7.         // TODO Auto-generated method stub  
    8.         super.onCreate(savedInstanceState);  
    9.         setContentView(R.layout.main);  
    10.           
    11.         btnStart = (Button)findViewById(R.id.start);  
    12.         btnStart.setOnClickListener(new View.OnClickListener() {  
    13.               
    14.             @Override  
    15.             public void onClick(View v) {  
    16.                 //開始執行AsyncTask,並傳入某些數據  
    17.                 new LongTimeTask().execute("New Text");  
    18.             }  
    19.         });  
    20.     }  
    21.       
    22.     private class LongTimeTask extends AsyncTask  
    23.     {  
    24.   
    25.         @Override  
    26.         protected String doInBackground(String... params)  
    27.         {  
    28.             try  
    29.             {  
    30.                 //線程睡眠5秒,模擬耗時操作,這裏面的內容Android系統會自動爲你啓動一個新的線程執行  
    31.                 Thread.sleep(5000);  
    32.             }  
    33.             catch (InterruptedException e)  
    34.             {  
    35.                 e.printStackTrace();  
    36.             }  
    37.             return params[0];  
    38.         }  
    39.   
    40.         @Override  
    41.         protected void onPostExecute(String result)  
    42.         {  
    43.             //更新UI的操作,這裏面的內容是在UI線程裏面執行的  
    44.             btnStart.setText(result);  
    45.         }  
    46.           
    47.     }  
    48.   
    49. }  

     

     這個方法確實挺好,因爲它爲你封裝了許多操作,你只需要記住在doInBackground方法中寫耗時操作的代碼,在onPostExecute方法中寫更新UI的方法就行了。

     

    轉載From: http://chen592969029.iteye.com/blog/1467237

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章