安卓开发之Service进阶学习

与Activity一样,Service也有自己的生命周期
在这里插入图片描述
由上图可以看到service有两种启动方式,分别为startService与bindService两种方式,还有一种就是启动service后绑定service。
首先看一下生命周期的各个方法然后再看下不同的启动方式:

onCreate:与Activity一样,在服务被第一次创建的时候回调,该方法在生命周期中只会执行一次。
onstartCommand:onCreate之后调用,服务被创建之后还可以startService但是不会创建新的service对象,而是复用前面产生的service对象并回调onstartCommand方法。
onOnbind:服务被绑定时调用。该方法是service都必须实现的方法,该方法会返回一个 IBinder对象,app通过该对象与Service组件进行通信
onUnbind:当服务上绑定的所有客户端断开时会调用。

弄懂了生命周期的各个方法,再看下几种启动方式:
1)StartService启动Service
前面已经说过会创建一个Service实例,依次调用onCreate()和onStartCommand()方法,此时Service 进入运行状态,如果再次调用StartService启动Service,将不会再创建新的Service对象, 系统会直接复用前面创建的Service对象,调用它的onStartCommand()方法。直到stopService()或stopSelf()方法被调用,service停止运行并回调onDestroy()。
这样的service生命周期与其调用者无关,就算调用者结束了自己的生命周期,service也会照常运行。这种情况下,想要关闭service只需调用一次stopService()或stopSelf()方法,服务就会停止,而无论启动了多少次startService()方法。

2)bindService启动Service
当调用者首次使用bindService启动Service时,系统会实例化一个Service实例,然后会依次调用onCreate()和onBind()方法,调用者会获得onBind()方法返回的IBinder对象实例,通过这个IBinder对象实例调用者就可以和Service进行交互了。此后如果再使用bindService绑定Service,系统不会创建新的Sevice实例,也不会再调用onBind()方法,而是直接把IBinder对象传递给其他后来增加的客户端。
如果Service只与一个客户端绑定的话,只要调用方和服务之间的连接没有断开,服务就会一直保持运行状态。如果想要解除客户端与Service的绑定只需要调用unbindService(),此时onUnbind和onDestory方法将会被调用,Service被解绑并销毁。
如果是多个客户端绑定一个Service时,只有所有的客户端都解绑,Service才会停止运行。

3)先StartService后bindService
也就是Service已经由某个客户端通过StartService()启动,之后其他客户端再通过bindService绑定这个Service,那么会触发如下生命周期方法:onCreate( )->onStartCommand( )->onBind( ),之后如果解绑再绑定,会调用onUnbind( )->onRebind( )。
这种情况下想要让服务停止运行也就是onDestory需要同时调用 stopService()和 unbindService()方法。

写个小Demo验证下BindService:
在这里插入图片描述
MainActivity中绑定Service并调用Service中的方法:
首先自定义TestService继承Service,重要的是onCreate方法和onBind方法,前面提到调用方通过BindService启动Service最后会获得onBind()方法返回的IBinder对象实例,因此需要自定义一个Binder类:
在这里插入图片描述
在onCreate方法中也就是服务创建时就new一个线程对time做改变,接着在MainActivity中,通过binderService绑定
在这里插入图片描述
在这里插入图片描述
点击绑定:
在这里插入图片描述
点击解绑:
在这里插入图片描述
点击获取time:
在这里插入图片描述
可见活动与服务双方之间的通信是通过Binder对象通信的,Service中是OnBind中的Binder对象,活动中是onServiceConnected获得的binder。

前面看到在Service中new了线程以防止造成ARN,之后如果要结束服务还要调用stopSelf()停止服务,不顾程序猿有的时候会忘记new线程或者stopSelf,Android也提供了一个异步的、会自动停止的服务IntentService,接下来验证一下:
新建一个类继承IntentService,并重写相关方法:
在这里插入图片描述
MainActivity中显示启动:
在这里插入图片描述
那么这个服务会有两个特点,1是会自动停止,2是会在不同线程中运行,log日志如下图所示:
在这里插入图片描述

 看完上述service的一些基本信息后,来实践一个常用的系统服务,这里选用AlarmManager闹钟服务,简单点说就是我们自己定一个时间, 然后当到时间时,AlarmManager会为我们广播一个我们设定好的Intent,比如时间到了,可以指向某个 Activity或者Service。

首先通过getSystemService获取AlarmManager实例,然后通过set方法设置一次性闹钟:set(int type,long startTime,PendingIntent pi)

其中type值的是闹钟类型,常用的有5个可选值:

AlarmManager.ELAPSED_REALTIME: 闹钟在手机睡眠状态下不可用,该状态下闹钟使用相对时间(相对于系统启动开始);
AlarmManager.ELAPSED_REALTIME_WAKEUP 闹钟在睡眠状态下会唤醒系统并执行提示功能,该状态下闹钟使用相对时间;
AlarmManager.RTC 闹钟在睡眠状态下不可用,该状态下闹钟使用绝对时间,即当前系统时间;
AlarmManager.RTC_WAKEUP 表示闹钟在睡眠状态下会唤醒系统并执行提示功能,该状态下闹钟使用绝对时间;
AlarmManager.POWER_OFF_WAKEUP 表示闹钟在手机关机状态下也能正常进行提示功能。

time值指的是闹钟的第一次执行时间,以毫秒为单位,一般使用当前时间,与type参数相对应:

如果第一个参数对应的闹钟使用的是相对时间 (ELAPSED_REALTIME和ELAPSED_REALTIME_WAKEUP),那么本属性就得使用相对时间 (相对于系统启动时间来说),比如当前时间就表示为:SystemClock.elapsedRealtime();那么这里参数就可以写成SystemClock.elapsedRealtime()+time。
如果第一个参数对应的闹钟使用的是绝对时间(RTC、RTC_WAKEUP、POWER_OFF_WAKEUP), 那么本属性就得使用绝对时间,比如当前时间就表示 为:System.currentTimeMillis()。

PendingIntent对象绑定了闹钟的执行动作,比如发送一个广播,这样当定时任务执行的时候广播接收器的onReceive方法就会被执行。

新建TestService3继承Service:在onStartCommand中通过getSystemService来获得AlarmManager实例,之后通过set方法设置闹钟

在这里PendIntent通过getBroadcast()方法来获取一个能够执行广播的PendingIntent。这样当定时任务被触发的时候,广播接收器的onReceive()方法就可以得到执行。
在这里插入图片描述

新建AlarmReceiver继承广播接收器,注意名称要和getBroadcast的第二个参数相同:

在这里插入图片描述

在MainActivity中启动:
在这里插入图片描述
这样点击后,TestService3就会被启动,并且10秒钟后就会通过发送广播实现提示,这里接收到广播后又启动了TestService3,我们知道如果再启动的话会回调onStarCommand方法:
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_42011443/article/details/106730072