Search

Android - Multi-thread 多執行緒與 UI 更新操作

2015-07-23 2:13 PM

在 Android 的開發規則裡有一個規定

就是所有與網路連線的功能都必須在另一個執行緒內執行

而另一個相關的重要規定是

所有與 UI 相關的功能只能在 UI Thread 內執行(也就是主執行緒)

這項規定是由於程式執行流暢度的考量而產生的

但這麼一來

若在其他執行緒的做完某件事情時 該如何使用 UI 告知使用者呢?

這時就需要用到 Thread 搭配 Handler 的 Design Pattern 了

當然還有其他的選項 例如 AsyncTask

但為了最大的開發自由度

我建議還是使用 Thread 搭配 Handler 會較一勞永逸

當然還是要看情況而定

廢話不多說 我們來看看該如何使用這項功能

這裡同時會用到 WeakReference 的 Design Pattern

會在另一個教學文章 WeakReference 與多執行緒的 Handler 提到

程式碼範例
  1. // Handler in main activity
  2. private SSHThreadHandler sshHandler = new SSHThreadHandler(this);
  3.  
  4. private static class SSHThreadHandler extends Handler{
  5.  
  6. private WeakReference<MainActivity> refActivity;
  7.  
  8. public SSHThreadHandler(MainActivity activity) {
  9. refActivity = new WeakReference<>(activity);
  10. }
  11.  
  12. @Override
  13. public void handleMessage(Message message){
  14. MainActivity activity = refActivity.get();
  15. SSHConnection sshConnection = activity.getSshConnection();
  16. TextView redirectPageStatus = activity.getRedirectPageStatus();
  17.  
  18. switch(message.what){
  19. case SSHConnection.waitingUsername:
  20. sshConnection.inputUsername();
  21. redirectPageStatus.setText( "連線中... 快好囉~" );
  22. break;
  23. case SSHConnection.waitingPassword:
  24. sshConnection.inputPassword();
  25. break;
  26. case ISSHConnection.waitingDuplicateConfirm:
  27. activity.showDuplicateLoginAlert();
  28. break;
  29. case SSHConnection.waitingContinueConfirm:
  30. sshConnection.doContinue();
  31. redirectPageStatus.setText( "歡迎回來 " + sshConnection.getUsername() );
  32. break;
  33. case SSHConnection.menuPage:
  34. activity.hideLoginPageFrame();
  35. activity.hideLoadingFrame();
  36. activity.showMenuPageHolder();
  37. break;
  38. }
  39. }
  40. }
  41.  
  42. // 其他執行緒與 Handler 互動的方式
  43.  
  44. public static final int connected = 0;
  45. public static final int waitingUsername = 1;
  46. public static final int waitingPassword = 2;
  47. public static final int waitingDuplicateConfirm = 3;
  48. public static final int waitingContinueConfirm = 4;
  49. public static final int menuPage = 5;
  50.  
  51. @Override
  52. public void connect(String username, String password, Handler handler) throws IOException {
  53. if( somthingHappened ){
  54. sendMessage( doSomething );
  55. }
  56. else{
  57. sendMessage( doAnother );
  58. }
  59. }
  60.  
  61. /**
  62. * Send message to the UI thread.
  63. */
  64. private void sendMessage(int msg){
  65. Message message = new Message();
  66. message.what = msg;
  67. handler.sendMessage(message);
  68. }
詳細解說

首先我們必須要在 MainActivity.class 中建立一個實作 android.os.Handler 的 class

這個 Handler 可以根據另一個執行緒回傳的事件而執行與 UI 相關的動作

  1. private static class SSHThreadHandler extends Handler{
  2. }

並在這個 class 內使用 WeakReference 的設計方式以取得 MainActivity 的成員

  1. private WeakReference<MainActivity> refActivity;
  2.  
  3. public SSHThreadHandler(MainActivity activity) {
  4. refActivity = new WeakReference<>(activity);
  5. }

最後 Override handleMessage 方法以針對各種回傳的訊息做相對應的處理

  1. @Override
  2. public void handleMessage(Message message){
  3. MainActivity activity = refActivity.get();
  4. SSHConnection sshConnection = activity.getSshConnection();
  5. TextView redirectPageStatus = activity.getRedirectPageStatus();
  6.  
  7. switch(message.what){
  8. case SSHConnection.waitingUsername:
  9. sshConnection.inputUsername();
  10. redirectPageStatus.setText( "連線中... 快好囉~" );
  11. break;
  12. case SSHConnection.waitingPassword:
  13. sshConnection.inputPassword();
  14. break;
  15. case ISSHConnection.waitingDuplicateConfirm:
  16. activity.showDuplicateLoginAlert();
  17. break;
  18. case SSHConnection.waitingContinueConfirm:
  19. sshConnection.doContinue();
  20. redirectPageStatus.setText( "歡迎回來 " + sshConnection.getUsername() );
  21. break;
  22. case SSHConnection.menuPage:
  23. activity.hideLoginPageFrame();
  24. activity.hideLoadingFrame();
  25. activity.showMenuPageHolder();
  26. break;
  27. }
  28. }

在其他執行緒中我們需要經由 message.what 屬性傳遞訊息

而這個屬性是 int 類型

所以我們需要建立一個對應的訊息變數以便了解此訊息代表的意義

  1. public static final int connected = 0;
  2. public static final int waitingUsername = 1;
  3. public static final int waitingPassword = 2;
  4. public static final int waitingDuplicateConfirm = 3;
  5. public static final int waitingContinueConfirm = 4;
  6. public static final int menuPage = 5;

為了方便使用 我另外新增了一個 sendMessage 的方法

而傳入的參數就是上面建立的訊息變數

handler 則是傳入執行緒的參數,也就是我們剛剛在 MainActivity 建立的 Handler

  1. /**
  2. * Send message to the UI thread.
  3. */
  4. private void sendMessage(int msg){
  5. Message message = new Message();
  6. message.what = msg;
  7. handler.sendMessage(message);
  8. }

最後我們只要在需要的時候傳遞訊息即可

  1. @Override
  2. public void connect(String username, String password, Handler handler) throws IOException {
  3. if( somthingHappened ){
  4. sendMessage( doSomething );
  5. }
  6. else{
  7. sendMessage( doAnother );
  8. }
  9. }
各項資料連結
Android - WeakReference 與多執行緒的 Handler

No comments:

Post a Comment