Service | 青训营笔记

156 阅读3分钟

image.png

这是我参与「第四届青训营 」笔记创作活动的的第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分钟会自动重试的定时任务。

image.png