當應用程序啓動,創建了一個叫“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的兩個重要方法:sendMessage和post。
-
HandlerActivity03
- 官方推薦最佳方法。
HandlerActivity01主要代碼:
- btnEnd.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- new Thread(new Runnable() {
- @Override
- public void run()
- {
- //在新建的線程(工作線程)中改變Button的文字
- btnEnd.setText("Text Changed in Sub Thread");
- }
- }).start();
- }
- });
這是一種錯誤的做法,運行程序,會報錯誤:
- android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
HandlerActivity02主要代碼:
- public class HandlerActivity02 extends Activity
- {
- private int title = 0;
- Button btnStart,btnEnd;
- private Handler mHandler = new Handler()
- {
- public void handleMessage(Message msg)
- {
- //更新UI
- switch (msg.what)
- {
- case 1:
- updateTitle();
- break;
- }
- };
- };
- public void onCreate(Bundle savedInstanceState)
- {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
- btnStart = (Button)findViewById(R.id.start);
- btnEnd = (Button)findViewById(R.id.end);
- //新啓動一個線程,進行耗時操作
- Timer timer = new Timer();
- //每六秒執行一次MyTask的run方法
- timer.scheduleAtFixedRate(new MyTask(this), 1, 6000);
- }
- private class MyTask extends TimerTask
- {
- private Activity context;
- MyTask(Activity context)
- {
- this.context = context;
- }
- @Override
- public void run()
- {
- //耗時操作略....
- //更新UI方法 1
- Message message = new Message();
- message.what = 1;
- mHandler.sendMessage(message);
- //更新UI方法 2
- mHandler.post(updateThread);
- //更新UI方法 3
- context.runOnUiThread(updateThread);
- }
- }
- public void updateTitle()
- {
- setTitle("Welcome to Mr Wei's blog " + title);
- title++;
- }
- Runnable updateThread = new Runnable()
- {
- @Override
- public void run()
- {
- //更新UI
- btnStart.setText(String.valueOf(title));
- btnEnd.setText(String.valueOf(title));
- }
- };
- }
這裏有個容易出錯的地方,在更新UI方法2和3中,我們傳入的參數是一個Runnable對象,一般認爲這就會啓動一個新的線程,而且常有人在這個Runnable對象的run方法中進行耗時操作。看過這塊的源碼就會知道,其實,android只是調用了這個Runnable對象的run方法而已,並沒有啓動新的線程,而且我們不應該在run方法中進行耗時操作,因爲這個run方法最終是在UI線程裏面執行的。也就是說,run方法裏面只應該放更新UI的代碼,handleMessage方法也一樣。
如果你要看這部分源代碼的話,相信這個圖對你會有幫助:
HandlerActivity03主要代碼:
- public class HandlerActivity03 extends Activity
- {
- Button btnStart;
- @Override
- protected void onCreate(Bundle savedInstanceState)
- {
- // TODO Auto-generated method stub
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
- btnStart = (Button)findViewById(R.id.start);
- btnStart.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- //開始執行AsyncTask,並傳入某些數據
- new LongTimeTask().execute("New Text");
- }
- });
- }
- private class LongTimeTask extends AsyncTask
- {
- @Override
- protected String doInBackground(String... params)
- {
- try
- {
- //線程睡眠5秒,模擬耗時操作,這裏面的內容Android系統會自動爲你啓動一個新的線程執行
- Thread.sleep(5000);
- }
- catch (InterruptedException e)
- {
- e.printStackTrace();
- }
- return params[0];
- }
- @Override
- protected void onPostExecute(String result)
- {
- //更新UI的操作,這裏面的內容是在UI線程裏面執行的
- btnStart.setText(result);
- }
- }
- }
這個方法確實挺好,因爲它爲你封裝了許多操作,你只需要記住在doInBackground方法中寫耗時操作的代碼,在onPostExecute方法中寫更新UI的方法就行了。