Handler訊息溝通
Prerequisites:
Handler
當背景執行緒(background Thread)處理完耗時操作,要通知UI執行緒(主要執行緒Main Thread)處理畫面更新,就需要使用到handler來通知UI執行緒來更新畫面。
建立handler
1
Handler handler = new Handler();
Handler主要做二件事:通知、接收訊息
建立背景執行緒(background Thread)
建立背景執行緒(background Thread)可以透過new thread或建立runnable物件,二種方式建立背景執行緒(background Thread)
1
2
3
4
5
6
new Thread() {
@Override
public void run() {
...
}
}.start();
1
2
3
4
5
6
new Thread(new Runnable() {
@Override
public void run() {
...
}
}).start();
訊息Message
建立訊息語法
Message message = new Message();
message.what = 1;
handler.sendMessage(message);
所謂的what,可視為訊息的id編號,代表我這個訊息是1號
Handler通知
背景執行緒background thread可使用handler.sendMessage()通知ui thread要更新ui畫面。
1
2
3
4
5
6
7
8
9
10
11
new Thread() {
@Override
public void run() {
// 建立訊息
Message message = new Message();
// 建立訊息編號id
message.what = 1;
// 通知ui thread,要更新ui,sendMessage()參數為先前建立的訊息
handler.sendMessage(message);
}
}.start();
Handler收到通知
onCreate()是屬於UI Thread,UI Thread中handler收到message通知。
取出message,再取出what,根據不同的訊息id,處理相對映的更新畫面動作。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
private Handler handler;
@Override
protected void onCreate(Bundle savedInstanceState) {
handler = new Handler() {
@Override
public void handleMessage(@NonNull Message msg) {
switch (msg.what) {
case 1:
Toast.makeText(MainActivity.this, "test", Toast.LENGTH_SHORT).show();
Log.e(TAG,"handler test");
break;
}
}
};
完整程式碼
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
private Handler handler;
@Override
protected void onCreate(Bundle savedInstanceState) {
// 建立訊息接收者
handler = new Handler() {
// 收到訊息
@Override
public void handleMessage(@NonNull Message msg) {
// 根據訊息編號判斷要做什麼畫面更新的流程
switch (msg.what) {
case 1: //訊息編號為1
Toast.makeText(MainActivity.this, "test", Toast.LENGTH_SHORT).show();
Log.e(TAG,"handler test");
break;
}
}
};
// 建立背景執行緒
new Thread() {
@Override
public void run() {
// 建立訊息
Message message = new Message();
// 訊息編號
message.what = 1;
// 傳送訊息,通知ui thread更新畫面
handler.sendMessage(message);
}
}.start();// 啟動背景執行緒
}
message.obj
可以在message加上訊息。
語法如下:
1
2
3
4
String data = "12345";
Message message = new Message();
message.what = 1;
message.obj = data
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public void testHandler() {
new Thread() {
@Override
public void run() {
String data = "12345";
// 建立訊息
Message message = new Message();
// 建立訊息編號id
message.what = 1;
// 建立物件
message.obj = data;
// 通知ui thread,要更新ui,sendMessage()參數為先前建立的訊息
handler.sendMessage(message);
}
}.start();
}
public void btnClick(View view) {
testHandler();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
handler = new Handler() {
// 收到訊息
@Override
public void handleMessage(@NonNull Message msg) {
// 根據訊息編號判斷要做什麼畫面更新的流程
switch (msg.what) {
case 1: //訊息編號為1
String data = (String) msg.obj;
Toast.makeText(MainActivity.this, "test" + data, Toast.LENGTH_SHORT).show();
Log.e(TAG, "handler test data = " + data);
break;
case 2:
break;
}
}
};
sendEmptyMessage()
語法
handler.sendEmptyMessage(整數的編號);
handler.sendEmptyMessage(1);
參數為訊息的編號
可以省略new Message()…等等相關流程,一樣有同樣的效果。
1
2
3
4
5
6
7
new Thread() {
@Override
public void run() {
// 傳送訊息,通知ui thread更新畫面
handler.sendEmptyMessage(1);
}
}.start();// 啟動背景執行緒
sendEmptyMessageDelayed()
幾秒後像handler發送訊息
語法
handler.sendEmptyMessageDelayed(整數的編號, 幾秒後發送);
handler.sendEmptyMessage(2, 2*1000);
- 參數1:訊息編號
- 參數2:單位是毫秒,1秒是1000毫秒,此範例使用2秒也就是2000毫秒
ProgressBar xml
1
2
3
4
5
6
7
8
9
10
<ProgressBar
android:id="@+id/progressBar1"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:max="100"
android:visibility="gone"
app:layout_constraintBottom_toTopOf="@+id/button"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
private ProgressBar progressBar1;
// 進度條變數
private int progress = 0;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
progressBar1 = (ProgressBar) findViewById(R.id.progressBar1);
handler = new Handler() {
// 收到訊息
@Override
public void handleMessage(@NonNull Message msg) {
// 根據訊息編號判斷要做什麼畫面更新的流程
switch (msg.what) {
case 1: //訊息編號為1
Toast.makeText(MainActivity.this, "test", Toast.LENGTH_SHORT).show();
Log.e(TAG,"handler test");
break;
case 2:
// 每收到訊息一次,progress小於100 progress就+10
if (progress < 100) {
// 進度條變數+10
progress+=10;
// 將進度條變數放入progressBar中
progressBar1.setProgress(progress);
// 這個是重點!循環向handler傳送訊息,每隔2秒
// progress滿了100就不再發訊息
handler.sendEmptyMessageDelayed(2, 2*1000);
}
break;
}
}
};
// 發送訊息
public void testTimer() {
progressBar1.setVisibility(View.VISIBLE);
// 2*1000毫秒 = 2 秒 2秒以後傳送handler訊息
handler.sendEmptyMessageDelayed(2, 2*1000);
}
// 按了按鈕才開始
public void btnClick(View view) {
testTimer();
}
post(Runnable)
變更畫面的流程,寫進參數Runnable中的run()方法,post()方法會把Runnable物件丟給主要執行緒UI thread,由主要執行緒(UI Thread)去執行Runnable物件中的run()方法。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public void postTest() {
new Thread() {
@Override
public void run() {
handler.post(new Runnable() {
@Override
public void run() {
Toast.makeText(MainActivity.this, "post clicked!", Toast.LENGTH_LONG).show();
Log.d("test:", "post here");
}
});
}
}.start();// 啟動背景執行緒
}
public void btnClick(View view) {
postTest();
}
view.post(Runnable)
ui元件都繼承view,所以用ui元件.post(new Runnable()),畫面處理的流程寫在Runnable介面中的run()方法,通知UI thread,並根據run()方法中的流程,更新ui畫面。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
private Button btn;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btn = (Button) findViewById(R.id.button);
}
public void viewpostTest() {
new Thread() {
@Override
public void run() {
// btn是view的子類別,使用post()的方法可以通知ui thread
btn.post(new Runnable() {
@Override
public void run() {
Toast.makeText(MainActivity.this, "view.post clicked!", Toast.LENGTH_LONG).show();
Log.d("test:", "view.post here");
}
});
}
}.start();// 啟動背景執行緒
}
public void btnClick(View view) {
viewpostTest();
}
runOnUiThread 更新畫面
除了handler外,runOnUiThread也可以進行畫面的更新。
在執行緒中添加以下程式碼,並把畫面處理的流程寫在Runnable介面中的run()方法,通知ui thread,並根據run()方法中的流程,更新ui畫面。
1
2
3
4
5
6
7
8
9
10
// 通知ui thread更新畫面
runOnUiThread(
new Runnable() {
@Override
public void run() {
// 畫面更新的流程
Toast.makeText(MainActivity.this, "ui Test", Toast.LENGTH_SHORT).show();
Log.e(TAG,"ui Thread Test");
}
});
完整程式碼
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 建立背景執行緒
new Thread(new Runnable() {
@Override
public void run() {
// 通知ui thread更新畫面
runOnUiThread(new Runnable() {
@Override
public void run() {
// 畫面更新的流程
Toast.makeText(MainActivity.this, "ui Test", Toast.LENGTH_SHORT).show();
Log.e(TAG,"ui Thread Test");
}
});
}
}).start();// 啟動背景執行緒