首页 > 解决方案 > Android - WorkManager - Result.success() 调用次数过多

问题描述

我的应用程序出现问题。我正在尝试将一些照片上传到云中,然后,我将向用户显示要复制的 url 列表,以便第二次使用它们。

我正在为此使用 RxWorker。

当我将工人列表排入队列时,一切似乎都很好(每个图像一个 OneTimeWorkRequestBuilder),但是当我检索结果时,似乎我的观察被触发了太多次。

为了使此步骤可暂停,我有两个列表(图像和工作人员)。当一项工作完成时(无论是失败还是成功),我都会从他们自己的列表中取消图像和工作人员。

这是我的代码。谢谢大家!

我的片段:


class ImagesUploadFragment : Fragment() {

    companion object {
        private const val TAG = "ImagesUploadFragment"
        private const val PHOTOS_LIST = "PHOTOS_LIST"
        private const val WORK_NAME = "PHOTO_UPLOAD"

        fun newInstance(optionalData: String?): ImagesUploadFragment {
            val imagesUploadFragment = ImagesUploadFragment()
            val bundle = Bundle()
            bundle.putString(PHOTOS_LIST, optionalData)
            imagesUploadFragment.arguments = bundle
            return imagesUploadFragment
        }
    }

    private lateinit var imagesUploadBinding: FragmentImagesUploadBinding

    private var stepListener: StepListener? = null

    private val mDownloadUrlsList = mutableListOf<String>()

    private val mWorkManger: WorkManager by lazy { WorkManager.getInstance(requireContext()) }

    private val mWorkRequestList = mutableListOf<OneTimeWorkRequest>()

    private var isUploadPaused = false

    private val mImagesList = mutableListOf<String>()

    private val gson: Gson by inject()

    override fun onAttach(context: Context) {
        super.onAttach(context)
        stepListener = context as? StepListener
    }

    private val imagesUploadViewModel: ImagesUploadViewModel by viewModel()

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
        imagesUploadBinding = FragmentImagesUploadBinding.inflate(inflater, container, false)
        return imagesUploadBinding.root
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        val imagesList = arguments?.getString(PHOTOS_LIST)

        imagesList?.let {
            startImagesUpload(it)
        }

        imagesUploadBinding.successfullyUpdated.text = String.format(getString(R.string.succeeded_updates), 0)
        imagesUploadBinding.workActionIv.setImageDrawable(ContextCompat.getDrawable(requireContext(), R.drawable.ic_pause))

        imagesUploadBinding.buttonContinue.setOnClickListener {
            stepListener?.onStepChange(4, gson.toJson(mDownloadUrlsList))
        }

        mWorkManger.cancelAllWorkByTag(TAG)
        mWorkManger.cancelAllWork()
        mWorkManger.pruneWork()

        imagesUploadBinding.workActionIv.setOnClickListener {
            if (!isUploadPaused) {
                isUploadPaused = true
                pauseUpload()
                imagesUploadBinding.workActionIv.setImageDrawable(ContextCompat.getDrawable(requireContext(), R.drawable.ic_play))
            } else {
                isUploadPaused = false
                resumeUpload()
                imagesUploadBinding.workActionIv.setImageDrawable(ContextCompat.getDrawable(requireContext(), R.drawable.ic_pause))
            }
        }

        setupWorkManagerObserve()

