mirror of
https://github.com/pppscn/SmsForwarder
synced 2025-08-03 01:17:41 +08:00
新增:自动任务·快捷指令 —— 定时任务(CoroutineWorker方案) #279
This commit is contained in:
parent
51149c95cd
commit
af63302df6
@ -1,7 +1,6 @@
|
|||||||
package com.idormy.sms.forwarder.adapter
|
package com.idormy.sms.forwarder.adapter
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.util.Log
|
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
@ -48,9 +47,9 @@ class TaskPagingAdapter(private val itemClickListener: OnItemClickListener) : Pa
|
|||||||
holder.binding.layoutActionsIcons.removeAllViews()
|
holder.binding.layoutActionsIcons.removeAllViews()
|
||||||
if (item.actions.isNotEmpty()) {
|
if (item.actions.isNotEmpty()) {
|
||||||
val actionList = Gson().fromJson(item.actions, Array<TaskSetting>::class.java).toMutableList()
|
val actionList = Gson().fromJson(item.actions, Array<TaskSetting>::class.java).toMutableList()
|
||||||
Log.d("TaskPagingAdapter", "actionList:$actionList")
|
//Log.d("TaskPagingAdapter", "actionList:$actionList")
|
||||||
for (action in actionList) {
|
for (action in actionList) {
|
||||||
Log.d("TaskPagingAdapter", "action:$action")
|
//Log.d("TaskPagingAdapter", "action:$action")
|
||||||
val layoutActionItem = View.inflate(App.context, R.layout.item_setting, null) as LinearLayout
|
val layoutActionItem = View.inflate(App.context, R.layout.item_setting, null) as LinearLayout
|
||||||
val ivActionIcon = layoutActionItem.findViewById<ImageView>(R.id.iv_setting_icon)
|
val ivActionIcon = layoutActionItem.findViewById<ImageView>(R.id.iv_setting_icon)
|
||||||
ivActionIcon.setImageResource(action.iconId)
|
ivActionIcon.setImageResource(action.iconId)
|
||||||
|
@ -10,6 +10,7 @@ import androidx.room.Update
|
|||||||
import androidx.sqlite.db.SupportSQLiteQuery
|
import androidx.sqlite.db.SupportSQLiteQuery
|
||||||
import com.idormy.sms.forwarder.database.entity.Task
|
import com.idormy.sms.forwarder.database.entity.Task
|
||||||
import io.reactivex.Single
|
import io.reactivex.Single
|
||||||
|
import java.util.Date
|
||||||
|
|
||||||
@Dao
|
@Dao
|
||||||
interface TaskDao {
|
interface TaskDao {
|
||||||
@ -23,6 +24,9 @@ interface TaskDao {
|
|||||||
@Query("SELECT * FROM Task ORDER BY id DESC")
|
@Query("SELECT * FROM Task ORDER BY id DESC")
|
||||||
fun getAll(): List<Task>
|
fun getAll(): List<Task>
|
||||||
|
|
||||||
|
@Query("SELECT * FROM Task where type = 1000 ORDER BY id DESC")
|
||||||
|
fun getAllCron(): List<Task>
|
||||||
|
|
||||||
@Query("SELECT * FROM Task where type < 1000 ORDER BY id DESC")
|
@Query("SELECT * FROM Task where type < 1000 ORDER BY id DESC")
|
||||||
fun pagingSourceFixed(): PagingSource<Int, Task>
|
fun pagingSourceFixed(): PagingSource<Int, Task>
|
||||||
|
|
||||||
@ -34,7 +38,7 @@ interface TaskDao {
|
|||||||
fun getAllRaw(query: SupportSQLiteQuery): List<Task>
|
fun getAllRaw(query: SupportSQLiteQuery): List<Task>
|
||||||
|
|
||||||
@Query("SELECT * FROM Task WHERE type = :taskType")
|
@Query("SELECT * FROM Task WHERE type = :taskType")
|
||||||
fun getByType(taskType: String): List<Task>
|
fun getByType(taskType: Int): List<Task>
|
||||||
|
|
||||||
//TODO:根据条件查询,不推荐使用
|
//TODO:根据条件查询,不推荐使用
|
||||||
@Query("SELECT * FROM Task WHERE type = :taskType AND conditions LIKE '%' || :conditionKey || '%' AND conditions LIKE '%' || :conditionValue || '%'")
|
@Query("SELECT * FROM Task WHERE type = :taskType AND conditions LIKE '%' || :conditionKey || '%' AND conditions LIKE '%' || :conditionValue || '%'")
|
||||||
@ -46,6 +50,9 @@ interface TaskDao {
|
|||||||
@Update
|
@Update
|
||||||
fun update(task: Task)
|
fun update(task: Task)
|
||||||
|
|
||||||
|
@Query("UPDATE Task SET last_exec_time = :lastExecTime, next_exec_time = :nextExecTime, status = :status WHERE id = :taskId")
|
||||||
|
fun updateExecTime(taskId: Long, lastExecTime: Date, nextExecTime: Date, status: Int)
|
||||||
|
|
||||||
@Query("DELETE FROM Task WHERE id = :taskId")
|
@Query("DELETE FROM Task WHERE id = :taskId")
|
||||||
fun delete(taskId: Long)
|
fun delete(taskId: Long)
|
||||||
|
|
||||||
|
@ -26,7 +26,7 @@ import com.idormy.sms.forwarder.databinding.FragmentTasksEditBinding
|
|||||||
import com.idormy.sms.forwarder.entity.task.CronSetting
|
import com.idormy.sms.forwarder.entity.task.CronSetting
|
||||||
import com.idormy.sms.forwarder.entity.task.TaskSetting
|
import com.idormy.sms.forwarder.entity.task.TaskSetting
|
||||||
import com.idormy.sms.forwarder.utils.*
|
import com.idormy.sms.forwarder.utils.*
|
||||||
import com.idormy.sms.forwarder.utils.task.CronUtils
|
import com.idormy.sms.forwarder.utils.task.CronJobScheduler
|
||||||
import com.xuexiang.xaop.annotation.SingleClick
|
import com.xuexiang.xaop.annotation.SingleClick
|
||||||
import com.xuexiang.xpage.annotation.Page
|
import com.xuexiang.xpage.annotation.Page
|
||||||
import com.xuexiang.xpage.base.XPageFragment
|
import com.xuexiang.xpage.base.XPageFragment
|
||||||
@ -338,10 +338,14 @@ class TasksEditFragment : BaseFragment<FragmentTasksEditBinding?>(), View.OnClic
|
|||||||
when (task.type) {
|
when (task.type) {
|
||||||
//定时任务
|
//定时任务
|
||||||
TASK_CONDITION_CRON -> {
|
TASK_CONDITION_CRON -> {
|
||||||
//取消旧任务的定时器
|
//取消旧任务的定时器 & 设置新的定时器
|
||||||
CronUtils.cancelAlarm(task)
|
//CronUtils.cancelAlarm(task)
|
||||||
//设置新的定时器
|
//CronUtils.scheduleAlarm(task)
|
||||||
CronUtils.scheduleAlarm(task)
|
|
||||||
|
//val uuid = App.TaskIdToWorkerIdMap[task.id]
|
||||||
|
//uuid?.let { CronJobScheduler.cancelTask(it) }
|
||||||
|
CronJobScheduler.cancelTask(task.id)
|
||||||
|
CronJobScheduler.scheduleTask(task)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,12 +22,14 @@ import com.idormy.sms.forwarder.activity.MainActivity
|
|||||||
import com.idormy.sms.forwarder.database.AppDatabase
|
import com.idormy.sms.forwarder.database.AppDatabase
|
||||||
import com.idormy.sms.forwarder.entity.LocationInfo
|
import com.idormy.sms.forwarder.entity.LocationInfo
|
||||||
import com.idormy.sms.forwarder.utils.*
|
import com.idormy.sms.forwarder.utils.*
|
||||||
|
import com.idormy.sms.forwarder.utils.task.CronJobScheduler
|
||||||
import com.idormy.sms.forwarder.workers.LoadAppListWorker
|
import com.idormy.sms.forwarder.workers.LoadAppListWorker
|
||||||
import com.jeremyliao.liveeventbus.LiveEventBus
|
import com.jeremyliao.liveeventbus.LiveEventBus
|
||||||
import com.king.location.LocationClient
|
import com.king.location.LocationClient
|
||||||
import com.king.location.LocationErrorCode
|
import com.king.location.LocationErrorCode
|
||||||
import com.king.location.OnExceptionListener
|
import com.king.location.OnExceptionListener
|
||||||
import com.king.location.OnLocationListener
|
import com.king.location.OnLocationListener
|
||||||
|
import com.xuexiang.xaop.util.PermissionUtils
|
||||||
import com.xuexiang.xutil.XUtil
|
import com.xuexiang.xutil.XUtil
|
||||||
import com.xuexiang.xutil.file.FileUtils
|
import com.xuexiang.xutil.file.FileUtils
|
||||||
import frpclib.Frpclib
|
import frpclib.Frpclib
|
||||||
@ -141,6 +143,16 @@ class ForegroundService : Service() {
|
|||||||
CommonUtils.toggleNotificationListenerService(this)
|
CommonUtils.toggleNotificationListenerService(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//启动定时任务
|
||||||
|
GlobalScope.async(Dispatchers.IO) {
|
||||||
|
val taskList = AppDatabase.getInstance(App.context).taskDao().getByType(TASK_CONDITION_CRON)
|
||||||
|
taskList.forEach { task ->
|
||||||
|
Log.d(TAG, "task = $task")
|
||||||
|
CronJobScheduler.cancelTask(task.id)
|
||||||
|
CronJobScheduler.scheduleTask(task)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//异步获取所有已安装 App 信息
|
//异步获取所有已安装 App 信息
|
||||||
if (SettingUtils.enableLoadAppList) {
|
if (SettingUtils.enableLoadAppList) {
|
||||||
val request = OneTimeWorkRequestBuilder<LoadAppListWorker>().build()
|
val request = OneTimeWorkRequestBuilder<LoadAppListWorker>().build()
|
||||||
@ -169,8 +181,10 @@ class ForegroundService : Service() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//远程找手机
|
//远程找手机 TODO: 判断权限 ACCESS_COARSE_LOCATION ACCESS_FINE_LOCATION
|
||||||
if (SettingUtils.enableLocationTag || HttpServerUtils.enableApiLocation) {
|
if ((SettingUtils.enableLocationTag || HttpServerUtils.enableApiLocation)
|
||||||
|
&& PermissionUtils.isGranted(android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION)
|
||||||
|
) {
|
||||||
//可根据具体需求设置定位配置参数(这里只列出一些主要的参数)
|
//可根据具体需求设置定位配置参数(这里只列出一些主要的参数)
|
||||||
val locationOption = locationClient.getLocationOption().setAccuracy(Criteria.ACCURACY_FINE)//设置位置精度:高精度
|
val locationOption = locationClient.getLocationOption().setAccuracy(Criteria.ACCURACY_FINE)//设置位置精度:高精度
|
||||||
.setPowerRequirement(Criteria.POWER_LOW) //设置电量消耗:低电耗
|
.setPowerRequirement(Criteria.POWER_LOW) //设置电量消耗:低电耗
|
||||||
|
@ -0,0 +1,50 @@
|
|||||||
|
package com.idormy.sms.forwarder.utils.task
|
||||||
|
|
||||||
|
import android.util.Log
|
||||||
|
import androidx.work.Data
|
||||||
|
import androidx.work.ExistingWorkPolicy
|
||||||
|
import androidx.work.OneTimeWorkRequestBuilder
|
||||||
|
import androidx.work.WorkManager
|
||||||
|
import com.idormy.sms.forwarder.database.entity.Task
|
||||||
|
import com.idormy.sms.forwarder.workers.TaskWorker
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
|
class CronJobScheduler {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
private const val TAG: String = "CronJobScheduler"
|
||||||
|
|
||||||
|
fun scheduleTask(task: Task) {
|
||||||
|
val currentTimeMillis = System.currentTimeMillis()
|
||||||
|
val delayInMillis = task.nextExecTime.time / 1000 * 1000 - currentTimeMillis
|
||||||
|
val inputData = Data.Builder().putLong("taskId", task.id).build()
|
||||||
|
val taskRequest = if (delayInMillis <= 0L) {
|
||||||
|
Log.d(TAG, "立即执行任务${task.id},delayInMillis = $delayInMillis")
|
||||||
|
OneTimeWorkRequestBuilder<TaskWorker>()
|
||||||
|
.setInputData(inputData)
|
||||||
|
.build()
|
||||||
|
} else {
|
||||||
|
Log.d(TAG, "延迟 $delayInMillis 毫秒执行任务${task.id}")
|
||||||
|
OneTimeWorkRequestBuilder<TaskWorker>()
|
||||||
|
.setInitialDelay(delayInMillis, TimeUnit.MILLISECONDS)
|
||||||
|
.setInputData(inputData)
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 确保同一个任务 ID 的 Worker 在同一时间只会执行一个实例
|
||||||
|
val uniqueTaskName = "$TAG-${task.id}"
|
||||||
|
WorkManager.getInstance().beginUniqueWork(
|
||||||
|
uniqueTaskName, // 给任务设置一个唯一的名称
|
||||||
|
ExistingWorkPolicy.KEEP, // 设置任务存在时的策略
|
||||||
|
taskRequest
|
||||||
|
).enqueue()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun cancelTask(taskId: Long) {
|
||||||
|
val uniqueTaskName = "$TAG-$taskId"
|
||||||
|
WorkManager.getInstance().cancelUniqueWork(uniqueTaskName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,80 @@
|
|||||||
|
package com.idormy.sms.forwarder.workers
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.util.Log
|
||||||
|
import androidx.work.CoroutineWorker
|
||||||
|
import androidx.work.WorkerParameters
|
||||||
|
import com.google.gson.Gson
|
||||||
|
import com.idormy.sms.forwarder.App
|
||||||
|
import com.idormy.sms.forwarder.database.AppDatabase
|
||||||
|
import com.idormy.sms.forwarder.entity.task.CronSetting
|
||||||
|
import com.idormy.sms.forwarder.entity.task.TaskSetting
|
||||||
|
import com.idormy.sms.forwarder.utils.task.CronJobScheduler
|
||||||
|
import gatewayapps.crondroid.CronExpression
|
||||||
|
import kotlinx.coroutines.delay
|
||||||
|
import java.util.Date
|
||||||
|
|
||||||
|
@Suppress("PrivatePropertyName")
|
||||||
|
class TaskWorker(context: Context, params: WorkerParameters) : CoroutineWorker(context, params) {
|
||||||
|
|
||||||
|
private val TAG: String = TaskWorker::class.java.simpleName
|
||||||
|
|
||||||
|
override suspend fun doWork(): Result {
|
||||||
|
val taskId = inputData.getLong("taskId", -1L)
|
||||||
|
if (taskId == -1L) {
|
||||||
|
Log.d(TAG, "taskId is -1L")
|
||||||
|
return Result.failure()
|
||||||
|
}
|
||||||
|
|
||||||
|
val task = AppDatabase.getInstance(App.context).taskDao().getOne(taskId)
|
||||||
|
// 根据任务信息执行相应操作
|
||||||
|
val conditionList = Gson().fromJson(task.conditions, Array<TaskSetting>::class.java).toMutableList()
|
||||||
|
if (conditionList.isEmpty()) {
|
||||||
|
Log.d(TAG, "onReceive conditionList is empty")
|
||||||
|
return Result.failure()
|
||||||
|
}
|
||||||
|
val firstCondition = conditionList.firstOrNull()
|
||||||
|
if (firstCondition == null) {
|
||||||
|
Log.d(TAG, "onReceive firstCondition is null")
|
||||||
|
return Result.failure()
|
||||||
|
}
|
||||||
|
val cronSetting = Gson().fromJson(firstCondition.setting, CronSetting::class.java)
|
||||||
|
if (cronSetting == null) {
|
||||||
|
Log.d(TAG, "onReceive cronSetting is null")
|
||||||
|
return Result.failure()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新任务的上次执行时间和下次执行时间
|
||||||
|
val now = Date()
|
||||||
|
task.lastExecTime = task.nextExecTime
|
||||||
|
val cronExpression = CronExpression(cronSetting.expression)
|
||||||
|
val nextExecTime = cronExpression.getNextValidTimeAfter(now)
|
||||||
|
// 将 nextExecTime 的毫秒部分设置为 0,避免因为毫秒部分不同导致的任务重复执行
|
||||||
|
nextExecTime.time = nextExecTime.time / 1000 * 1000
|
||||||
|
task.nextExecTime = nextExecTime
|
||||||
|
Log.d(TAG, "lastExecTime = ${task.lastExecTime}, nextExecTime = ${task.nextExecTime}")
|
||||||
|
|
||||||
|
// 自动禁用任务
|
||||||
|
if (task.nextExecTime.time / 1000 < now.time / 1000) {
|
||||||
|
task.status = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新任务信息
|
||||||
|
AppDatabase.getInstance(App.context).taskDao().updateExecTime(task.id, task.lastExecTime, task.nextExecTime, task.status)
|
||||||
|
|
||||||
|
if (task.status == 0) {
|
||||||
|
Log.d(TAG, "onReceive task is disabled")
|
||||||
|
return Result.success()
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: 执行具体任务
|
||||||
|
Log.d(TAG, "执行具体任务,耗时 200 毫秒")
|
||||||
|
delay(200L)
|
||||||
|
|
||||||
|
// 为新的 nextExecTime 调度下一次任务执行
|
||||||
|
CronJobScheduler.cancelTask(task.id)
|
||||||
|
CronJobScheduler.scheduleTask(task)
|
||||||
|
return Result.success()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user