在Activity中,啟動Service有兩種方式:startService方式,bindService方式。
如果想要調用Service中的方法,只能使用bindService方式。因為這種方式可以拿到Service的Binder對象,從而可以調用Service中的方法。
這里,我們不說怎么調用,只是來驗證一個問題:ServiceConnection接口中的onServiceConnected方法執行在哪個線程(另一種問法是:在onServiceConnected方法中是否可以直接更新UI)。
結論:執行在主線程,也就是說,在這個方法中可以直接進行UI更新。
下面,用一個例子來證明上面的結論。
自定義Service類
public class SevenService extends Service {
private int progress = 0;
private OnProgressListener mOnProgressListener;
public interface OnProgressListener{
void onProgress(int progress);
}
public void setOnProgressListener(OnProgressListener listener){
mOnProgressListener = listener;
}
public class SevenBinder extends Binder {
public SevenService getService(){
return SevenService.this;
}
}
public int getProgress(){
return progress;
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return new SevenBinder();
}
//模擬下載
public void startDownload(){
new Thread(new Runnable() {
@Override
public void run() {
Log.e("startDownload()","Thread: " + Thread.currentThread().getName());//Thread: Thread-9945
while (progress < 100){
progress += 10;
//通知調用方
if(null!= mOnProgressListener){
mOnProgressListener.onProgress(progress);
}
try{
Thread.sleep(1000);
}catch (Exception e){
e.printStackTrace();
}
}
}
}).start();
}
public void startSetText(){
progress = 19;
if(null!= mOnProgressListener){
mOnProgressListener.onProgress(progress);
}
}
}
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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
Service是四大組件之一,記得在清單文件中注冊。
Activity類
public class ServiceActivity extends AppCompatActivity {
private SevenService mSevenService;
private int progress;
private TextView service_text;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_service);
service_text = (TextView) findViewById(R.id.service_text);
Log.e("ServiceActivity","Thread: " + Thread.currentThread().getName());//Thread: main
//
Intent intent = new Intent(this, SevenService.class);
// 標志位BIND_AUTO_CREATE使得服務中onCreate得到執行,onStartCommand不會執行
bindService(intent, conn, Context.BIND_AUTO_CREATE);
}
//點擊按鈕
public void bt_download(View view){
// mSevenService.startDownload();//這個方法內部開啟了子線程
mSevenService.startSetText();
}
/***
* 通過一系列驗證,終於確定了:ServiceConnection的onServiceConnected方法是運行在主線程的,可以直接更新UI,
*/
ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
//綁定成功,回調這個方法
mSevenService = ((SevenService.SevenBinder)service).getService();
Log.e("onServiceConnected","Thread: " + Thread.currentThread().getName());//Thread: main
mSevenService.setOnProgressListener(new SevenService.OnProgressListener() {
@Override
public void onProgress(final int progress) {
Log.e("setOnProgressListener","Thread: " + Thread.currentThread().getName());//Thread: Thread-9894
//如果點擊按鈕調用的是startSetText()方法,這里就可以在回調中直接更新UI。
service_text.setText(""+progress);
//這里不能直接更新UI的原因是:在SevenService中,startDownload()方法開啟了子線程,所以mOnProgressListener.onProgress(progress);是在子線程中執行的
//android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
// runOnUiThread(new Runnable() {
// @Override
// public void run() {
// service_text.setText(""+progress);
// }
// });
}
});
}
@Override
public void onServiceDisconnected(ComponentName name) {
//如果Service中途掛掉,client端也能通過onServiceDisconnected感知到(通過Binder的linkToDeath實現)
}
};
@Override
protected void onDestroy() {
unbindService(conn);//解綁
super.onDestroy();
}
}
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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
從打印的日志也可以得到上述結論:
這里寫圖片描述
---------------------
作者:唐人小調
來源:CSDN
原文:https://blog.csdn.net/gengbaolong/article/details/78574225
版權聲明:本文為博主原創文章,轉載請附上博文鏈接!