Android WorkManager启动Worker两次

Android WorkManager启动Worker两次,android,kotlin,android-room,android-jetpack,android-workmanager,Android,Kotlin,Android Room,Android Jetpack,Android Workmanager,我有一组大任务要在后台执行: 存储数据 解析一堆文件并将它们存储在房间 出于这个原因,我创建了一个独特的工作者链,该链具有相同的标签 class GtfsStaticManager() { private val workerManager = WorkManager.getInstance() override fun load() { val constraints = Constraints.Builder().setRequiredNetworkType

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

  • 存储数据
  • 解析一堆文件并将它们存储在
    房间
  • 出于这个原因,我创建了一个独特的
    工作者链,该链具有相同的
    标签

    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分钟。一段时间后,我注意到它再次排队,但第一个作业仍在运行,因此我有两个巨大的作业在后台运行,当然我的数据被复制了

    我遵循了教程,我错过了什么吗

    下面是我的
    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()
        }
    }
    

    另外,我的依赖项是
    实现“android.arch.work:work runtime:1.0.0”
    WorkManager中的工人类的执行限制为10分钟。
    发件人:

    系统指示应用程序出于某种原因停止工作。如果超过10分钟的执行期限,则可能发生这种情况。计划稍后重试该工作

    在您的情况下,您没有处理工作的停止,但WorkManager将忽略任何结果,因为它将作业标记为“已取消”,并在可能的情况下再次执行

    这可能导致您正在经历的双重执行

    在不了解您想要实现的目标的情况下,很难提出替代方法。但是,一般来说,WorkManager适用于需要保证执行的可延迟任务


    已在1.0版本后扩展,您可以在那里找到更多信息。

    您需要首先检查下载的文件是否存在于下载文件的路径上,然后将字节存储在循环内的SharedReferences中,这些字节以前从下载路径读取,一旦您的作业再次启动,然后首先检查下载的字节,下次从该偏移量开始

    示例代码:

    RandomAccessFile seeker = new RandomAccessFile(fname, "r");
    seeker.seek(readOffset()); // move to the offset
    seeker.readLine(); // and read the String
    

    如果需要检查相同的数据库条目,则将行的状态标记为“成功”,以便下次跳过该行。

    谢谢,此限制对我来说是新的。因此,在我的例子中,要处理正确的
    取消
    状态,我应该将
    while
    循环更改为如下内容:
    while(line!=null | | | isStopped)
    ?我的目标很简单,在我下载了一个
    zip
    文件后,我需要
    解压缩它并解析其中的
    csv
    文件。在我的例子中,一个文件有400万行,并且将它们存储在
    文件室
    需要一段时间。请记住,在这种情况下,WorkManager将忽略工作人员的返回值并重新安排它。这意味着您需要在离开工作人员之前跟踪处理数据的地点,或者在下次执行时,您可能会处理相同的数据两次,并被相同的10分钟限制所捕获。您能否建议我存储我的
    进度
    值的最佳选项(静态变量、共享首选项等)?
    Android
    world对我来说是新事物,所以我不确定如何做到最好。在这种情况下,共享Prefs似乎是一个不错的选择:但我不知道整个应用程序/用例,所以…阅读文档,检查此解决方案是否适用于您的特定问题。