Android面试之Service
1.Service的基本认识
1.1 Service是什么?
Service(服务)是一个一种可以在后台执行长时间运行操作而没有用户界面的组件。它运行于UI线程,因此不能进行耗时的操作。
1.2 Service和Thread的区别
Service的运行是在UI线程当中的,是绝对绝对不能进行 耗时操作 的,而Thread开启的子线程则可以进行耗时操作;
但是Thread开启的子线程是不能直接 对UI进行操作 的,否则极有可能发生直接让程序崩掉,这就是它们的区别。
1.3 能否在Service开启耗时操作 ? 怎么做 ?
service 默认并不会运行在子线程中,也不运行在一个独立的进程中,它同样 执行在主线程中(UI线程)。
换句话说,不要在 Service 里执行耗时操作,除非手动打开一个子线程,否则有可能出现主线程被阻塞(ANR)的情况;
1.4 常见服务
2.启动Service的2种方式
2.1 startService()方法开启Service
步骤:
a.定义一个类继承Service。
b.在AndroidManifest.xml文件中配置该Service。
c.使用Context的startService(Intent)方法启动该Service。
d.不再使用该Service时,调用Context的stopService(Intent)方法停止该Service
2.2 bindService方法开启Service(Activity与Service绑定)
步骤:
a.创建BinderService服务端,继承自Service并在类中创建一个实现IBinder接口的实现实例对象并提供公共方法给客户端调用。
b.从onBind()回调方法返回此Binder实例。
c.在客户端中,从onServiceConnected回调方法接收Binder,并使用提供的方法调用绑定服务。
2.3 两种启动方式的区别
-
startService():通过这种方式调用 startService,onCreate() 只会被调用一次,多次调用 startSercie 会多次执行 onStartCommand() 和 onStart() 方法。如果外部没有调用 stopService() 或 stopSelf() 方法, service 会一直运行。
-
bindService():如果该服务之前还没创建,系统回调顺序为 onCreate()→onBind()。如果调用 bindService() 方法前服务已经被绑定,多次调用 bindService() 方法不会多次创建服务及绑定。如果调用者希望与正在绑定的服务解除绑定,可以调用 unbindService() 方法,回调顺序为 onUnbind()→onDestroy();
3.Service的生命周期
Service的生命周期涉及到六大方法
onCreate():如果 service 没被创建过,调用 startService() 后会执行 onCreate() 回调;如果 service 已处于运行中,调用 startService() 不会执行 onCreate() 方法。
onCreate()只会在第一次创建service时候调用,多次执行startService()不会重复调用onCreate(),此方法适合完成一些初始化工作;
onStartComand():服务启动时调用,此方法适合完成一些数据加载工作,比如会在此处创建一个线程用于下载数据或播放音乐;
onBind():服务被绑定时调用;
onUnBind():服务被解绑时调用;
onDestroy():服务停止时调用;
服务的生命周期有两种,因为服务可以跟Activity绑定起来,也可以不绑定,Activity和服务进行通信的话,是需要把服务和Activity进行绑定的。因此服务的生命周期分为未绑定Activity的和绑定Activity的。
3.1 没有绑定Activity的服务生命周期图
-
通过 Intent 和 startService() 方法启动了一个服务,接下来执行
onCreate()
方法,首次创建服务时,系统将调用此方法来执行一次性设置程序(在调用 onStartCommand() 或 onBind() 之前)。如果服务已在运行,则不会调用此方法。 -
当另一个组件(如 Activity)通过调用 startService() 请求启动服务时,系统将调用此方法。一旦执行此方法
onStartConmmand()
,服务即会启动并可在后台无限期运行。 如果实现了此方法,则在服务工作完成后,需要通过调用 stopSelf() 或 stopService() 来停止服务。(如果只想提供绑定,则无需实现此方法。) -
服务开始处于运行状态。
-
某个操作导致服务停止,比如执行了方法
stopService()
,那么服务接下来会执行onDestory()
销毁。服务应该实现此方法来清理所有资源,如线程、注册的侦听器、接收器等。 这是服务接收的最后一个调用。 -
服务被完全销毁,下一步就是等待被垃圾回收器回收了。
3.2 绑定Activity的服务生命周期图
- 通过 Intent 和 bindService() 方法启动了一个服务,接下来会执行 onCreate() 方法,首次创建服务时,系统将调用此方法来执行一次性设置程序(在调用 onStartCommand() 或 onBind() 之前)。如果服务已在运行,则不会调用此方法。
- 当另一个组件想通过调用 bindService() 与服务绑定(例如执行 RPC)时,系统将调用此方法。在此方法的实现中,您必须通过返回 IBinder 提供一个接口,供客户端用来与服务进行通信。请务必实现此方法,但如果您并不希望允许绑定,则应返回 null。
- 服务开始处于运行状态。成功与Activity绑定。
- 某个操作导致服务解除绑定,比如执行了方法unbindService(),那么服务接下来会解除与当前Activity的绑定。接下来服务将面临销毁。
- 服务执行onDestory()方法被销毁。服务应该实现此方法来清理所有资源,如线程、注册的侦听器、接收器等。 这是服务接收的最后一个调用。
- 服务被完全销毁,下一步就是等待被垃圾回收器回收了。
4. 关于服务,总结一下:
- 被启动的服务的生命周期:如果一个Service被某个Activity 调用 Context.startService 方法启动,那么不管是否有Activity使用bindService绑定或unbindService解除绑定到该Service,该Service都在后台运行。如果一个Service被startService 方法多次启动,那么onCreate方法只会调用一次,onStart将会被调用多次(对应调用startService的次数),并且系统只会创建Service的一个实例(因此你应该知道只需要一次stopService调用)。该Service将会一直在后台运行,而不管对应程序的Activity是否在运行,直到被调用stopService,或自身的stopSelf方法。当然如果系统资源不足,android系统也可能结束服务。
- 被绑定的服务的生命周期:如果一个Service被某个Activity 调用 Context.bindService 方法绑定启动,不管调用 bindService 调用几次,onCreate方法都只会调用一次,同时onStart方法始终不会被调用。当连接建立之后,Service将会一直运行,除非调用Context.unbindService 断开连接或者之前调用bindService 的 Context 不存在了(如Activity被finish的时候),系统将会自动停止Service,对应onDestroy将被调用。
- 被启动又被绑定的服务的生命周期:如果一个Service又被启动又被绑定,则该Service将会一直在后台运行。并且不管如何调用,onCreate始终只会调用一次,对应startService调用多少次,Service的onStart便会调用多少次。调用unbindService将不会停止Service,而必须调用 stopService 或 Service的 stopSelf 来停止服务。
- 当服务被停止时清除服务:当一个Service被终止(1、调用stopService;2、调用stopSelf;3、不再有绑定的连接(没有被启动))时,onDestroy方法将会被调用,在这里你应当做一些清除工作,如停止在Service中创建并运行的线程。
特别注意:
5. 在调用 bindService 绑定到Service的时候,就应当保证在某处调用 unbindService 解除绑定(尽管 Activity 被 finish 的时候绑定会自动解除,并且Service会自动停止);
6. 使用 startService 启动服务之后,一定要使用 stopService 停止服务,不管你是否使用bindService;
7. 同时使用 startService 与 bindService 要注意到,Service 的终止,需要 unbindService 与 stopService 同时调用,才能终止 Service,不管 startService 与 bindService 的调用顺序,如果先调用 unbindService 此时服务不会自动终止,再调用 stopService 之后服务才会停止,如果先调用 stopService 此时服务也不会终止,而再调用 unbindService 或者 之前调用 bindService 的 Context 不存在了(如Activity 被 finish 的时候)之后服务才会自动停止;
8. 当在 旋转手机屏幕的时候,当手机屏幕在“横”“竖”变换时,此时如果你的 Activity 如果会自动旋转的话,旋转其实是 Activity 的重新创建,因此旋转之前的使用 bindService 建立的连接便会断开(Context 不存在了),对应服务的生命周期与上述相同。
9. 在 sdk 2.0 及其以后的版本中,对应的 onStart 已经被否决变为了 onStartCommand,不过之前的 onStart 任然有效。这意味着,如果你开发的应用程序用的 sdk 为 2.0 及其以后的版本,那么你应当使用 onStartCommand 而不是 onStart。
5. 如何保证Service不被杀死 ?
-
onStartCommand 方式中,返回 START_STICKY 或 START_REDELIVER_INTENT
- START_STICKY:如果返回 START_STICKY,表示 Service 运行的进程被 Android 系统强制杀掉之后,Android 系统会将该 Service 依然设置为 started 状态(即运行状态),但是不再保存 onStartCommand 方法传入的 intent 对象
- START_NOT_STICKY:如果返回 START_NOT_STICKY,表示当 Service 运行的进程被 Android 系统强制杀掉之后,不会重新创建该 Service
- START_REDELIVER_INTENT:如果返回START_REDELIVER_INTENT,其返回情况与 START_STICKY 类似,但不同的是 系统会保留最后一次传入 onStartCommand 方法中的 Intent 再次保留下来并再次传入到重新创建后的 Service 的 onStartCommand 方法中;
-
提高 Service 的优先级 在 AndroidManifest.xml 文件中对于 intent-filter 可以通过 android:priority = “1000” 这个属性设置最高优先级,1000 是最高值,如果数字越小则优先级越低,同时适用于广播;
-
在 onDestroy 方法里重启 Service ;当 service 走到 onDestroy() 时,发送一个自定义广播,当收到广播时,重新启动 service;
-
提升 Service 进程的优先级 进程优先级由高到低:
前台进程 一 可视进程 一 服务进程 一 后台进程 一 空进程
可以使用 startForeground 将 service 放到前台状态,这样低内存时,被杀死的概率会低一些; -
系统广播监听 Service 状态
-
将APK安装到 /system/app,变身为系统级应用
注意:以上机制都不能百分百保证Service不被杀死,除非做到系统白名单,与系统同生共死
6. AMS 是什么?发挥什么作用
- ActivityManagerService 是 Android 中最核心的服务,
- 主要负责系统中四大组件的启动、切换、调度及应用进程的管理和调度等工作,其职责与操作系统中的进程管理和调度模块类似;