Fragments: IllegalStateException Can not perform this action after onSaveInstanceState - android

I am sending an HTTP request to some server(in a background thread) inside onCreateView() method within my fragment class.
When the request received i check some stuff and according to that i add a new fragment(through fragment transaction in the UI thread).
I use the support library
But, for example, if the user press the android home button, while the request hasn't received yet, the fragment is going to pause or stop state and then the request received and of course the exception is thrown(just after trying to commit).
I searched the web and found some very good articles and answers which are relevant to this issue and all the things related to the 'state loss' after onSaveInstanceState() has been called, etc...
For example i read this excellent article of Alex Lockwood: http://www.androiddesignpatterns.com/2013/08/fragment-transaction-commit-state-loss.html
and also those stackoverflow questions/answers: getting exception "IllegalStateException: Can not perform this action after onSaveInstanceState"
and: "Can not perform this action after onSaveInstanceState" - why am I getting this exception from my activity's onResume method? and more.
But i couldn't came to a conclusion of what to do in my case.
The only thing that came to mind is to use commitAllowingStateLoss() instead of commit() but this fills hacky and not correct as some of the answers that i read conclude about it.
Also there was a suggestion to commit the transaction in onCreate() because it safe, but of course it can't work in my situation, all other suggestions doesn't work for me as well.
Maybe i can use some boolean flag to check if i can't make a transaction and then wait for the fragment to resume and if the flag is true then do the transaction.
This fills to much work and also presents the problem in which i need to know if the transaction can be done(don't even know how to check it).

Related

How to know if FragmentManager's back stack popped anything?

I want to know if anything got popped of when I called getFragmentManager().popBackStack(). How can I find this out?
I'm using android.support.v4.app.FragmentManager.
What I tried
Way 1: return value
In the documentation for android.support.v4.app.FragmentManager.popBackStack(), it says:
Pop the top state off the back stack. Returns true if there was one to pop, else false.
So according to that, it should work like this:
boolean popped = getFragmentManager().popBackStack();
But this doesn't work because the return value of popBackStack() is void.
Is this either a bug in their code or in their documentation?
Way 2: back stack count
I tried to calculate from getFragmentManager().getBackStackEntryCount() if anything was popped:
int countBefore = getFragmentManager().getBackStackEntryCount();
getFragmentManager().popBackStack();
int countAfter = getFragmentManager().getBackStackEntryCount();
boolean popped = countAfter != countBefore;
But this also doesn't work because of how popBackStack() works:
This function is asynchronous -- it enqueues the request to pop, but
the action will not be performed until the application returns to its
event loop.
So this means that both calls to getBackStackEntryCount() give me the same number.
Questions
How can I find out if anything was popped?
What I described in way 1: Is either a bug in Android's code or in Android's documentation?
If it's a bug: How can I tell them, that there's something mixed up? (I'm asking this because once I already tried to fix a mistake in their Android documentation but until today they never merged my pull request)
Question: How can I find out if anything was popped?
There are two methods for popping back stack:
void popBackStack()
Pop the top state off the back stack. This function is asynchronous -- it enqueues the request to pop, but the action will not be performed until the application returns to its event loop.
boolean popBackStackImmediate()
Like popBackStack(), but performs the operation immediately inside of the call. This is like calling executePendingTransactions() afterwards without forcing the start of postponed Transactions.
The latter one seems like the one you were looking for.
Alternatively you could addOnBackStackChangedListener in onStart and unregister it in onStop if you need to observe changes at all times.
Question: What I described in way 1: Is either a bug in Android's code or in Android's documentation?
Quick glance in the docs reveals both methods with appropriate description. Support library looks intact as well. This was probably an oversight on your part.
EDIT: Ok, I glanced too quickly. It does say "Pop the top state off the back stack. Returns true if there was one to pop, else false. This function is asynchronous [...]". This is of course wrong.
Question: If it's a bug: How can I tell them, that there's something mixed up?
GitHub repo is only a mirror. The Android dev team does not go there and merge pull requests. If you wish to contribute, use AOSP gerrit. More info here: https://source.android.com/source/contributing
Alternatively you can file a bug report here: https://issuetracker.google.com/issues

What to do when activity is destroyed but I must continue executing code in fragment?

I have a pretty odd problem here. In a fragment, I do a process and when the process finishes I show an advert that callsback to the fragment when user clicks/dismisses the ad. The problem is that in some devices when the ad calls back to the handler (that is in the running fragment) the activity containing the fragment has been destroyed, but I need to do some more work through a runnable. So, in this case the runnable throws a NullPointerException int is run method when executed.
I could just check if the activity is still alive and run just the runnable when it is, but in the cases it is not alive I still need to continue to do the part of the job that needs to be done after the ad.
How do you handle this kind of situations? I have been thinking about the problem during some hours without finding a solution to this.
Thanks in advance.
You can use AsyncTask in this case .
AsyncTask processes are not automatically killed by the OS. AsyncTask processes run in the background and is responsible for finishing it's own job in any case. You can cancel your AsycnTask by calling cancel(true) method. This will cause subsequent calls to isCancelled() to return true. After invoking this method, onCancelled(Object) method is called instead of onPostExecute() after doInBackground() returns.
Hope it helps..
mmm the way this is asked I am not sure what you are asking, perhaps some text connectors might work, I am not sure if this is a quite basic question about state changes or a very complex one.
from what I understood:
wouldn't this be the same problem as when you flip screen? make a Bundle of the data that is restored through activity changes. This way if your activity has been Destroyed you restore it
fragments have a feature that you can use to keep instance alive across a configuration change: retainInstance
setRetainInstance(true) // false by default
Parcelable like Serializable, is an API for saving an object out to a stream of bytes. Objects may elect to implement the Parcelable interface if they are what we will call "stashable" here. Objects are stashed in Java by putting them in a Bundle, or by marking them Serializable so they can be serialized, or by implementing the Parcelable interface. Whichever way you do it, the same idea applies: you should not be using any of these tools unless your object is stashable
---or---
turn that "advert" of yours into an Alert, which wont mess with the Activity.
---or---
run the response on a different thread?

android - dialogfragment using commitAllowStateLoss

What are the effects of commiting a dialogfragment transaction with state loss in android: Since its just a simple error dialog im showing with an ok button to close it i dont think i need to worry about state loss.
in my DialogFragment subclass i've over rided the show class so that it commits to include state loss so that i dont get illegalstateException...
#Override
public void show(FragmentManager manager, String tag) {
FragmentTransaction ft = manager.beginTransaction();
ft.add(this, tag);
//its just dialogs so can we allow state loss to not trigger illegalStateExceptions
ft.commitAllowingStateLoss();
}
According to this Article
Originally you are trying to avoid this error (Caused by: java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState).
This error stems from the fact that these Bundle objects of the onSaveInstanceState() represent a snapshot of an Activity at the moment onSaveInstanceState() was called, and nothing more. That means when you call FragmentTransaction#commit() after onSaveInstanceState() is called, the transaction won't be remembered because it was never recorded as part of the Activity's state in the first place
You tried to work around that by using commitAllowingStateLoss(), let's discuss
the difference between calling commit() and commitAllowingStateLoss() is that the latter will not throw an exception if state loss occurs. Usually you don't want to use this method because it implies that there is a possibility that state loss could happen. The better solution, of course, is to write your application so that commit() is guaranteed to be called before the activity's state has been saved, as this will result in a better user experience. Unless the possibility of state loss can't be avoided, commitAllowingStateLoss() should not be used.
Surprised no one ever gave a simple correct answer. The transaction will be lost. Since in this case you are adding a dialog fragment, if the fragments have to be restored, the dialog will be gone.
Test this by going into the developer settings and check 'never keep activities' or something that sounds a lot like that. Then go to your app, get the dialog open and press Home. Then open the app again.

App crashes when user changes fragment during AsyncTask

I've got the following problem. In my app I'm loading data in an AsyncTask. The problem is, when the user now clicks on the icon to open the Navigation Drawer and opens up another fragment the app crashes. When the AsyncTask is finished the app doesn't crash. The problem that is encountering is, that when I switch the fragment (The fragments are always the same, just with another content dependent on the NavigationDrawer Item click) the app crashes.
I guess the problem is, that the async task isn't finished, I'm calling the same fragment again want to display different data.
So what would be my approach to handle this? Use for every different view a different fragment? I thought using the same fragment every time is much easier, since it's just displaying different data but the structure, layout etc. is all the same. Just the data that it gets is different.
I also thought about somehow "blocking" the user from doing any other actions while the asynctask but still show him that the app is processing. But that would be not the definition of an AsyncTask.
How would you approach it? Use different fragments for every different display? Or how? Block somehow? If a user clicks on an item of the navigation drawer the asynctask needs to stop all its actions (if some are done) and then restart doing all the actions. Is there a way to do it?
Please note that the fragment where the async is executed and the activity where the fragments are called are in two different files
You can either block the screen with a loading screen (not that good UX wise...) or you could cancel the asynctask when you change the fragment, in the destroy or detach method.
You didnt show the errors, but I would guess that the app crashes because you are trying to acess something in the asynctask onPostExecute method and it is no longer available...
I guess that it crashes because your AsyncTask is sending data to a class instance that doens't exist.You should change the Class that receives callbacks from asynctask. Anyway i can't give you a better answer till i will see your real code of AsyncTask ( at least onPostExecute() and onProgressUpdate())
use intent service to do that ask task means call ask task in a intent service that one is capable to handle background task without hang UI

Handling of server requests and device rotations

I read a lot about handling rotation in android applications, but I still have so many questions and need to much to understand.
Let me explain my problem or implementation, that I'm using now in my application.
If an activity will be opened, a get request will be sent to server. This request will be executed in a Thread (new Thread(...)) and if request was completed, activity's ui will be refreshed.
But what should I do, if the user rotate his device?
By default, the activity will be destroyed and request will be started again and start a new thread, but the thread of destroyed activity may be still running.
I guess, it's a quite wrong way, I have now.
But what is the best approach, to handle this?
Probably is the best way to forbid rotation, but what If I don't want that?!
May be it's the second part of my question:
I saw a video form Google IO. Mr. Dobjanschi suggested to use services and to store retrieved data in content provider. So, probably I can use a service for executing my requests. But should data be replaced every time the get request was completed?!
Well dont know exactly how its done, You can try saving the instance and retrieving the same when config changes with following methods:
I have read about them but haven't really implemented them yet. I hope it can give you some start.
#Override
public Object onRetainNonConfigurationInstance() {
return(myServerThread);
}
private void restoreServerFunctions() {
if (getLastNonConfigurationInstance()!=null) {
myServerThread=(Thread)getLastNonConfigurationInstance();
}
}
You can specify that the activity handles the rotation itself. This is done through adding:
android:configChanges="keyboardHidden|orientation"
in the tag of the activity inside your android manifest. You don't have to actually handle the rotation but this will tell android to not destroy your activity. The base activity class will handle all the rotating of the user interface for you and your thread will be executed correct.
A small side note: if you are doing only a small server task use AsyncTask to execute the call to the server in the background instead of creating a thread. This will minimze some of the programming effort you need to communicate the results from the thread to the activity and update your UI.
One easy way, though I've never tried it. Instead of refreshing the current UI, when the thread finishes, start a new Activity with the just downloaded content. So first, you start an Activity with a blank page (or just the page's frame), then you rotate the blank page as much as you like, then the downloading Thread spawns a new Activity, replacing the blank page Activity with the loaded content page using the current orientation.

Categories

Resources