        with(imagesUploadViewModel) {
            observe(imagesListResource,
                success = { imagesList ->
                    imagesUploadBinding.uploadProgress.max = imagesList.first.size
                    mImagesList.addAll(imagesList.first.map { it.jsonValue })
                    setupWorkerList()
                },
                failure = {
                    Timber.e(it)
                }
            )
            observe(uploadedFileResource,
                success = {
                    if(it.isNotEmpty()) mDownloadUrlsList.add(it)
                    imagesUploadBinding.buttonContinue.isVisible = mImagesList.size == 0
                    imagesUploadBinding.workActionIv.isVisible = mImagesList.size > 0
                    imagesUploadBinding.horizontalSpace.isVisible = mImagesList.size > 0

                },
                failure = {
                    Timber.e(it)
                }
            )
        }
    }

    private fun pauseUpload() {
        mWorkManger.cancelAllWork()
    }

    private fun resumeUpload() {
        setupWorkerList()
    }

    private fun startImagesUpload(imagesListJson: String) {
        imagesUploadViewModel.getImagesFromUri(imagesListJson)
    }

    private fun setupWorkManagerObserve() {
        mWorkManger
            .getWorkInfosByTagLiveData(TAG)
            .observe(viewLifecycleOwner) {

                var successNumber = 0
                var failureNumber = 0

                it.forEach { work ->
                    when {
                        work.state == WorkInfo.State.SUCCEEDED && work.state.isFinished -> {
                            successNumber++
                            val image = work.outputData.getString(PHOTO)
                            val uploadedFileJson = work.outputData.getString(UPLOAD_RESPONSE)

                            mWorkRequestList.removeAll { it.id == work.id }
                            mImagesList.removeAll { it == image }

                            uploadedFileJson?.let {
                                imagesUploadViewModel.getDownloadUrlFromResponse(it)
                            }
                            imagesUploadBinding.uploadProgress.progress = successNumber + failureNumber
                            imagesUploadBinding.successfullyUpdated.text =
                                String.format(getString(R.string.succeeded_updates), successNumber)
                        }
                        work.state == WorkInfo.State.RUNNING -> {

                        }
                        work.state == WorkInfo.State.FAILED -> {
                            failureNumber++
                            val image = work.outputData.getString(PHOTO)

                            mWorkRequestList.removeAll { it.id == work.id }
                            mImagesList.removeAll { it == image }

                            imagesUploadBinding.uploadProgress.progress = successNumber + failureNumber
                            imagesUploadBinding.failuredUpdated.text =
                                String.format(getString(R.string.failed_updates), failureNumber)
                            imagesUploadViewModel.addFailure()
                        }
                        else -> {
                        }
                    }
                }
            }
    }

    private fun setupWorkerList() {
        for (i in 0 until mImagesList.size) {
            val work = OneTimeWorkRequestBuilder<ImagesUploadWorkManager>()
                .addTag(TAG)
                .setInputData(Data.Builder().putString(PHOTO, mImagesList[i])
                    .build())
                .build()
            mWorkRequestList.add(work)
        }
        mWorkManger.enqueue(mWorkRequestList)
    }
}

我的工人:

class ImagesUploadWorkManager(val context: Context, parameters: WorkerParameters) : RxWorker(context, parameters) {

    companion object {
        const val PHOTO = "PHOTO"
        const val UPLOAD_RESPONSE = "UPLOAD_RESPONSE"
    }

    private val uploadFilesUseCase: UploadFilesUseCase by inject(UploadFilesUseCase::class.java)
    private val gson: Gson by inject(Gson::class.java)

    override fun createWork(): Single<Result> {

        val imageStringUri = inputData.getString(PHOTO)

        val imageUri = Uri.parse(imageStringUri)

        return Single.fromObservable(
            uploadFilesUseCase.buildObservable(UploadFilesUseCase.Params(imageUri))
                .doOnError {
                    Timber.e(it)
                }
                .map {
                    Result.success(workDataOf(UPLOAD_RESPONSE to gson.toJson(it), PHOTO to imageStringUri))
                }
                .onErrorReturn {
                    Timber.e(it)
                    Result.failure(workDataOf(PHOTO to imageStringUri))
                }
        )
        }

    }

我的用例:


class UploadFilesUseCase(
    private val context: Context,
    private val getServerUseCase: GetServerUseCase,
) : UseCase<UploadFilesUseCase.Params, GoFileDataEntity>() {

    private val goFileApi: GoFileApi by inject(GoFileApi::class.java)

    override fun buildObservable(param: Params): Observable<GoFileDataEntity> {
        return Observable.just(param.imageUri)
            .flatMap {
                val file = createTempFile(it) ?: throw Exception()
                Observable.just(file)
            }.flatMap { imageFile ->
                getServerUseCase.buildObservable(GetServerUseCase.Params())
                    .flatMap { server ->
                        goFileApi.uploadFile(
                            String.format(BuildConfig.api_gofile_upload, server.server),
                            MultipartBody.Part.createFormData("file", imageFile.name, createUploadRequestBody(imageFile, "image/jpeg"))
                        ).toObservable()
                    }.map { it.goFileDataDTO.toEntity() }
            }
    }

    private fun createUploadRequestBody(file: File, mimeType: String) =
        file.asRequestBody(mimeType.toMediaType())

    private fun createTempFile(uri: Uri): File? {
        val tempFile = File.createTempFile(System.currentTimeMillis().toString().take(4), ".jpg", context.externalCacheDir)
        val inputStream = context.contentResolver.openInputStream(uri) ?: return null
        FileOutputStream(tempFile, false).use { outputStream ->
            var read: Int
            val bytes = ByteArray(DEFAULT_BUFFER_SIZE)
            while (inputStream.read(bytes).also { read = it } != -1) {
                outputStream.write(bytes, 0, read)
            }
            outputStream.close()
        }
        inputStream.close()
        return tempFile
    }


    data class Params(val imageUri: Uri)

}

标签: androidfile-uploadrx-androidandroid-workmanager

解决方案


我有一个类似的问题,看起来队列已经从以前的 Workmanager 排队中满了。

我在应用程序启动期间做了以下操作,现在它工作正常:

 WorkManager.getInstance(mContext).cancelAllWork();

推荐阅读