寫這篇博客的緣由來自一個需求:
每日定時推送通知,提醒用戶完成簽到。如果后臺被清理缀踪,則在打開App后立即推送盈匾。
眾所周知腾务,如今的Android國產(chǎn)ROM想要實現(xiàn)定時操作需要做極強的保活削饵。
然而一旦做了毖沂荩活,那么程序就可能會消耗沒有必要的資源窿撬,變得很流氓启昧,這并不是我們想要看到的。
于是jobSchedule應運而生劈伴,這個庫應該是最完美的解決方案密末,但有api限制,并不能滿足我的需要跛璧。
后來嘗試了AlarmManager严里,這個工具也能較精確地定時工作,但是一旦程序被殺后臺追城,就再也不起作用刹碾。而且由于采用繼承Receiver的方式,在8點打開app無法收到本應是7點的通知座柱,因此也無法滿足需要迷帜。
于是選擇了workManager,這個比較新鮮的官方框架辆布。
各種情況是否定時推送 | 后臺未被清理 | 后臺被清理 |
---|---|---|
原生ROM | 推送 | 推送 |
國產(chǎn)ROM | 推送 | 打開App后繼續(xù)推送 |
在程序沒有被殺的情況下瞬矩,能夠完成定時工作。
在程序被殺后锋玲,若是原生ROM景用,不會有影響;若是國產(chǎn)ROM惭蹂,不會自動推送通知伞插,但會在打開App的第一時間自動調用代碼,完成推送盾碗。
愚以為媚污,微博,知乎App也是用類似這樣的操作實現(xiàn)的推送廷雅,因為你殺掉后臺后并不能接收到推送(一加5t 氫os 8.1)耗美,但是一旦重新打開App京髓,會收到之前的提醒。
當然商架,這樣做的缺點就是無法做到精確定時堰怨,因為workManager的重復工作間隔必須大于15分鐘。因此即使使用最短的時間間隔蛇摸,也最多只能保證精度為15*2=30分鐘备图。
做到定時的原理就是,每15分鐘調用一次赶袄,查看時間是否是在指定的時間段揽涮。
如果在時間段內,且未推送饿肺,那么推送蒋困,并記錄。如果不在時間段內敬辣,則重置記錄為未推送家破。
有點繞,需要理一理=_=购岗。如果說精度大于一小時,就不用判斷了门粪。
至于使用方法就很簡單了喊积,其他博客也有詳細的講解,這里主要示例定時代碼玄妈,定時7點-7點30分推送任務乾吻。
1.添加依賴
implementation "android.arch.work:work-runtime:1.0.0-alpha09"
2.實現(xiàn)Worker的子類
import android.app.PendingIntent
import android.content.Intent
import androidx.work.Worker
import com.mredrock.cyxbs.common.utils.extensions.defaultSharedPreferences
import com.mredrock.cyxbs.common.utils.extensions.editor
import com.mredrock.cyxbs.mine.util.NotificationUtil
import java.util.*
/**
* Created by zia on 2018/10/8.
* 每日簽到提醒的work
*/
class SignWorker : Worker() {
private val FLAG = "SIGNPUSH"
override fun doWork(): Worker.Result {
//讀取是否通知過
val isPush = applicationContext.defaultSharedPreferences.getBoolean(FLAG, false)
if (compareCurrentHour(7)) {
if (!isPush) {
//如果在指定時間段,并且沒有推送過通知
applicationContext.defaultSharedPreferences.editor {
//寫入已通知
putBoolean(FLAG, true)
}
//繼續(xù)后面的推送通知代碼
} else {
//在指定時間段拟蜻,已推送過了绎签,則不再推送
return Result.RETRY
}
} else {
//不在時間段,重置標志位false
applicationContext.defaultSharedPreferences.editor {
putBoolean(FLAG, false)
}
return Result.RETRY
}
val resultIntent = Intent(applicationContext, DailySignActivity::class.java)
val intent = PendingIntent.getActivity(applicationContext, 0, resultIntent, PendingIntent.FLAG_UPDATE_CURRENT)
//推送通知
NotificationUtil.makeNotification(applicationContext, "趕快去簽到領取積分哦~", CHANNEL_ID = "sign", pendingIntent = intent)
return Worker.Result.SUCCESS
}
private fun compareCurrentHour(targetHour: Int): Boolean {
val current = Calendar.getInstance().get(Calendar.HOUR_OF_DAY)
return current == targetHour
}
}
3.在其他地方調用
//獲取一個builder
val request = PeriodicWorkRequest
.Builder(SignWorker::class.java, 15, TimeUnit.MINUTES)
.build()
//插入worker隊列酝锅,并且使用enqueueUniquePeriodicWork方法诡必,防止重復
WorkManager.getInstance().enqueueUniquePeriodicWork(workName,ExistingPeriodicWorkPolicy.KEEP, request)
4.用戶取消推送
WorkManager.getInstance().cancelUniqueWork(workName)