概要
本文主要讲解Service与Activity进行通信的几种方式,Activity主要通过Intent出传递数据到Service,而比较常见的Service传递数据到Activity有三种方式,但是这次所介绍的Service与Activity的通信方式都是位于同一应用进程中的,并没有介绍不同进程之间如何通信,不同进程通过aidl或者Messenger在以后的文章中会再做详细的介绍。
Service和Activity是Android中最常用的两大组件,Activity的设计与Web页面非常类似,从页面的跳转通过连接,以及从页面的定位通过URL,从每个页面的独立封装等方面都可以看出来,它主要负责与用户进行交互。Service则是在后台运行,默默地为用户提供功能,进行调度和统筹,它用来“执行”后台的、耗时的、重要的任务,三者缺一不可,而最重要的原因是第三点:要执行重要的任务。
从上面可以看出,Activity负责与用户的直接交互,Service是运行在后台用户不可见的而且常常执行的是重要的任务,既然是重要的任务然而用户却不知道任务的执行情况,比如下载,用户想知道下载的进度,这种情况下就需要与Activity通信,方面用户实时知道任务的执行情况。
Activity传递数据到Service
这种方式传递数据基本上是依赖Intent来传递数据的,更为详细的请参考Intent学习笔记,在Activity中可以使用两种方式来与Service进行交互,一种是通过bindService(Intent service, ServiceConnection conn, int flags)
方法,另一种是startService(Intent service)
,当然了也可以通过广播来与服务通信,但是个人感觉Activity通过Broadcast来通信就有点多此一举了,但是反过来则是可行的,在Service中通过Broadcast来传递数据到Activity。
接下来介绍最常见的通过Service传递数据到Activity的三种方式。
方式一:通过Binder
在以前的一篇文章Service简介及启动方式就已经介绍过,Service主要提供两种功能,其中之一就是将应用程序的某些功能暴露给其它程序,该种方式就是通过Binder来实现的。
我们知道在每创建一个Service实例是都要实现一个方法onBind(Intent intent)
,该方法返回一个IBinder类型的实例,IBinder是远程对象的基本接口,是为高性能而设计的轻量级远程调用机制的核心部分。但它不仅用于远程调用,也用于进程内调用。这个接口定义了与远程对象交互的协议。不要直接实现这个接口,而应该从Binder派生。
下面是一个简单的Service实现:
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
|
public class MyService extends Service {
@Override
public IBinder onBind(Intent intent) {
return new MyBinder();
}
public class MyBinder extends Binder {
private MyService service;
public MyBinder() {
service = MyService.this;
}
public String getName() {
return "admin";
}
public void startPlay() {
service.startPlay();
}
}
private void startPlay() {
System.out.println("start play");
}
}
|
当我们在Activity通过bindService(Intent service, ServiceConnection conn, int flags)来绑定一个Service时,bindService会立即返回,它不会返回Ibinder给客户端,要接收Ibinder,客户端Activity必须创建一个长连接ServiceConnection的实例并传给bindService(),ServiceConnection包含一个回调方法,系统调用这个方法来传递要返回的IBinder。
下面是Activity的代码:
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
|
public class MainActivity extends Activity {
private MyBinder service;
private MyConnection conn;
private Intent intent;
private String str;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.layout_main);
conn = new MyConnection();
intent = new Intent(this, MyService.class);
}
public void binderClick(View v) {
bindService(intent, conn, Context.BIND_AUTO_CREATE);
//这里输出null 因为conn还没有执行完成回调函数
System.out.println("aaaaaaaaaaaaaaaa:" + str);
}
private class MyConnection implements ServiceConnection {
@Override
public void onServiceConnected(ComponentName name, IBinder binder) {
service = (MyBinder) binder;
str = service.getName();
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
}
@Override
protected void onDestroy() {
super.onDestroy();
//注销长连接
unbindService(conn);
}
}
|
在本实例中,主要是通过IBinder来建立一个Service与Activity的一个桥梁,通过Binder对象来调用Service中的方法,当然了也可以在Service中在新建一个Binder对象时将Service对象本身作为一个方法的返回值返回,然后我们在Activity中就可以直接通过ServiceConnection长连接回调函数中的IBinder对象来得到一个Service实例,这样就可以使用Service中暴露的特定方法了,这种方式好像更简洁了,IBinder的目的就是为了将Service对象传递到Activity,其余的事情都不用管了。
方式二:通过Broadcast广播
在上一篇文章中我们已经讲过Broadcast可以用于应用程序之间的通信,更多的内容可以查看Android Broadcast广播机制。
Service的代码实现:
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
|
public class BService extends Service {
private int progress = 0;
//创建一个Intent,设置Action为com.sunny.demo.RECEIVER
private Intent intent = new Intent("com.sunny.demo.RECEIVER");
public IBinder onBind(Intent intent) {
return new BBinder();
}
public class BBinder extends Binder {
//返回Service对象
public BService getService() {
return BService.this;
}
}
//下载方法
public void startDownload() {
new Thread(new Runnable() {
public void run() {
while (progress < 100) {
progress++;
SystemClock.sleep(200);
//设置进度 发送广播
intent.putExtra("progress", progress);
sendBroadcast(intent);
}
}
}).start();
}
}
|
Activity中代码实现:
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
|
public class BActivity extends Activity {
private ProgressBar progressBar;
private BService service;
private Intent intent;
private MyConnection conn;
private MyReceiver receiver;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.layout_b);
progressBar = (ProgressBar) findViewById(R.id.progressBar);
// 动态注册广播接收器
receiver = new MyReceiver();
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction("com.sunny.demo.RECEIVER");
registerReceiver(receiver, intentFilter);
conn = new MyConnection();
intent = new Intent(this, BService.class);
bindService(intent, conn, Context.BIND_AUTO_CREATE);
}
public class MyReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
// 拿到进度,更新UI
int progress = intent.getIntExtra("progress", 0);
progressBar.setProgress(progress);
}
}
//开始下载
public void binderClick(View v) {
service.startDownload();
}
private class MyConnection implements ServiceConnection {
@Override
public void onServiceConnected(ComponentName name, IBinder binder) {
service = ((BBinder) binder).getService();
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
}
@Override
protected void onDestroy() {
super.onDestroy();
//注销服务和广播
unbindService(conn);
unregisterReceiver(receiver);
}
}
|
方式三:自定义接口回调
事实上方式一也是一种接口回调,只是回调接口中传递的是IBinder的一个实例而已,这里我们们仍然会借助第一种方式的接口回调来获取IBinder的一个实例,然后通过该实例获取相对应得Service实例,再通过Service中自定义的回调接口将下载的进度实时更新。先看一下回调接口的定义:
1
2
3
4
|
//自定义下载回调借口
public interface ProgressListener {
void onProgress(int progress);
}
|
Service代码:
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
|
public class CallbackService extends Service {
private int progress = 0;
private ProgressListener progressListener;
public IBinder onBind(Intent intent) {
return new CallbackBinder();
}
public class CallbackBinder extends Binder {
//获取service
public CallbackService getService() {
return CallbackService.this;
}
}
public void startDownload() {
new Thread(new Runnable() {
public void run() {
while (progress < 100) {
progress++;
SystemClock.sleep(200);
//更新进度
progressListener.onProgress(progress);
}
}
}).start();
}
public void setProgressListener(ProgressListener progressListener) {
this.progressListener = progressListener;
}
}
|
Activity的部分代码:
1
2
3
4
5
6
7
8
9
10
11
12
|
public void onServiceConnected(ComponentName name, IBinder binder) {
//通过binder得到service
service = ((CallbackBinder) binder).getService();
//设置service的回调监听
service.setProgressListener(new ProgressListener() {
@Override
public void onProgress(int progress) {
//设置进度更新
progressBar.setProgress(progress);
}
});
}
|
小结
本篇文章虽然说是三种方式实现Service传递数据到Activity,可是归根到底仍然只是一种方式,都是借助于IBinder暴露Service中的相应操作。网上还有些文章中提到用观察者模式,观察者模式也是接口回调的一种方式,xUtils框架中的下载直接使用Service中的静态方法来获取了下载管理器DownloadManager,它的这种方式是直接在Activity或者Fragment中使用了DownloadService,然后在获取下载管理器DownloadManager的方法中再启动Service,简单来说就是调用一个类的静态方法,这种方式跟面向对象中在一个类中调用另一个类的静态方法一样,不需要借助任何纽带桥梁直接创建调用,应用程序之间通信,大概就这么两种形式,一种是通过一个桥梁纽带来传递数据,另外一种就是直接在调用者中使用被调用者。