mirror of
https://github.com/pppscn/SmsForwarder
synced 2025-08-02 17:07:41 +08:00
新增:自动任务·快捷指令 —— 定时任务(CoroutineWorker方案) #279
This commit is contained in:
parent
51149c95cd
commit
af63302df6
@ -1,7 +1,6 @@
|
||||
package com.idormy.sms.forwarder.adapter
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
@ -48,9 +47,9 @@ class TaskPagingAdapter(private val itemClickListener: OnItemClickListener) : Pa
|
||||
holder.binding.layoutActionsIcons.removeAllViews()
|
||||
if (item.actions.isNotEmpty()) {
|
||||
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) {
|
||||
Log.d("TaskPagingAdapter", "action:$action")
|
||||
//Log.d("TaskPagingAdapter", "action:$action")
|
||||
val layoutActionItem = View.inflate(App.context, R.layout.item_setting, null) as LinearLayout
|
||||
val ivActionIcon = layoutActionItem.findViewById<ImageView>(R.id.iv_setting_icon)
|
||||
ivActionIcon.setImageResource(action.iconId)
|
||||
|
@ -10,6 +10,7 @@ import androidx.room.Update
|
||||
import androidx.sqlite.db.SupportSQLiteQuery
|
||||
import com.idormy.sms.forwarder.database.entity.Task
|
||||
import io.reactivex.Single
|
||||
import java.util.Date
|
||||
|
||||
@Dao
|
||||
interface TaskDao {
|
||||
@ -23,6 +24,9 @@ interface TaskDao {
|
||||
@Query("SELECT * FROM Task ORDER BY id DESC")
|
||||
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")
|
||||
fun pagingSourceFixed(): PagingSource<Int, Task>
|
||||
|
||||
@ -34,7 +38,7 @@ interface TaskDao {
|
||||
fun getAllRaw(query: SupportSQLiteQuery): List<Task>
|
||||
|
||||
@Query("SELECT * FROM Task WHERE type = :taskType")
|
||||
fun getByType(taskType: String): List<Task>
|
||||
fun getByType(taskType: Int): List<Task>
|
||||
|
||||
//TODO:根据条件查询,不推荐使用
|
||||
@Query("SELECT * FROM Task WHERE type = :taskType AND conditions LIKE '%' || :conditionKey || '%' AND conditions LIKE '%' || :conditionValue || '%'")
|
||||
@ -46,6 +50,9 @@ interface TaskDao {
|
||||
@Update
|
||||
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")
|
||||
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.TaskSetting
|
||||
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.xpage.annotation.Page
|
||||
import com.xuexiang.xpage.base.XPageFragment
|
||||
@ -338,10 +338,14 @@ class TasksEditFragment : BaseFragment<FragmentTasksEditBinding?>(), View.OnClic
|
||||
when (task.type) {
|
||||
//定时任务
|
||||
TASK_CONDITION_CRON -> {
|
||||
//取消旧任务的定时器
|
||||
CronUtils.cancelAlarm(task)
|
||||
//设置新的定时器
|
||||
CronUtils.scheduleAlarm(task)
|
||||
//取消旧任务的定时器 & 设置新的定时器
|
||||
//CronUtils.cancelAlarm(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.entity.LocationInfo
|
||||
import com.idormy.sms.forwarder.utils.*
|
||||
import com.idormy.sms.forwarder.utils.task.CronJobScheduler
|
||||
import com.idormy.sms.forwarder.workers.LoadAppListWorker
|
||||
import com.jeremyliao.liveeventbus.LiveEventBus
|
||||
import com.king.location.LocationClient
|
||||
import com.king.location.LocationErrorCode
|
||||
import com.king.location.OnExceptionListener
|
||||
import com.king.location.OnLocationListener
|
||||
import com.xuexiang.xaop.util.PermissionUtils
|
||||
import com.xuexiang.xutil.XUtil
|
||||
import com.xuexiang.xutil.file.FileUtils
|
||||
import frpclib.Frpclib
|
||||
@ -141,6 +143,16 @@ class ForegroundService : Service() {
|
||||
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 信息
|
||||
if (SettingUtils.enableLoadAppList) {
|
||||
val request = OneTimeWorkRequestBuilder<LoadAppListWorker>().build()
|
||||
@ -169,8 +181,10 @@ class ForegroundService : Service() {
|
||||
}
|
||||
}
|
||||
|
||||
//远程找手机
|
||||
if (SettingUtils.enableLocationTag || HttpServerUtils.enableApiLocation) {
|
||||
//远程找手机 TODO: 判断权限 ACCESS_COARSE_LOCATION ACCESS_FINE_LOCATION
|
||||
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)//设置位置精度:高精度
|
||||
.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