目录
本文介绍了Android四大组件之一Service的基本概念和常见用法。包括服务的生命周期及其两种启停方式——普通方式和绑定方式(含立即绑定和延迟绑定),还介绍了如何在活动和服务之间交互数据。
服务的启动和停止
既然Android自带了系统服务,App也可以拥有自己的服务。Service与Activity相比,不同之处在于没有对应的页面,相同之处在于都有生命周期。想要用好服务,就要弄清楚它的生命周期。
Service与生命周期有关的说明如下:
- onCreate:创建服务。
- onStart:开始服务,Android 2.0以下版本使用,现已废弃。
- onStartCommand:开始服务,Android 2.0及以上版本使用。该方法的返回值说明见表
返回值类型 | 返回值说明 |
---|---|
START_STICKY | 黏性的服务。如果服务进程被杀掉,就保留服务的状态为开始状态,但不保留传送的Intent对象。随后系统尝试重新创建服务,由于服务状态为开始状态,因此创建服务后一定会调用onStartCommand方法。如果在此期间没有任何启动命令传送给服务器,参数Intent就为空值 |
START_NOT_STICKY | 非黏性服务。使用这个返回值时,如果服务被异常杀掉,系统就不会自动重启该服务 |
START_REDELIEVER_INTENT | 重传Intent的服务。使用这个返回值时,如果服务被异常杀掉,系统就会自动重启该服务,并传入Intent的原值 |
START_STICKY_COMPATIBILITY | START_STICKY的兼容版本,但不保证服务被杀掉后一定能重启 |
- onDestroy:销毁服务。
- onBind:绑定服务。
- onUnbind:解除绑定。返回值为true表示允许再次绑定,之后在绑定服务时,不会调用onBind方法而是调用onRebind方法;返回值为false表示只能绑定一次,不能再次绑定。
- onRebind:重新绑定。只有上次的onUnbind方法返回true时,再次绑定服务才会调用onRebind方法。
服务的生命周期也存在好几个环节,除了必须的onCreate方法和onDestroy方法,还有其他几种生命周期方法。接下来已普通启停为例,讲解服务的生命周期过程。
首先在Java代码包下面创建名为service的新包,右击该包并在右键菜单中依次选择New→Service→Service,弹出下示服务创建对话框。
在服务创建对话框的Class Name一栏填写服务名称,比如NormalService ,再点击Finish按钮,Android Studio便自动再service包下生成NormalService .java,同时在AndroidManifest.xml的application节点内部添加如下的服务注册配置:
<service
android:name=".service.NormalService"
android:enabled="true"
android:exported="true" />
打开NormalService .java,为了方便观察服务的生命周期过程,需要重写该服务的所有周期方法,给每个方法都打印相应的运行日志。
public class NormalService extends Service {
private static final String TAG = "NormalService";
private void refresh(String text) {
Log.d(TAG, text);
Service1.showText(text);
}
@Override
public void onCreate() { // 创建服务
super.onCreate();
refresh("onCreate");
}
@Override
public int onStartCommand(Intent intent, int flags, int startid) { // 启动服务,Android2.0以上使用
Log.d(TAG, "测试服务到此一游!");
refresh("onStartCommand. flags=" + flags);
return START_STICKY;
}
@Override
public void onDestroy() { // 销毁服务
super.onDestroy();
refresh("onDestroy");
}
@Override
public IBinder onBind(Intent intent) { // 绑定服务。普通服务不存在绑定和解绑流程
refresh("onBind");
return null;
}
@Override
public void onRebind(Intent intent) { // 重新绑定服务
super.onRebind(intent);
refresh("onRebind");
}
@Override
public boolean onUnbind(Intent intent) { // 解绑服务
refresh("onUnbind");
return true; // 返回false表示只能绑定一次,返回true表示允许多次绑定
}
}
启停服务很简单,只要创建一个指向服务的意图,然后调用startService方法即可启动服务,若要停止服务,调用stopService方法即可停止指定意图的服务。
//创建一个通往普通服务的意图
mIntent = new Intent(this, NormalService.class);
//启动指定意图的服务
startService(mIntent);
//停止指定意图的服务
stopService(mIntent);
服务的绑定与解绑
Android还提供了另一种启停方式,也就是绑定服务和解绑服务。对于服务来说,要求提供黏合剂Binder指定服务的绑定关系,同时黏合剂还负责再两个组件或者在两个进程之间的交流通信。此时增加了黏合剂的服务代码示例如下:
public class BindImmediateService extends Service {
private static final String TAG = "BindImmediateService";
private final IBinder mBinder = new LocalBinder(); // 创建一个粘合剂对象
// 定义一个当前服务的粘合剂,用于将该服务黏到活动页面的进程中
public class LocalBinder extends Binder {
public BindImmediateService getService() {
return BindImmediateService.this;
}
}
private void refresh(String text) {
Log.d(TAG, text);
Service2.showText(text);
}
@Override
public void onCreate() { // 创建服务
super.onCreate();
refresh("onCreate");
}
@Override
public void onDestroy() { // 销毁服务
super.onDestroy();
refresh("onDestroy");
}
@Override
public IBinder onBind(Intent intent) { // 绑定服务。返回该服务的粘合剂对象
Log.d(TAG, "绑定服务开始旅程!");
refresh("onBind");
return mBinder;
}
@Override
public void onRebind(Intent intent) { // 重新绑定服务
super.onRebind(intent);
refresh("onRebind");
}
@Override
public boolean onUnbind(Intent intent) { // 解绑服务
Log.d(TAG, "绑定服务结束旅程!");
refresh("onUnbind");
return true; // 返回false表示只能绑定一次,返回true表示允许多次绑定
}
}
对于绑定了黏合剂的服务,它的绑定和解绑操作与普通方式不同:首先要定义一个ServiceConnection的服务连接对象,然后调用bindService方法绑定服务,绑定之后再择机调用unbindService方法解绑服务,具体的活动代码示例如下:
public class Service2 extends AppCompatActivity implements View.OnClickListener {
private static final String TAG = "BindImmediateActivity";
private static TextView tv_immediate; // 声明一个文本视图对象
private Intent mIntent; // 声明一个意图对象
private static String mDesc; // 日志描述
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_service2);
tv_immediate = findViewById(R.id.tv_result);
findViewById(R.id.btn_start_bind).setOnClickListener(this);
findViewById(R.id.btn_unbind).setOnClickListener(this);
mDesc = "";
// 创建一个通往立即绑定服务的意图
mIntent = new Intent(this, BindImmediateService.class);
}
@Override
public void onClick(View v) {
if (v.getId() == R.id.btn_start_bind) { // 点击了绑定服务按钮
// 绑定服务。如果服务未启动,则系统先启动该服务再进行绑定
boolean bindFlag = bindService(mIntent, mServiceConn, Context.BIND_AUTO_CREATE);
Log.d(TAG, "bindFlag=" + bindFlag);
} else if (v.getId() == R.id.btn_unbind) { // 点击了解绑服务按钮
if (mBindService != null) {
// 解绑服务。如果先前服务立即绑定,则此时解绑之后自动停止服务
unbindService(mServiceConn);
}
}
}
public static void showText(String desc) {
if (tv_immediate != null) {
mDesc = String.format("%s%s %s\n", mDesc, DateUtil.getNowTime(), desc);
tv_immediate.setText(mDesc);
}
}
private BindImmediateService mBindService; // 声明一个服务对象
private ServiceConnection mServiceConn = new ServiceConnection() {
// 获取服务对象时的操作
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
// 如果服务运行于另外一个进程,则不能直接强制转换类型,否则会报错
mBindService = ((BindImmediateService.LocalBinder) service).getService();
Log.d(TAG, "onServiceConnected");
}
// 无法获取到服务对象时的操作
@Override
public void onServiceDisconnected(ComponentName name) {
mBindService = null;
Log.d(TAG, "onServiceDisconnected");
}
};
}
为了更好的说明绑定方式的优势,Android还提供了另一种延迟绑定。延迟绑定与立即绑定的区别在于:延迟绑定要先通过startService方法启动服务,再通过bindService方法绑定已存在服务;同理延迟解绑要先通过unbindService方法解绑服务,再通过stopService方法停止服务。这样一来因为启动操作在先、绑定操作在后,所以解绑操作只能撤销绑定操作,而不能撤销启动操作。由于解绑操作不能销毁服务,因此存在再次绑定的可能。
从上面两个日志界面可知,延迟绑定和立即绑定这两种方式的生命周期区别在于:
- 延迟绑定的首次绑定操作只触发onBind方法,再次绑定操纵只触发onRebind方法(是否允许再次绑定要看上次onUnbind方法的返回值)。
- 延迟绑定的解绑操作只触发onUnbind方法。
活动与服务之间的交互
若想及时获取服务的运行情况,活动就得主动打探消息,此时需要有个信使承担消息传输的任务,这个信使便是绑定方式用到的服务黏合剂——IBinder。
由于IBinder是个接口,它的实现类名叫Binder,因此每个服务的黏合剂都得从Binder派生而来。除了定义getService方法返回当前的服务对象之外,黏合剂还可以定义一般的数据交互方法,用于同活动代码往来通信。
public class DataService extends Service {
private static final String TAG = "DataService";
private final IBinder mBinder = new LocalBinder(); // 创建一个粘合剂对象
// 定义一个当前服务的粘合剂,用于将该服务黏到活动页面的进程中
public class LocalBinder extends Binder {
public DataService getService() {
return DataService.this;
}
// 获取数字描述
public String getNumber(int number) {
return "我收到了数字"+number;
}
}
@Override
public IBinder onBind(Intent intent) { // 绑定服务。返回该服务的粘合剂对象
Log.d(TAG, "绑定服务开始旅程!");
return mBinder;
}
@Override
public boolean onUnbind(Intent intent) { // 解绑服务
Log.d(TAG, "绑定服务结束旅程!");
return true; // 返回false表示只能绑定一次,返回true表示允许多次绑定
}
活动代码在调用bindService方法时,第二个参数为ServiceConnection类型,表示绑定结果的连接对象。这个连接对象来自接口ServiceConnection,它的onServiceConnected方法在连接成功时回调,onServiceDisconnected方法在连接断开时回调。重写ServiceConnection的onServiceConnected方法,即可拿到已绑定服务的黏合剂对象。有了服务的黏合剂,才能通过黏合剂获取服务的内部情况。
private DataService.LocalBinder mBinder; // 声明一个粘合剂对象
private ServiceConnection mServiceConn = new ServiceConnection() {
// 获取服务对象时的操作
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
// 如果服务运行于另外一个进程,则不能直接强制转换类型,否则会报错
mBinder = (DataService.LocalBinder) service;
// 活动代码通过粘合剂与服务代码通信
String response = mBinder.getNumber(new Random().nextInt(100));
tv_result.setText(DateUtil.getNowTime()+" 绑定服务应答:"+response);
Log.d(TAG, "onServiceConnected");
}
// 无法获取到服务对象时的操作
@Override
public void onServiceDisconnected(ComponentName name) {
mBinder = null;
Log.d(TAG, "onServiceDisconnected");
}
};