这是我参与「第四届青训营 」笔记创作活动的的第4天
[第四届青训营笔记创作活动]
Android基础组件(2):Service
一、service是什么
Service是Android中实现程序后台运行的解决方案,他非常适合执行那些不需要和用户交互并且还要长时间运行的任务。Service的运行不依赖于任何用户界面,即使程序被切换到后台,service仍然能保持正常运行。
二、定义一个Service
定义一个Service必须创建一个kotlin或java类继承Service()类,并且实现其中的唯一的抽象方法onBind()方法。
class GetMovieService : Service() {
override fun onBind(intent: Intent?): IBinder? {
return null
}
override fun onCreate() {
super.onCreate()
.....
.....
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
......
......
return super.onStartCommand(intent, flags, startId)
}
}
onCreate()方法在service被创建时被调用,可以在里面做一些关于service的初始化操作。onStartCommand()方法中定义了用户希望service一启动便运行的操作,onDestroy()方法定义了一些service销毁时需要进行的资源回收操作。
定义好Service类之后还需要在AndroidMainfest.xml中注册我们创建好的service
<application
android:name=".TikTokApplication"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme"
tools:targetApi="31">
<service
android:name=".logic.network.movie.GetMovieService"
android:enabled="true"
android:exported="true" />
</application>
三、service的启动和停止
service的启动和停止主要是借助intent来实现的。
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
startForegroundService(Intent(this, GetMovieService::class.java))
} else {
startService(Intent(this, GetMovieService::class.java))
}
在上面的代码中我们做了兼容性设置,如果android8.0之后,我们使用startForegroundService来启动service服务,如果是android8.0之前我们使用startService来启动service服务,但是使用了startForegroundService来启动的service服务必须是前台服务,我们需要为其配置一个通知。
stopService(Intent(this, GetMovieService::class.java))
当我们想关闭某服务时,我们需要使用stopService来手动关闭某服务
四、Service + AlarmManager实现定时任务
AlarmManager是android系统提供的一个闹钟管理服务,我们使用AlarmManager来实现我们的定时任务。
val manager = getSystemService(ALARM_SERVICE) as AlarmManager
val i = Intent(TikTokApplication.context, AlarmReceiver::class.java)
val pi = PendingIntent.getBroadcast(TikTokApplication.context, 0, i, 0)
manager[AlarmManager.RTC_WAKEUP, triggerAtTime] = pi
在上面代码中AlarmManager.RTC_WAKEUP代表使用RTC绝对时间和唤醒睡眠的手机来进行定时任务,triggerAtTime表示下一次定时任务进行的绝对时间(相对于1970年到现在一共的时间)。设置好这两个之后,到了定时时会根据 PendingIntent.getBroadcast还是别的方法来决定是采用广播通知、Activity还是其他方法来进行定时任务。
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
//这里开辟一条线程,用来执行具体的逻辑操作:
GlobalScope.launch(Dispatchers.IO) {
......
......
if (result.isFailure) {
//操作失败清空一切缓存
//Repository.clearAllMovies()
//操作失败,15分钟后重试
triggerAtTime = Date().time + 15 * 60 * 1000
logUtils.d("后台", "下次榜单的刷新时间:${sdf.format(triggerAtTime)}")
} else {
//操作成功清空一切缓存
//Repository.clearAllMovies()
//操作完成,明天中午刷新
logUtils.d("后台", "下次榜单的刷新时间:${sdf.format(tomorrowHour)}")
triggerAtTime = tomorrowHour.time
}
......
......
//开启下一个定时任务
val manager = getSystemService(ALARM_SERVICE) as AlarmManager
val i = Intent(TikTokApplication.context, AlarmReceiver::class.java)
val pi = PendingIntent.getBroadcast(TikTokApplication.context, 0, i, 0)
manager[AlarmManager.RTC_WAKEUP, triggerAtTime] = pi
} catch (e: Exception) {
triggerAtTime = Date().time + 15 * 60 * 1000
logUtils.d("后台", "下次榜单的刷新时间:${sdf.format(triggerAtTime)}")
val manager = getSystemService(ALARM_SERVICE) as AlarmManager
val i = Intent(TikTokApplication.context, AlarmReceiver::class.java)
val pi = PendingIntent.getBroadcast(TikTokApplication.context, 0, i, 0)
manager[AlarmManager.RTC_WAKEUP, triggerAtTime] = pi
e.printStackTrace()
}
}
return super.onStartCommand(intent, flags, startId)
}
class AlarmReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent?) {
val i = Intent(context, GetMovieService::class.java)
context.startService(i)
"开启定时任务".sendToast()
}
}
在上面代码中我们通过service + AlarmManager + BroadcastReceiver实现了一个每天12点更新,更新成功定时明天12点进行更新,并且如果失败之后15分钟会自动重试的定时任务。