一、Service 常见问题
由于Service比较简单,就不介绍基础的东西了,这篇文章主要是用于记录使用Service过程中遇到的一些问题。
1.1 描述 Service 的生命周期
Service的生命周期如下图所示:

Service的启动方式有两种,startService和bindService,先 单独 地看一下这两种方式的调用。
-
startService:如果该Service没有运行,那么会先调用onCreate方法,之后再回调onStartCommand方法,如果再次启动已经在运行的Service,仍然会回调该方法。如果调用了stopService,那么会回调onDestroy方法。 -
bindService:如果该Service没有运行,那么会先调用onCreate方法,之后再回调onBind方法,如果再次启动已经在运行的Service,仍然会回调。如果调用了unBindService,那么会回调onBind和onDestroy方法。
假如同时通过start和bind方式启动了Service,那么必须保证stopService和unBindService都调用后,Service才会被销毁。
1.2 onStartCommand 返回值的含义
onStartCommand决定了Service被系统杀死后的处理行为。
START_NOT_STICKY
如果Service在onStartCommand返回之后被杀死,它不会重启。
START_STICKY
如果Service在onStartCommand返回之后被杀死,并在稍后 尝试重新创建 这个Service,依次回调onCreate、onStartCommand,onStartCommand当中传入的Intent将为null。
START_REDELIVER_INTENT
如果Service在onStartCommand返回之后被杀死,那么系统会重新创建这个Service,依次回调onCreate、onStartCommand,onStartCommand当中的Intent为最后一次传递的Intent。
1.3 同一进程通过 Service 进行通信
前面我们有谈到Service启动的两种方式,下面我们介绍一下在同一进程中,如何和Service进行交互。
1.3.1 startService 下的交互
通过startService启动服务的时候,只能通过onStartCommand当中的Intent进行交互,Service根据Intent中的action区分行为,intent的数据作为输入参数。
这种方式的优点是 简单,缺点是 这种通信方式是单向的,只能由调用者告诉Service做什么,Service无法返回给调用者信息。
/**
* @author lizejun
**/
public class CommandWorkerService extends Service {
private static final String TAG = CommandWorkerService.class.getSimpleName();
public static final String ACTION_LOG = "com.android.action.log";
public static final String ACTION_KEY_LOG_MSG = "com.android.action.log.msg";
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
handleIntent(intent);
return super.onStartCommand(intent, flags, startId);
}
private void handleIntent(Intent intent) {
String action = intent.getAction();
if (!TextUtils.isEmpty(action)) {
switch (action) {
case ACTION_LOG:
Log.d(TAG, intent.getStringExtra(ACTION_KEY_LOG_MSG));
break;
default:
break;
}
}
}
}
调用者的处理方式:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
startService();
}
@Override
protected void onDestroy() {
super.onDestroy();
stopService();
}
private void startService() {
Intent intent = new Intent(this, CommandWorkerService.class);
intent.setAction(CommandWorkerService.ACTION_LOG);
intent.putExtra(CommandWorkerService.ACTION_KEY_LOG_MSG, "Call CommandWorkerService");
startService(intent);
}
private void stopService() {
Intent intent = new Intent(this, CommandWorkerService.class);
stopService(intent);
}
}
1.3.2 bindService 下的交互
首先定义调用者和Service之间交互的接口。
/**
* 契约类,定义和 Service 之间交互的接口。
*
* @author lizejun
**/
public interface IBindWorker {
/**
* 调用方法。
*/
int add(int a, int b);
}
实现Service,在onBind方法中,返回一个Binder的子类,同时实现了契约类的接口。
/**
* @author lizejun
**/
public class BindWorkerService extends Service {
@Nullable
@Override
public IBinder onBind(Intent intent) {
return new BindWorker();
}
private class BindWorker extends Binder implements IBindWorker {
@Override
public int add(int a, int b) {
return a + b;
}
}
}
实现调用者,在bindService方法调用时,会要求传入ServiceConnection的子类,在该子类的onServiceConnected会返回一个IBinder,这个就是与Service交互的桥梁,将它转型为契约类,通过它来调用相应的接口。
public class MainActivity extends AppCompatActivity {
private ServiceConnection mConnection;
private IBindWorker mWorker;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
bindService();
findViewById(R.id.tv_hello).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (mWorker != null) {
//调用接口方法。
Log.d("IBindWorker", "result=" + mWorker.add(1, 2));
}
}
});
}
@Override
protected void onDestroy() {
super.onDestroy();
unBindService();
}
private void bindService() {
Intent intent = new Intent(this, BindWorkerService.class);
mConnection = new BindWorkerConnection();
bindService(intent, mConnection, Service.BIND_AUTO_CREATE);
}
private void unBindService() {
unbindService(mConnection);
}
private class BindWorkerConnection implements ServiceConnection {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
//通过返回的 IBinder 子类,与 Service 进行交互。
mWorker = (IBindWorker) service;
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
}
}
1.4 不同进程通过 Service 进行通信
在不同的进程之间进行通信,需要采用AIDL来实现,这个在 Framework 源码解析知识梳理(3) - 应用进程之间的通信实现 中有详细的介绍。
1.5 Service 和 Thread 对比
1.5.1 定义区别
-
Thread是线程执行的最小单元,也是分配CPU的基本单位。 -
Service是Android的四大组件之一,通过Binder实现没有UI的后台服务。
1.5.2 使用场景
- 由于
Android中不允许在主线程执行耗时的操作,因此常通过Thread将耗时的任务放在子线程中进行。 -
Service默认是运行于主线程中的,因此不可以在其中执行耗时的操作,否则会产生ANR,它适合于长时间运行在后台,且不需要交互的场景。
1.6 前台服务
前台服务相比于普通的后台Service有更高的优先级,因此在内存不足时,也不会考虑将其终止。前台服务要求提供通知栏。
-
startForeground(int id, Notification notification):将当前服务设为前台服务,id参数表示唯一标识通知的整数,不允许为0,Notification是一个通知栏的通知。 -
stopForeground(boolean removeNotification)从前台删除服务,boolean表示是否也删除状态栏通知,该方法并不会停止服务。
1.7 Android 5.0 之后不允许隐式启动服务
在Android 5.0之后,启动服务的时候必须要提供component或者package,否则会抛出异常。
1.8 IntentService
IntentService继承于Service,因此我们可以像start普通Service那样启动它。
它常被用于在 后台执行单次的耗时任务,任务的执行逻辑放在onHandleIntent方法当中。它内部是采用HandlerThread来实现的,因此如果有多个任务,那么将会排队等待执行。
在onHandleIntent执行完后,会调用stopSelf(int startId)。
public abstract class IntentService extends Service {
private volatile Looper mServiceLooper;
private volatile ServiceHandler mServiceHandler;
private String mName;
private boolean mRedelivery;
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
onHandleIntent((Intent)msg.obj);
stopSelf(msg.arg1);
}
}
这个方法和stopSelf()的区别在于,stopSelf(int startId) 在只有最后依次启动服务的 ID 与它相同时,才会停止服务,也就是说,当最后一次发送给IntentService的任务被执行完后,该服务会自动停止。