在 Android 的開發規則裡有一個規定
就是所有與網路連線的功能都必須在另一個執行緒內執行
而另一個相關的重要規定是
所有與 UI 相關的功能只能在 UI Thread 內執行(也就是主執行緒)
這項規定是由於程式執行流暢度的考量而產生的
但這麼一來
若在其他執行緒的做完某件事情時 該如何使用 UI 告知使用者呢?
這時就需要用到 Thread 搭配 Handler 的 Design Pattern 了
當然還有其他的選項 例如 AsyncTask
但為了最大的開發自由度
我建議還是使用 Thread 搭配 Handler 會較一勞永逸
當然還是要看情況而定
廢話不多說 我們來看看該如何使用這項功能
這裡同時會用到 WeakReference 的 Design Pattern
會在另一個教學文章 WeakReference 與多執行緒的 Handler 提到
// Handler in main activity private SSHThreadHandler sshHandler = new SSHThreadHandler(this); private static class SSHThreadHandler extends Handler{ private WeakReference<MainActivity> refActivity; public SSHThreadHandler(MainActivity activity) { refActivity = new WeakReference<>(activity); } @Override public void handleMessage(Message message){ MainActivity activity = refActivity.get(); SSHConnection sshConnection = activity.getSshConnection(); TextView redirectPageStatus = activity.getRedirectPageStatus(); switch(message.what){ case SSHConnection.waitingUsername: sshConnection.inputUsername(); redirectPageStatus.setText( "連線中... 快好囉~" ); break; case SSHConnection.waitingPassword: sshConnection.inputPassword(); break; case ISSHConnection.waitingDuplicateConfirm: activity.showDuplicateLoginAlert(); break; case SSHConnection.waitingContinueConfirm: sshConnection.doContinue(); redirectPageStatus.setText( "歡迎回來 " + sshConnection.getUsername() ); break; case SSHConnection.menuPage: activity.hideLoginPageFrame(); activity.hideLoadingFrame(); activity.showMenuPageHolder(); break; } } } // 其他執行緒與 Handler 互動的方式 public static final int connected = 0; public static final int waitingUsername = 1; public static final int waitingPassword = 2; public static final int waitingDuplicateConfirm = 3; public static final int waitingContinueConfirm = 4; public static final int menuPage = 5; @Override public void connect(String username, String password, Handler handler) throws IOException { if( somthingHappened ){ sendMessage( doSomething ); } else{ sendMessage( doAnother ); } } /** * Send message to the UI thread. */ private void sendMessage(int msg){ Message message = new Message(); message.what = msg; handler.sendMessage(message); }
首先我們必須要在 MainActivity.class 中建立一個實作 android.os.Handler 的 class
這個 Handler 可以根據另一個執行緒回傳的事件而執行與 UI 相關的動作
private static class SSHThreadHandler extends Handler{ }
並在這個 class 內使用 WeakReference 的設計方式以取得 MainActivity 的成員
private WeakReference<MainActivity> refActivity; public SSHThreadHandler(MainActivity activity) { refActivity = new WeakReference<>(activity); }
最後 Override handleMessage 方法以針對各種回傳的訊息做相對應的處理
@Override public void handleMessage(Message message){ MainActivity activity = refActivity.get(); SSHConnection sshConnection = activity.getSshConnection(); TextView redirectPageStatus = activity.getRedirectPageStatus(); switch(message.what){ case SSHConnection.waitingUsername: sshConnection.inputUsername(); redirectPageStatus.setText( "連線中... 快好囉~" ); break; case SSHConnection.waitingPassword: sshConnection.inputPassword(); break; case ISSHConnection.waitingDuplicateConfirm: activity.showDuplicateLoginAlert(); break; case SSHConnection.waitingContinueConfirm: sshConnection.doContinue(); redirectPageStatus.setText( "歡迎回來 " + sshConnection.getUsername() ); break; case SSHConnection.menuPage: activity.hideLoginPageFrame(); activity.hideLoadingFrame(); activity.showMenuPageHolder(); break; } }
在其他執行緒中我們需要經由 message.what 屬性傳遞訊息
而這個屬性是 int 類型
所以我們需要建立一個對應的訊息變數以便了解此訊息代表的意義
public static final int connected = 0; public static final int waitingUsername = 1; public static final int waitingPassword = 2; public static final int waitingDuplicateConfirm = 3; public static final int waitingContinueConfirm = 4; public static final int menuPage = 5;
為了方便使用 我另外新增了一個 sendMessage 的方法
而傳入的參數就是上面建立的訊息變數
handler 則是傳入執行緒的參數,也就是我們剛剛在 MainActivity 建立的 Handler
/** * Send message to the UI thread. */ private void sendMessage(int msg){ Message message = new Message(); message.what = msg; handler.sendMessage(message); }
最後我們只要在需要的時候傳遞訊息即可
@Override public void connect(String username, String password, Handler handler) throws IOException { if( somthingHappened ){ sendMessage( doSomething ); } else{ sendMessage( doAnother ); } }
No comments:
Post a Comment