我有一组大任务要在后台执行:
Room
因此,我创建了具有相同Worker
的唯一tag
链。
class GtfsStaticManager() {
private val workerManager = WorkManager.getInstance()
override fun load() {
val constraints = Constraints.Builder().setRequiredNetworkType(NetworkType.CONNECTED).build()
val inputData = GtfsStaticLoadDataWorker.inputData(staticUrl, cacheDir)
// 1. Loading data
val downloadWorkRequest = OneTimeWorkRequest.Builder(GtfsStaticLoadDataWorker::class.java)
.addTag("GTFS")
.setConstraints(constraints)
.setInputData(inputData)
.build()
// 2. List of Workers to parse and store data to the Room
val parseWorkers = GtfsFile.values().map {
OneTimeWorkRequest.Builder(GtfsStaticParseFileWorker::class.java)
.setInputData(GtfsStaticParseFileWorker.inputData(it.file, cacheDir + File.separator + "feed"))
.addTag("GTFS")
.build()
}
workerManager
.beginUniqueWork("GTFS", ExistingWorkPolicy.KEEP, downloadWorkRequest)
.then(parseWorkers)
.enqueue()
}
}
一切正常,只有一件事:文件之一具有400万条记录,大约需要10-15分钟才能完成。一段时间后,我注意到它再次被排队,但第一个作业仍在运行,因此结果是我有2个巨大的作业在后台运行,当然我的数据已被复制。
我遵循了codelabs教程,我错过了什么吗?
下面是我的Worker
,具有解析逻辑:
class GtfsStaticParseFileWorker(
context: Context,
workerParameters: WorkerParameters
) : Worker(context, workerParameters) {
private val fileName: String get() = inputData.getString(FILE_NAME) ?: ""
private val cacheDir: String get() = inputData.getString(UNZIP_FOLDER) ?: ""
companion object {
private const val FILE_NAME = "FILE_NAME"
private const val UNZIP_FOLDER = "UNZIP_FOLDER"
fun inputData(fileName: String, cacheDir: String) = Data
.Builder()
.putString(FILE_NAME, fileName)
.putString(UNZIP_FOLDER, cacheDir)
.build()
}
override fun doWork(): Result {
val db = LvivTransportTrackerDataBase.getUpdateInstance(applicationContext)
val agencyRepository = AgencyRepository(db.agencyDao())
val calendarRepository = CalendarRepository(db.calendarDao())
val calendarDateRepository = CalendarDateRepository(db.calendarDateDao())
val routeRepository = RouteRepository(db.routeDao())
val stopTimeRepository = StopTimeRepository(db.stopTimeDao())
val stopRepository = StopRepository(db.stopDao())
val tripRepository = TripRepository(db.tripDao())
val file = File(cacheDir + File.separator + fileName)
val fileType = GtfsFile.from(fileName) ?: return Result.failure()
when (fileType) {
GtfsFile.Agency -> agencyRepository.deleteAll()
GtfsFile.CalendarDates -> calendarDateRepository.deleteAll()
GtfsFile.Calendar -> calendarRepository.deleteAll()
GtfsFile.Routes -> routeRepository.deleteAll()
GtfsFile.StopTimes -> stopTimeRepository.deleteAll()
GtfsFile.Stops -> stopRepository.deleteAll()
GtfsFile.Trips -> tripRepository.deleteAll()
}
FileInputStream(file).use { fileInputStream ->
InputStreamReader(fileInputStream).use inputStreamReader@{ inputStreamReader ->
val bufferedReader = BufferedReader(inputStreamReader)
val headers = bufferedReader.readLine()?.split(',') ?: return@inputStreamReader
var line: String? = bufferedReader.readLine()
while (line != null) {
val mapLine = headers.zip(line.split(',')).toMap()
Log.d("GtfsStaticParse", "$fileType: $line")
when (fileType) {
GtfsFile.Agency -> agencyRepository.create(AgencyEntity(mapLine))
GtfsFile.CalendarDates -> calendarDateRepository.create(CalendarDateEntity(mapLine))
GtfsFile.Calendar -> calendarRepository.create(CalendarEntity(mapLine))
GtfsFile.Routes -> routeRepository.create(RouteEntity(mapLine))
GtfsFile.StopTimes -> stopTimeRepository.create(StopTimeEntity(mapLine))
GtfsFile.Stops -> stopRepository.create(StopEntity(mapLine))
GtfsFile.Trips -> tripRepository.create(TripEntity(mapLine))
}
line = bufferedReader.readLine()
}
}
}
return Result.success()
}
}
P.S。我的依存关系是implementation "android.arch.work:work-runtime:1.0.0"
答案 0 :(得分:0)
WorkManager中的工人类的执行限制为10分钟。
来自WorkManager guide on how to handle cancellation:
系统指示您的应用出于某种原因停止工作。如果您超过10分钟的执行期限,就会发生这种情况。该工作计划在以后重试。
在您的情况下,您没有处理工作的停止,但是WorkManager将忽略任何结果,因为它将作业标记为“已取消”,并且将在可能的情况下再次执行。
这可能会导致您遇到双重执行。
在不了解您要实现的目标的情况下,很难提出一种替代方法,但是,作为一般规则,WorkManager适用于需要保证执行的可延期任务。
WorkManager documentation在1.0版本之后进行了扩展,您可以在此处找到更多信息。
答案 1 :(得分:0)
您需要首先检查下载文件的路径中是否存在下载的文件,然后将字节存储在循环内的SharedPreferences中,这些字节是先前从下载路径中读取的,一旦您的Job重新开始,则首先检查下载的字节然后下一次从该偏移量开始。
示例代码:
RandomAccessFile seeker = new RandomAccessFile(fname, "r");
seeker.seek(readOffset()); // move to the offset
seeker.readLine(); // and read the String
如果需要对Database条目进行相同的检查,则将行的状态标记为“成功”,以完成下一次跳过该行。