- Как исправить исключение «Job was Cancelled»?
- 2 ответа
- Похожие вопросы:
- Cancellation in coroutines
- Cancellation and Exceptions in Coroutines (Part 2)
- Coroutines: First things first
- Cancellation and Exceptions in Coroutines (Part 1)
- Calling cancel
- Why isn’t my coroutine work stopping?
- Making your coroutine work cancellable
- Checking for job’s active state
- Let other work happen using yield()
- Job.join vs Deferred.await cancellation
- Handling cancellation side effects
- Check for !isActive
- Try catch finally
- suspendCancellableCoroutine and invokeOnCancellation
- Coroutines & Patterns for work that shouldn’t be cancelled
- Cancellation and Exceptions in Coroutines (Part 4)
- Coroutines or WorkManager?
- Coroutines best practices
- 1. Inject Dispatchers into classes
- 2. The ViewModel/Presenter layer should create coroutines
- 3. The layers below the ViewModel/Presenter layer should expose suspend functions and Flows
- Operations that shouldn’t be cancelled in Coroutines
- Which coroutine builder to use?
- What about something simpler?
- Testing
Как исправить исключение «Job was Cancelled»?
В моем фрагменте я пытаюсь извлечь данные из базы данных Firebase с помощью сопрограмм, где данные извлекаются правильно. Вот мой код
Отредактированный: Это мой навигационный поток фрагментов
OnBoarding-Authentication-MainFragment-CheckItemListFragment
Отредактировано : мой Logcat-это :-
Вот как я использовал свой блок Try Catch в каждом фрагменте, чтобы справиться с этой проблемой, но он также не работает вообще. Я также использую метод isActive, чтобы проверить, является ли задание все еще активным или нет, прежде чем извлекать какие-либо данные. Я получаю » фатальное исключение: Main, задание было отменено «, если я нажал кнопку назад до того, как recyclerview покажет данные. Это исключение возникает только в том случае, если я использую поток обратного вызова. Есть ли какой-нибудь способ справиться с этой проблемой или это ошибка в потоке обратного вызова?. До сих пор я не мог найти ни одного возможного ответа, который решит мою проблему. Пожалуйста, скажите мне, как это исправить?
2 ответа
Я пытаюсь установить альтернативный значок приложения с именем MyIcon в моем приложении iOS. У меня есть изображение MyIcon.png в моем проекте bundle (не в моей папке активов), и оно объявлено в моем Info.plist примерно так: CFBundleIcons .
Я использую Google App Engine V. 1.9.48. Во время некоторых моих запросов к хранилищу данных я случайно получаю ошибку CancellationException: Task was cancelled. И я не совсем уверен, что именно вызывает эту ошибку. Из других сообщений Stackoverflow я смутно понимаю, что это связано с тайм-аутами.
Я сталкивался с той же проблемой раньше, и вот мое решение с расширением SendChannel
а когда испускают событие просто звоните offerCatching
Почему вы запускаете сопрограмму во фрагменте onStart с viewModelScope ? фрагмент/деятельности следует использовать lifecycleScope . смотрите здесь для получения более подробной информации.
Похожие вопросы:
Я использую android. недавно я кодировал повторную отправку данных. но возникший таймер illegalstateexception был отменен. Это мое чтение logcat: java.lang.RuntimeException: Unable to start receiver.
Я использую Google AppEngine и отложенную библиотеку с классом Mapper , как описано здесь (с некоторыми улучшениями, как здесь ). в некоторых итерациях mapper я получаю следующую ошибку.
Я пишу некоторые powershell, чтобы поговорить с AWS API, в одном модуле. Я написал одну функцию, Get-CloudFormation , которая возвращает статус CloudFormation. Я написал еще одну функцию.
Я пытаюсь установить альтернативный значок приложения с именем MyIcon в моем приложении iOS. У меня есть изображение MyIcon.png в моем проекте bundle (не в моей папке активов), и оно объявлено в.
Я использую Google App Engine V. 1.9.48. Во время некоторых моих запросов к хранилищу данных я случайно получаю ошибку CancellationException: Task was cancelled. И я не совсем уверен, что именно.
Я прочитал все документы kotlinx UI и реализовал ScopedActivity, как описано там (см. Код ниже). В моей реализации ScopedActivity я также добавляю CouroutineExceptionHandler, и, несмотря на то, что.
Я иногда получаю ошибки отмены при вызове методов gRPC. Вот мой клиентский код (с использованием библиотеки grpc-java 1.22.0): public class MyClient < private static final Logger logger =.
Я получаю довольно много сообщений о смертельных авариях в Crashlytics, которые, похоже, вызваны отменой сопрограммы. И я понятия не имею, как это исправить, и не могу воспроизвести это. Ошибка в.
Источник
Cancellation in coroutines
Cancellation and Exceptions in Coroutines (Part 2)
In development, as in life, we know it’s important to avoid doing more work than needed as it can waste memory and energy. This principle applies to coroutines as well. You need to make sure that you control the life of the coroutine and cancel it when it’s no longer needed — this is what structured concurrency represents. Read on to find out the ins and outs of coroutine cancellation.
If you prefer to see a video on this check out the talk Manuel Vivo and I gave at KotlinConf’19 on coroutines cancellation and exceptions:
⚠️ In order to follow the rest of the article without any problems, reading and understanding Part I of the series is required.
Coroutines: First things first
Cancellation and Exceptions in Coroutines (Part 1)
Calling cancel
When launching multiple coroutines, it can be a pain to keep track of them or cancel each individually. Rather, we can rely on cancelling the entire scope coroutines are launched into as this will cancel all of the child coroutines created:
Cancelling the scope cancels its children
Sometimes you might need to cancel only one coroutine, maybe as a reaction to a user input. Calling job1.cancel ensures that only that specific coroutine gets cancelled and all the other siblings are not affected:
A cancelled child doesn’t affect other siblings
Coroutines handle cancellation by throwing a special exception: CancellationException . If you want to provide more details on the cancellation reason you can provide an instance of CancellationException when calling .cancel as this is the full method signature:
If you don’t provide your own CancellationException instance, a default CancellationException will be created (full code here):
Because CancellationException is thrown, then you will be able to use this mechanism to handle the coroutine cancellation. More about how to do this in the Handling cancellation side effects section below.
Under the hood, the child job notifies its parent about the cancellation via the exception. The parent uses the cause of the cancellation to determine whether it needs to handle the exception. If the child was cancelled due to CancellationException , then no other action is required for the parent.
⚠️Once you cancel a scope, you won’t be able to launch new coroutines in the cancelled scope.
If you’re using the androidx KTX libraries in most cases you don’t create your own scopes and therefore you’re not responsible for cancelling them. If you’re working in the scope of a ViewModel , using viewModelScope or, if you want to launch coroutines tied to a lifecycle scope, you would use the lifecycleScope . Both viewModelScope and lifecycleScope are CoroutineScope objects that get cancelled at the right time. For example, when the ViewModel is cleared, it cancels the coroutines launched in its scope.
Why isn’t my coroutine work stopping?
If we just call cancel , it doesn’t mean that the coroutine work will just stop. If you’re performing some relatively heavy computation, like reading from multiple files, there’s nothing that automatically stops your code from running.
Let’s take a more simple example and see what happens. Let’s say that we need to print “Hello” twice a second using coroutines. We’re going to let the coroutine run for a second and then cancel it. One version of the implementation can look like this:
Let’s see what happens step by step. When calling launch , we’re creating a new coroutine in the active state. We’re letting the coroutine run for 1000ms. So now we see printed:
Once job.cancel is called, our coroutine moves to Cancelling state. But then, we see that Hello 3 and Hello 4 are printed to the terminal. Only after the work is done, the coroutine moves to Cancelled state.
The coroutine work doesn’t just stop when cancel is called. Rather, we need to modify our code and check if the coroutine is active periodically.
Cancellation of coroutine code needs to be cooperative!
Making your coroutine work cancellable
You need to make sure that all the coroutine work you’re implementing is cooperative with cancellation, therefore you need to check for cancellation periodically or before beginning any long running work. For example, if you’re reading multiple files from disk, before you start reading each file, check whether the coroutine was cancelled or not. Like this you avoid doing CPU intensive work when it’s not needed anymore.
All suspend functions from kotlinx.coroutines are cancellable: withContext , delay etc. So if you’re using any of them you don’t need to check for cancellation and stop execution or throw a CancellationException . But, if you’re not using them, to make your coroutine code cooperative we have two options:
- Checking job.isActive or ensureActive()
- Let other work happen using yield()
Checking for job’s active state
One option is in our while(i to add another check for the coroutine state:
This means that our work should only be executed while the coroutine is active. This also means that once we’re out of the while, if we want to do some other action, like logging if the job was cancelled, we can add a check for !isActive and do our action there.
The Coroutines library provides another helpful method — ensureActive() . Its implementation is:
Because this method instantaneously throws if the job is not active, we can make this the first thing we do in our while loop:
By using ensureActive , you avoid implementing the if statement required by isActive yourself, decreasing the amount of boilerplate code you need to write, but lose the flexibility to perform any other action like logging.
Let other work happen using yield()
If the work you’re doing is 1) CPU heavy, 2) may exhaust the thread pool and 3) you want to allow the thread to do other work without having to add more threads to the pool, then use yield() . The first operation done by yield will be checking for completion and exit the coroutine by throwing CancellationException if the job is already completed. yield can be the first function called in the periodic check, like ensureActive() mentioned above.
Job.join vs Deferred.await cancellation
There are two ways to wait for a result from a coroutine: jobs returned from launch can call join and Deferred (a type of Job ) returned from async can be await ’d.
Job.join suspends a coroutine until the work is completed. Together with job.cancel it behaves as you’d expect:
- If you’re calling job.cancel then job.join , the coroutine will suspend until the job is completed.
- Calling job.cancel after job.join has no effect, as the job is already completed.
You use a Deferred when you are interested in the result of the coroutine. This result is returned by Deferred.await when the coroutine is completed. Deferred is a type of Job , and it can also be cancelled.
Calling await on a deferred that was cancel led throws a JobCancellationException .
Here’s why we get the exception: the role of await is to suspend the coroutine until the result is computed; since the coroutine is cancelled, the result cannot be computed. Therefore, calling await after cancel leads to JobCancellationException: Job was cancelled .
On the other hand, if you’re calling deferred.cancel after deferred.await nothing happens, as the coroutine is already completed.
Handling cancellation side effects
Let’s say that you want to execute a specific action when a coroutine is cancelled: closing any resources you might be using, logging the cancellation or some other cleanup code you want to execute. There are several ways we can do this:
Check for !isActive
If you’re periodically checking for isActive , then once you’re out of the while loop, you can clean up the resources. Our code above could be updated to:
See it in action here.
So now, when the coroutine is no longer active, the while will break and we can do our cleanup.
Try catch finally
Since a CancellationException is thrown when a coroutine is cancelled, then we can wrap our suspending work in try/catch and in the finally block, we can implement our clean up work.
But, if the cleanup work we need to execute is suspending, the code above won’t work anymore, as once the coroutine is in Cancelling state, it can’t suspend anymore. See the full code here.
A coroutine in the cancelling state is not able to suspend!
To be able to call suspend functions when a coroutine is cancelled, we will need to switch the cleanup work we need to do in a NonCancellable CoroutineContext . This will allow the code to suspend and will keep the coroutine in the Cancelling state until the work is done.
Check out how this works in practice here.
suspendCancellableCoroutine and invokeOnCancellation
If you converted callbacks to coroutines by using the suspendCoroutine method, then prefer using suspendCancellableCoroutine instead. The work to be done on cancellation can be implemented using continuation.invokeOnCancellation :
To realise the benefits of structured concurrency and ensure that we’re not doing unnecessary work you need to make sure that you’re also making your code cancellable.
Use the CoroutineScopes defined in Jetpack: viewModelScope or lifecycleScope that cancels their work when their scope completes. If you’re creating your own CoroutineScope , make sure you’re tying it to a job and calling cancel when needed.
The cancellation of coroutine code needs to be cooperative so make sure you update your code to check for cancellation to be lazy and avoid doing more work than necessary.
Find out more about patterns for work that shouldn’t be cancelled from this post:
Источник
Coroutines & Patterns for work that shouldn’t be cancelled
Cancellation and Exceptions in Coroutines (Part 4)
In part 2 of the Cancellation and Exceptions in Coroutines series, we learnt the importance of cancelling work when it’s no longer needed. On Android, you can use the CoroutineScope s provided by Jetpack: viewModelScope or lifecycleScope that cancel any running work when their scope completes — that is when the Activity/Fragment/Lifecycle completes. If you’re creating your own CoroutineScope , make sure you tie it to a Job and call cancel when needed.
However, there are cases when you want an operation to complete even if the user navigated away from a screen. As such, you don’t want the work to be cancelled (e.g. writing to a database or making a certain network request to your server).
Keep reading for a pattern to achieve this!
Coroutines or WorkManager?
Coroutines will run as long as your application process is alive. If you need to run operations that should outlive the process (e.g. sending logs to your remote server), use WorkManager instead on Android. WorkManager is the library to use for critical operations that are expected to execute at some point in the future.
Use coroutines for operations that are valid in the current process and can be cancelled if the user kills the app (e.g. making a network request you want to cache). What’s the pattern to trigger these operations?
Coroutines best practices
Since this pattern builds upon other coroutine best practices; let’s recap them:
1. Inject Dispatchers into classes
Don’t hardcode them when creating new coroutines or calling withContext .
✅ Benefits: ease of testing as you can easily replace them for both unit and instrumentation tests.
2. The ViewModel/Presenter layer should create coroutines
If it’s a UI-only operation, then the UI layer can do it. If you think this is not possible in your project, it’s likely you’re not following best practice #1 (i.e. it’s more difficult to test VMs that don’t inject Dispatchers ; in that case exposing suspend functions makes it doable).
✅ Benefits: The UI layer should be dumb and not directly trigger any business logic. Instead, defer that responsibility to the ViewModel/Presenter layer. Testing the UI layer requires instrumentation tests in Android which need an emulator to run.
3. The layers below the ViewModel/Presenter layer should expose suspend functions and Flows
If you need to create coroutines, use coroutineScope or supervisorScope . If you need them to follow a different scope, this is what this article is about! Keep reading!
✅ Benefits: The caller (generally the ViewModel layer) can control the execution and lifecycle of the work happening in those layers, being able to cancel when needed.
Operations that shouldn’t be cancelled in Coroutines
Imagine we have a ViewModel and a Repository in our app with the following logic:
We don’t want veryImportantOperation() to be controlled by viewModelScope as it could be cancelled at any point. We want that operation to outlive viewModelScope . How can we achieve that?
To do this, create your own scope in the Application class and call those operations in coroutines started by it. That scope should be injected in the classes that need it.
The benefits of creating your own CoroutineScope vs other solutions we’ll see later (like GlobalScope ) is that you can configure it as you wish. Do you need a CoroutineExceptionHandler ? Do you have your own thread pool you use as a Dispatcher ? Place all that common configuration there in its CoroutineContext !
You can call it applicationScope and it must contain a SupervisorJob() so that failures in coroutines don’t propagate in the hierarchy (as seen in part 3 of the series):
We don’t need to cancel this scope since we want it to remain active as long as the application process is alive, so we don’t hold a reference to the SupervisorJob . We can use this scope to run coroutines that need a longer lifetime than the calling scope might offer in our app.
For operations that shouldn’t be cancelled, call them from a coroutine created by an application CoroutineScope
Whenever you create a new Repository instance, pass in the applicationScope we created above. For tests, check out the Testing section below.
Which coroutine builder to use?
Depending on veryImportantOperation ’s behavior, you’d need to start a new coroutine using either launch or async:
- If it needs to return a result, use async and call await to wait for it to finish.
- If not, use launch and wait for it to finish with join . Note that as explained in part 3 of the series, you have to handle exceptions manually inside the launch block.
This is how you’d trigger the coroutine using launch :
In any case, the ViewModel code doesn’t change and with the above, even if the viewModelScope gets destroyed, the work using externalScope will keep running. Furthermore, doWork() won’t return until veryImportantOperation() completes as with any other suspend call.
What about something simpler?
Another pattern that could serve some use cases (and it’s probably the first solution anyone would come up with) is wrapping veryImportantOperation in the externalScope ’s context using withContext as follows:
However, this approach has some caveats that you should be aware of:
- If the coroutine that calls doWork is cancelled while veryImportantOperation is getting executed, it will keep executing until the next cancellation point, not after veryImportantOperation finishes executing.
- CoroutineExceptionHandler s don’t work as you’d expect when the context is used in withContext since the exception will be re-thrown.
Testing
As we’ll need to inject both Dispatcher s and CoroutineScope s, what should you inject in those cases?
Источник