I've an Activity A that contains a Login Fragment and an Activity B that contains a Home Fragment.
I've to start B from Login Fragment after a succesfully login request (async).
I've a callback listener inside the login fragment:
onSuccess(result) {
startActivity(B);
}
Today I met this nice bug: getting exception "IllegalStateException: Can not perform this action after onSaveInstanceState".
I think that's not properly a bug, anyway I don't know how to workaround that. This blog post suggests to avoid transaction inside async callback methods, yeah but how? commitAllowingStateLoss() should be used as a last resort: in case, should I use it inside Home Fragment transaction in Activity B creation method?
Basically, what should I do to start another activity after async callback?
You should use onPostExecute(result) in the AsyncTask:
private class LoginTask extends AsyncTask<parameters,...> {
...
protected void onPostExecute(Long result) {
//if result successful start ActivityB
}
}
Onpost fires after the asynctask is complete.
It runs on the UI thread so that should solve your problem.
Put this in your main activity:
public void run(){
//code you would normally have after task completes
}
Then put this in your onSuccess:
mainactivity.runUIonthread()
Related
I'm using navigation in MainActivity, then I start SecondActivity (for result). After finish of SecondActivity I would like to continue with navigation in MainActivity, but FragmentManager has saved his state already.
On Navigation.findNavController(view).navigate(R.id.action_next, bundle) I receive log message:
Ignoring navigate() call: FragmentManager has already saved its state
How I can continue in navigation?
You must always call super.onActivityResult() in your Activity's onActivityResult. That is what:
Unlocks Fragments so they can do fragment transactions (i.e., avoid the state is already saved errors)
Dispatches onActivityResult callbacks to Fragments that called startActivityForResult.
Finally, I fix the issue by simple calling super.onPostResume() right before navigating to restore state.
I've solved this problem this way:
#Override
public void onActivityResult() { //inside my fragment that started activity for result
model.navigateToResults = true; //set flag, that navigation should be performed
}
and then
#Override
public void onResume() { //inside fragment that started activity for result
super.onResume();
if(model.navigateToResults){
model.navigateToResults = false;
navController.navigate(R.id.action_startFragment_to_resultsFragment);
}
}
not sure, if this is not a terrible hack, but it worked for me. FramgentManager state is restored at this point (onResume) and no problems with navigation occur.
I believe above solutions should work. But my problem was different. There was a third party sdk which was launching its activity using context provided by me and it was delivering the result on a listener which I had to implement.
So there was no option for me to work with onActivityResult :(
I used below hack to solve the issue:
private var runnable: Runnable? = null // Runnable object to contain the navigation code
override fun onResume() {
super.onResume()
// run any task waiting for this fragment to be resumed
runnable?.run()
}
override fun responseListener(response: Response) { // Function in which you are getting response
if (!isResumed) {
// add navigation to runnable as fragment is not resumed
runnable = Runnable {
navController.navigate(R.id.destination_to_navigate)
}
} else {
// navigate normally as fragment is already resumed
navController.navigate(R.id.destination_to_navigate)
}
}
Let me know if there is any better solution for this. Currently I found this very simple and easy to implement :)
call super.onPostResume() before navigation....It's working
Scenario: I have a dialog fragment. Onclick of an image it launches an activity which does the process in an asynctask. When I get back the result the activity finishes and goes back to the dialog fragment.On getting back the image must be changed.
Problem:The image remains the same and only when you close the dialogfragment and reopen it does it change.
Sorry I havnt posted the code, Just wanted some suggestions on how to update the view from the activitys async task.
If I understood you correctly, try use invalidate() method for your ImageView in onPostExecute() of your AsyncTask.
If you start your AsyncTask in launched Activity, as I suppose, you can start custom Activity in Dialog Fragment and pass your ImageView pointer to constructor. Something like this.
public class AsyncTaskActivity extends Activity{
private ImageView yourImage;
public AsyncTaskActivity(ImageView yourImage){
this.yourImage = yourImage;
}
....
//your AsyncTask should be like this
new AsyncTask<Void, Void, Void>(){
//here your backround process
...
//here we invalidate ImageView
#Override
protected void onPostExecute(Void result) {
//update image Bitmap or something else
...
yourImage.invalidate();
}
}.execute();
}
With best regards.
I would suggest the following:
Start the activity (B) by using startActivityForResult from your dialog.
Override OnActivityResult in your original activity (A, the one that host the dialog).
call super.OnAcitivityResult in it, so that the dialog can receive the call back.
Your dialogfragment should override OnActivityResult and change the image based on the result.
Need your code to see what you are actually trying to do.
I'm trying to understand under what circumstances can getActivity() return null in a fragment AFTER onAttach. I typically start an async task in onCreate or onCreateView inside my fragments but I'm getting error reports indicating sometimes getActivity() is null when the async task finishes. Error reports are coming in via crashlytics but can't reproduce them.
The async tasks are "blocking" - I display a modal non-dismissable progress bar. Also rotation is prevented by calling setRequestedOrientation.
I'm using v4 support Fragment and FragmentActivity. Fragments are set to retain state.
What am I missing? Are there other config changes that may cause the fragment to be detached?
I tried temporarily enabling rotation and the dev option to destroy activity after leaving it but still can't reproduce this...
Here's some of the relevant code inside one of my fragments, in this case it would sometimes break with an NPE at activity.dismissSpinner:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
checkIfLoggedIn();
}
public void checkIfLoggedIn() {
LoginActivity activity = (LoginActivity)getActivity();
activity.showSpinner("Connecting, please wait...");
AsyncTask<String, Void, JsonResponse> asyncTask = new AsyncTask<String, Void, JsonResponse>() {
protected JsonResponse doInBackground(String... notused) {
return cmsServer().getCurrentUser(getActivity());
}
protected void onPostExecute(JsonResponse result) {
LoginActivity activity = (LoginActivity)getActivity();
activity.dismissSpinner();
//...more stuff here
}
};
asyncTask.execute();
}
Do you stop/cancel your AsyncTask if your app goes to background or is paused?
Consider the following scenario: your AsyncTask is executed, and when prompted with the progress bar, the user decides to do other stuff while she waits for the task to complete. She does so by pressing the home button. Alas, this might destroy the fragment and the activity. The running AsyncTask knows nothing about it, and when done, getActivity() method invocations (or local variables pointing to a non-existent Activity) may as well return null, causing your app to crash.
The Fragmentlife cycle is as follows
According to Fragment life-cycle onCreate() and onCreateView()are called before the Activity creation. so when we call getActivity() in these methods in returns null.
so instead of starting the async task in the onCreateView() start it in onStart() or onResume() so that getActivity() returns the exact Activity reference.
For more details click here
First of all, some info about my project:
I have an application that consists of several fragments. (I have one class that extends FragmentActivity and another one that extends FragmentPagerAdapter.)
So actually my code is running inside the Fragments.
I call an asynctask inside one of the fragments to do some calculations. The asyncTask is a seperate file and the class is not located inside the Fragment class.
So 1 file is the asynctask and another one the fragmet (variables from the fragment are not accessible direct by the asynctask!!!).
I can display the result of the asyncTask by using ProgressDialog.
However I want to be able to return the data (result of the asynctask) back to a variable that I have in my fragment. What I have tried so far is:
Using get() from asyncatask.execute()
I tried to do it like this:
I used the following code that was called after a button click.
mytask1 = new myAsyncTask(this);
String result =mytask1.execute(value1).get();
However this results in the main thread to get stuck and wait for the asynctask to complete.
2.Using loginSharePreferens
loginPreferences = getActivity().getSharedPreferences("loginPrefs", Context.MODE_PRIVATE);
loginPrefsEditor = loginPreferences.edit();
loginPrefsEditor.putString("result", result);
loginPrefsEditor.commit();
This gave me errors.
3.I tried also this
SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext() );
That was similar with the 2nd method.
4.I tried to create a method in my fragment that would just set the value and I tried to call it inside the fragment
MyFrragment1.this.setRestult(1); // is called in the onPostExecute of the asyncTask
public void setRestult(int result){
this.test1=result; ///inside of the fragment
}
This gave me error: "No enclosing instance of the type MyFragment1 is accessible in scope"
I don't know what else to try. Any ideas ?
You can use the onPostExecute method which runs in the UI thread and is called after doInBackground finishes. You can override it in your Fragment like this:
new myAsyncTask() {
#Override
protected void onPostExecute( Result result ) {
super.onPostExecute( result );
// Do something with result here
}
}.execute(value1);
I have 3 Fragments in a Activity like splashscreen, login and register. My LoginFragment based on asynctask.
So how can I call registerfragment from onPostExecute() in the LoginFragment, after the login process has been done ?
Help me.
Code Snippet:
protected void onPostExecute(httpresponse reesponse) {
mProgressDialog.dismiss();
super.onPostExecute(response);
// here the fragment must be called but how???
}
Solved the problem that is...
mFragmentCallbacks.onFragmentFinished(fragmentname.this);
Thanks everyone