Android组件学习-Service

什么是Service?

Service是一个后台运行(当然也可以设置成前台运行)的组件, 执行长时间运行且不需要用户交互的任务. 即使应用被销毁也依然可以工作.

这里需要说明的是, Service是后台运行, 但是它仍然运行在主线程. 如果你要在Service内进行网络操作的话, 还是需要另外开一个线程. Service的意义在于, 即使应用销毁了, 异步任务也可以继续进行.

Service的三种形式

Service有启动、绑定、启动且绑定三种形式. 启动形式指调用startService方法启动一个Service, 以这种方式启动的Service会一直执行任务, 即使启动它的组件已经被销毁, 需要人为通过StopService或stopSelf停止; 绑定形式指调用bindService方法将一个服务绑定在组件上, 这种服务会提供一个接口, 允许组件与服务交互, 多个组件可以同时绑定服务, 当所有绑定的组件销毁后, 服务也会被销毁; 启动且绑定指时重写onStartCommand和onBind两个方法, 允许服务与组件交互, 但组件被销毁后服务不会被销毁, 需要人为停止.

Service的创建

使用Service之前, 需要在Manifest文件中声明服务的类名.

创建一个Service的子类时, 一般需要重写下面的几个方法:

  1. onStartCommand方法. 当组件通过startService启动一个Service时, 系统将调用此方法. 一旦调用此方法, 需要调用stopService或stopSelf来人为停止这个Service.

  2. onBind方法. 当组件通过bindService启动一个Service时, 系统将调用此方法. 此方法必须返回一个IBinder, 为组件与Service交互提供接口.

  3. onCreate. 创建服务时, 系统会调用此函数初始化服务. 如果服务已经运行则不会调用此方法.

  4. onDestroy. 当Service不再使用且即将被销毁时, 系统将调用此方法.

示例

示例代码

为了演示Service的三种启动方法, 我写了一个简单的demo. 没有注释, 因为这demo太简单了.

首先是UI布局:

就四个按钮, 很简单吧?

然后是Service的类:

Example
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
public class MyService extends Service {
static class MyBinder extends Binder{
public void execute(){
Log.i("tag","信息传递");
}
}

private MyBinder myBinder;
@Nullable
@Override
public IBinder onBind(Intent intent) {
Log.i("tag", "绑定服务");
return myBinder;
}

@Override
public boolean onUnbind(Intent intent) {
Log.i("tag", "解绑服务");
return super.onUnbind(intent);
}

@Override
public void onRebind(Intent intent) {
Log.i("tag", "重绑服务");
super.onRebind(intent);
}

@Override
public void onCreate() {
Log.i("tag", "服务创建");
myBinder = new MyBinder();
super.onCreate();
}

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i("tag", "服务开始");
return super.onStartCommand(intent, flags, startId);
}

@Override
public void onDestroy() {
Log.i("tag", "服务销毁");
super.onDestroy();
}
}

代码逻辑也很简单, 我就是把Service可能会出现的状态用日志输出而已. 这里要说明的是我写的MyBinder的内部类. 上面说了, 在使用绑定服务的时候, 服务需要提供一个接口, 这个接口用来进行服务与控件的交互. 在这里, 继承了Binder的MyBinder类就是一个接口. 它的execute方法大家可以理解为一个实现业务逻辑的方法, 当然大家也可以写很多方法, 并不是说只能写一个方法.

然后是Activity的代码:

Example
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 MainActivity extends AppCompatActivity {
private MyService.MyBinder myBinder;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

Button start = findViewById(R.id.start_btn);
Button end = findViewById(R.id.end_btn);

start.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(MainActivity.this, MyService.class);
startService(intent);
}
});

end.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(MainActivity.this, MyService.class);
stopService(intent);
}
});


final ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.i("tag", "链接到服务");
myBinder = (MyService.MyBinder)service;
myBinder.execute();
}

@Override
public void onServiceDisconnected(ComponentName name) {
Log.i("tag", "断开连接");
}
};

Button bind = findViewById(R.id.bind_btn);
Button unbind = findViewById(R.id.unbind_btn);

bind.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(MainActivity.this, MyService.class);
bindService(intent, serviceConnection, BIND_AUTO_CREATE);
}
});

unbind.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
unbindService(serviceConnection);
}
});
}
}

代码也很好懂, 这里就不再做解释了. 大家要好好看这个代码, 这就是最基本的管理服务的操作.

看完了代码, 我们来看一看实际效果.

启动模式的效果

首先我们看一下启动模式. 依次点击开始服务和结束服务, 看一看日志中会有什么输出:

可以看到这个过程用到了Service的onCreate、onStartCommand和onDestory方法.

然后我们再试一下点击两次开始服务, 接着点击结束服务:

可以看到, onCreat方法只被调用了一次, 这也是意料之中的.

这里再说另外两种不好展示的情况. 首先是只点击结束服务, 日志没有任何输出; 然后是只点击开始服务, 然后杀死进程, 服务会在进程被杀死后自动调用onDestory方法.

绑定模式

然后我们看一下绑定模式. 首先依次点击绑定和解绑:

我们看到, 点击绑定按钮(即调用bindService方法)后, 会依次执行onCreate、onBind、onServiceConnected和execute方法; 点击解绑(即调用unbindService方法)后, 会依次执行unbind和onDestory方法.

然后我们看一下点击两次绑定后再点解绑:

可以看到, 第二次点击绑定并没有什么作用, 这是因为MainActivity已经绑定在了MyService上.

这里再说两种不好展示的情况. 首先是只点击解绑, 程序会直接崩溃; 然后是只点击绑定接着杀死进程, 这种情况下不会调用onDestory和onDestory, **可能存在服务没有杀死的情况, 因此强烈不建议这种情况发生! **

启动绑定模式

这个模式不太好展示, 我就直接说一下结果, 大家可以自己跑一下demo验证.

首先是先点击绑定. 在这种情况下, 再点击开始只会执行onStartCommand方法; 再点击结束会没有任何效果; 再点击解绑会接触绑定并销毁Service.

然后是先点击开始再点击绑定. 在这种情况下, 再点击结束没有任何效果; 再点击解绑只会接触绑定. 在点击结束后解绑会解除绑定并销毁Service; 在解绑后点击结束会销毁Service.

根据上面的实验结果, 我们可以得到Service销毁的条件: **当Service既没有启动也没有绑定时, Service才会被销毁. **

IntentService

最后再提一下IntentService. IntentService主要是用来解决多线程与Service交互的问题. IntentService包含一个队列, 每一次处理时, IntentService只会处理队首的请求.

IntentService默认实现了Service的一系列函数, 它不需要我们人为地来销毁. 在使用它时, 我们只需要重写onHandleIntent方法就可以了.