WorkManager两次启动Worker

时间:2019-03-27 17:20:03

标签: android kotlin android-room android-jetpack android-workmanager

我有一组大任务要在后台执行:

  1. 住宿数据
  2. 解析一堆文件并将其存储在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"

2 个答案:

答案 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条目进行相同的检查,则将行的状态标记为“成功”,以完成下一次跳过该行。