本篇文章开始来分析Service相关原理,Service在开发中使用的相对较少,它主要用来处理后台任务。我们来看看官方对它的定义,如下所示:
Service 是一个可以在后台执行长时间运行操作而不提供用户界面的应用组件。服务可由其他应用组件启动,而且即使用户切换到其他应用,服务仍将在后台继续运行。 此外,组件可以绑定到服务,以与之进行交互,甚至是执行进程间通信 (IPC)。 例如,服务可以处理网络事务、播放音乐,执行文件 I/O 或与内容提供程序交互, 而所有这一切均可在后台进行。
Service从使用方式上可以分为两种,如下所示:
当应用组件(如 Activity)通过调用 startService() 启动服务时,服务即处于“启动”状态。一旦启动,服务即可在后台无限期运行,即使启动服务的组件已被销毁也 不受影响。 已启动的服务通常是执行单一操作,而且不会将结果返回给调用方。例如,它可能通过网络下载或上传文件。 操作完成后,服务会自行停止运行。
当应用组件通过调用 bindService() 绑定到服务时,服务即处于“绑定”状态。绑定服务提供了一个客户端-服务器接口,允许组件与服务进行交互、发送请求、获取结果,甚 至是利用进程间通信 (IPC) 跨进程执行这些操作。 仅当与另一个应用组件绑定时,绑定服务才会运行。 多个组件可以同时绑定到该服务,但全部取消绑定后,该服务即会被 销毁。
👉 注:我们虽然分开讨论这两种使用方式,但并不意味着Service只有启动状态或者绑定状态,状态的确定只依赖于是否实现了一些回调方法:onStartCommand() 允许组件启动服务, onBind() 允许组件绑定服务。如果同时实现了这两种回调方法,那一个Service可以同时处于启动和绑定状态。
另外,Service也是运行在主线程里的,所以它和Activity一样,如果在里面执行一些耗时操作,也是会引起ANR的,所以Service里的耗时操作也需要单独开新线程来处理。
我们接着来看看Service的生命周期。👇
Service和Activity一样也有自己的生命周期,只不过没有那么复杂,具体说来,如下所示:
nStartCommand()的返回值用来表示系统如何在Service停止的情况下继续运行Service,如下所示:
应用通过startService()或者bindService()方法去启动或者绑定Service的过程主要是通过ActivityManagerService来完成,Service启动的过程除了Service组件的创建 还包括Service所在进程(如果没有创建的话)的创建,具体流程如下图所示:
Service启动流程序列图如下图所示:
从整个序列图我们还可以看出,Service的启动流程涉及到4个进程,按颜色划分,如下所示:
Service的启动流程如下所示:
从上述序列图可以看出,最终创建Service的是ApplicationThread里的
public final class ActivityThread {
private void handleCreateService(CreateServiceData data) {
//当应用处于后台即将进行GC,而此时又被调回到活动状态,则跳过此次GC。
unscheduleGcIdler();
//LoadedApk描述一个呗加载到系统里的APK
LoadedApk packageInfo = getPackageInfoNoCheck(
data.info.applicationInfo, data.compatInfo);
Service service = null;
try {
java.lang.ClassLoader cl = packageInfo.getClassLoader();
// 1. 通过反射创建Service对象。
service = (Service) cl.loadClass(data.info.name).newInstance();
} catch (Exception e) {
if (!mInstrumentation.onException(service, e)) {
throw new RuntimeException(
"Unable to instantiate service " + data.info.name
+ ": " + e.toString(), e);
}
}
try {
if (localLOGV) Slog.v(TAG, "Creating service " + data.info.name);
// 2. 创建ContextImpl对象。
ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
context.setOuterContext(service);
// 3. 创建Application对象。
Application app = packageInfo.makeApplication(false, mInstrumentation);
service.attach(context, this, data.info.name, data.token, app,
ActivityManagerNative.getDefault());
// 4. 调用Service的onCreate()方法。
service.onCreate();
mServices.put(data.token, service);
try {
// 5. 调用服务创建完成,执行一些收尾工作。
ActivityManagerNative.getDefault().serviceDoneExecuting(
data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
} catch (Exception e) {
if (!mInstrumentation.onException(service, e)) {
throw new RuntimeException(
"Unable to create service " + data.info.name
+ ": " + e.toString(), e);
}
}
}
}
整个启动的流程也十分简单,如下所示:
此方法执行完成以后,便走到了Service的生命周期方法onCreate()里了,此时Service就被启动起来了。
Service绑定流程序列图如下所示:
前面我们说到Service也是运行在主线程的,所以Service里的耗时操作也会阻塞主线程,通常我们在处理耗时任务的时候会选用IntentService,它们的区别如下所示:
这是适用于所有服务的基类。扩展此类时,必须创建一个用于执行所有服务工作的新线程,因为默认情况下,服务将使用应用的主线程,这会降低应用正在运行的所有 Activity 的性能。
这是 Service 的子类,它内部创建了一个HandlerThread来逐一处理所有启动请求,使用的时候只需要实现onHandleIntent()方法即可,该方法会接收每个启动的请求的Intent,IntentService 可以用来处理耗时操作。
IntentService相比Service,多做了以下处理,如下所示:
👉 注:我们在使用Service时通常会执行一些耗时的后台任务,为了不阻塞主线程,通常会使用IntentService。
关于IntentService的实现非常简单,我们来看一下,如下所示:
public abstract class IntentService extends Service {
// 获取消息的Looper,被volatile休书,说明做了线程同步。
private volatile Looper mServiceLooper;
// 处理消息的Handler,被volatile休书,说明做了线程同步。
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
onHandleIntent((Intent)msg.obj);
// 关闭自己,说明IntentService执行完任务后会关闭自己。
stopSelf(msg.arg1);
}
}
// 构造函数,name表示的是worker线程的名字
public IntentService(String name) {
super();
mName = name;
}
// 设置Intent是否会重新分发,
// ① 如果为true,则onStartCommand返回START_REDELIVER_INTENT,
// 说明如果系统在 onStartCommand() 返回后终止服务,则会重建服务,并通过传递给服务的最后一个 Intent
// 调用 onStartCommand()。任何挂起 Intent 均依次传递。这适用于主动执行应该立即恢复的作业(例如下载文件)的服务。
// ② 如果为false(默认),则onStartCommand会返回START_NOT_STICKY,说明如果系统在 onStartCommand() 返回后终
// 止服务,则除非有挂起 Intent 要传递,否则系统不会重建服务。这是最安全的选项,可以避免在不必要时以及应用能够轻松重启所有未完成的作业时运行服务。
public void setIntentRedelivery(boolean enabled) {
mRedelivery = enabled;
}
@Override
public void onCreate() {
super.onCreate();
// 创建HandlerThread,HandlerThread是一种带有消息循环的线程。
HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
thread.start();
// 获取HandlerThread里的Looper
mServiceLooper = thread.getLooper();
// 构建该Looper的Handler
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);
}
@Override
public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
onStart(intent, startId);
// 根据mRedelivery判定是否重发INTENT(即重建服务)
return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
}
@Override
public void onDestroy() {
mServiceLooper.quit();
}
@Override
@Nullable
public IBinder onBind(Intent intent) {
// 通过startService()启动,不需要提供绑定的Binder代理对象。
return null;
}
// 覆写次方法在Worker线程处理任务
@WorkerThread
protected abstract void onHandleIntent(@Nullable Intent intent);
}
IntentService整体的实现还是比较简单的。
Copyright© 2013-2020
All Rights Reserved 京ICP备2023019179号-8