一、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
的任务被执行完后,该服务会自动停止。