1. 什么是服务(service)
官方给出的解释:
A Service is an application component that can perform long-running operations in the background, and it doesn’t provide a user interface. Another application component can start a service, and it continues to run in the background even if the user switches to another application
Service 是一个可以在后台执行长时间运行操作而不提供用户界面的应用组件。服务可由其他应用组件启动,而且即使用户切换到其他应用,服务仍将在后台继续运行。要注意的是服务默认在应用程序的主线程中运行,如果要在服务中进行耗时操作,建议使用 IntentService。
2. 基本使用
启动 service 的方法有两种,一种是 startService(),一种是 bindService()。不同的启动方式有不同的生命周期。
2.1 startService()
这种方式比较简单,在 AndroidManifest.xml 文件中注册后就可启动
// 启动服务
startService(Intent(this, ComponentService::class.java))
// 结束服务
stopService(Intent(this, ComponentService::class.java))
开启服务时,Service 会调用 onCreate() 方法,然后在调用 onStartCommand() ,多次调用 startService() 启动统一服务,该服务只会调用一次 onCreate() 方法,之后再调用直接执行 onStartCommand() ,如果想结束服务,调用 stopService() 或者在 Service 中调用 stopSelf() 即可。如果不调用,该服务会一直存在,直到应用结束,或者内存不足被回收。
2.2 bindService()
绑定服务
val conn = object:ServiceConnection{
override fun onServiceDisconnected(name: ComponentName?) {
//只有当我们自己写的MyService的onBind方法返回值不为null时,才会被调用
Log.i(TAG, "onServiceDisconnected")
}
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
//这个方法只有出现异常时才会调用,服务器正常退出不会调用。
// (service as ComponentService.MyBinder).print()
Log.i(TAG, "onServiceConnected")
}
}
// 绑定服务
bindService(Intent(this,ComponentService::class.java),conn, Context.BIND_AUTO_CREATE)
解绑服务
unbindService(conn)
被绑定了的服务,一定要解绑,如果在 activity 中绑定,在退出 activity 时没有解绑,会造成内存泄漏。
E/ActivityThread: Activity lxy.com.kotlinandroid.component.Component2Activity has leaked ServiceConnection lxy.com.kotlinandroid.component.Component2Activity$conn$1@62f65fb that was originally bound here
android.app.ServiceConnectionLeaked: Activity lxy.com.kotlinandroid.component.Component2Activity has leaked ServiceConnection lxy.com.kotlinandroid.component.Component2Activity$conn$1@62f65fb that was originally bound here
同时,unbindService() 只能调用一次,多次调用会报错。
E/AndroidRuntime: FATAL EXCEPTION: main
Process: lxy.com.kotlinandroid, PID: 16281
java.lang.IllegalArgumentException: Service not registered: lxy.com.kotlinandroid.component.Component2Activity$conn$1@e631618
总结一下,通过 bindService() 启动的服务,生命周期为 onStart() => onBind() => onUnbind() —> onDestroy()。需要注意的是关闭服务需要 stopService 和 unbindService 都被调用,没有先后顺序的影响,只调用其中一个方法,onDestory() 都不会执行,服务也不会被关闭。
同时要注意的是 service 运行并不是单独的一个进程或线程,它是运行在主线程中的,所以尽可能的不在service 中进行耗时操作,否则会引起 ANR (前台服务20s,后台服务200s)。如果实在要在 service 中进行耗时操作的话,可以使用 IntentService 。
IntentService
IntentService 继承 Service,与至不同的是 IntentService 在创建之后会另起一个线程,耗时操作可以运行在该线程中。其实主要是继承一个方法:
override fun onHandleIntent(intent: Intent?) {
// 执行耗时操作
}
那么为什么 IntentService 不用担心 ANR 问题呢?这个可以去源码里面找答案。
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);
}
}
/**
* Creates an IntentService. Invoked by your subclass's constructor.
*
* @param name Used to name the worker thread, important only for debugging.
*/
public IntentService(String name) {
super();
mName = name;
}
/**
* Sets intent redelivery preferences. Usually called from the constructor
* with your preferred semantics.
*
* <p>If enabled is true,
* {@link #onStartCommand(Intent, int, int)} will return
* {@link Service#START_REDELIVER_INTENT}, so if this process dies before
* {@link #onHandleIntent(Intent)} returns, the process will be restarted
* and the intent redelivered. If multiple Intents have been sent, only
* the most recent one is guaranteed to be redelivered.
*
* <p>If enabled is false (the default),
* {@link #onStartCommand(Intent, int, int)} will return
* {@link Service#START_NOT_STICKY}, and if the process dies, the Intent
* dies along with it.
*/
public void setIntentRedelivery(boolean enabled) {
mRedelivery = enabled;
}
@Override
public void onCreate() {
// TODO: It would be nice to have an option to hold a partial wakelock
// during processing, and to have a static startService(Context, Intent)
// method that would launch the service & hand off a wakelock.
super.onCreate();
HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
thread.start();
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
}
@Override
public void onStart(@Nullable Intent intent, int startId) {
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
msg.obj = intent;
mServiceHandler.sendMessage(msg);
}
/**
* You should not override this method for your IntentService. Instead,
* override {@link #onHandleIntent}, which the system calls when the IntentService
* receives a start request.
* @see android.app.Service#onStartCommand
*/
@Override
public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
onStart(intent, startId);
return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
}
@Override
public void onDestroy() {
mServiceLooper.quit();
}
/**
* Unless you provide binding for your service, you don't need to implement this
* method, because the default implementation returns null.
* @see android.app.Service#onBind
*/
@Override
@Nullable
public IBinder onBind(Intent intent) {
return null;
}
/**
* This method is invoked on the worker thread with a request to process.
* Only one Intent is processed at a time, but the processing happens on a
* worker thread that runs independently from other application logic.
* So, if this code takes a long time, it will hold up other requests to
* the same IntentService, but it will not hold up anything else.
* When all requests have been handled, the IntentService stops itself,
* so you should not call {@link #stopSelf}.
*
* @param intent The value passed to {@link
* android.content.Context#startService(Intent)}.
* This may be null if the service is being restarted after
* its process has gone away; see
* {@link android.app.Service#onStartCommand}
* for details.
*/
@WorkerThread
protected abstract void onHandleIntent(@Nullable Intent intent);
}
通过源码可以看出,在 onCreate() 方法中开启了一个线程,同时初始化了一个 handler ,在 onStartCommand() 方法中调用 onStart() 使用 handler 发送一个无延迟的消息,然后调用了我们实现的 onHandleIntent() 方法,执行耗时操作,所以不用担心 ANR。
注意,IntentService 在处理完所有启动请求后停止服务,因此不必调用 stopSelf()。