This problem is normally a symptom of a deeper problem with the architecture of your app. You tend to see it when there is not proper separation of concerns between the view layer and business logic layer.
I imagine you are doing some async work directly in the Activity/Fragment and then trying to do a Fragment Transaction when the result comes back.
There are a number of things you can do to solve this problem:
Don't use Fragments
There is almost never a need to use a Fragment - 90% of the time you could achieve the same result using a ViewGroup and avoid all the lifecycle headaches, random crashes & otherwise erratic behaviour Fragments cause.
The only times I'd use a Fragment were if I needed a specific lifecycle callback (onActivityResult) or if I'm forced to by a third party library (Android Pay, Braintree).
Separate Your View via an Interface
This is basically what the MVP/MVVM patterns are all about. It is separating your View from your business Logic (Presenter/ViewModel). This would help you because your Activity/Fragment would implement the View interface:
public class MyActivity implements MyView { ...
When your activity starts it binds to the Presenter and when it finishes it unbinds:
protected void onCreate(Bundle bundle) {
// ...
presenter.bind(this);
}
protected void onDestroy() {
// ...
presenter.unbind(this);
}
Now when in your business logic an async operation finishes and tries to update the view, if the Activity has finished the View will have been unbound. You can either null check it or (as I prefer) have a no-op View in its place.
Don't Update Your View From Long Running Async Operations
Where possible don't update your view directly from the results of a long running async process (network or heavy computation). Instead cache the results in the persistence layer (db/prefs/file). Have your view then subscribe to data from the persistence layer.
The subscription could be done in several ways - RxJava, event bus notification, static callback.
This way when the result comes back it just gets saved. If the view is still active it gets notified and loads the results from persistence. If not it can pick them up from there the next time it is loaded.
Finally
As an aside you can call .commitAllowingStateLoss()
instead of just .commit()
and it won't crash. This doesn't really fix the underlying problem though...