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.
Related
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()
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
I have an Activity with a ViewPager and some Fragments in it.
The problem: It takes ages to start the activity because the ViewPager is created in the onResume methode. 2-3 seconds because of database requests.
I thought I can start a AsyncTask in onResume and do there the heavy work. But the time to start the Activity doesn't decrease.
If I place a Button in the View and do the work in onClick everything works fine. The Activity starts really fast and after the click the heavy work starts.
some code to think about:
The AsyncTask implementation
private class LoadTask extends AsyncTask<Void, Integer, Boolean>{
#Override
protected Boolean doInBackground(Void... params) {
PageAdapter mPageAdapter = new PageAdapter(
getSupportFragmentManager(), mFragments);
mViewPager.setAdapter(mPageAdapter);
return true;
}
}
the task is called in onResume
#Override
protected void onResume() {
super.onResume();
LoadTask task = new LoadTask();
task.execute();
}
This solution doesn't improve the starting time of the activity.
Is there a way to start the Activity and after inflating and displaying everything (progressbar...) doing the heavy task?
What you need to do is setup the empty view pager and link it to the adapter when you create your activity. Then, fill the content into whatever collection backs your adapter in the async task and notify the view that the dataset has changed. The view can't be displayed until it's created and you have moved the registering of the adapter to the async task so you won't notice a difference. The goal is to have your app show some view when you start it and simply fill in the data as it becomes available (as you probably know).
In my activity I need a ProgressDialog with a horizontal progress bar to visualise the progress of a background task. To make the activity care for the dialog e.g. in case of screen rotation, I would like to use a managed dialog created in onCreateDialog. The problem is that I need to update the progress bar of the dialog after it has been created and therefor I need a reference to the managed progress dialog: Does anyone know how to retrieve a reference to a dialog created by onCreateDialog?
At the moment I am storing a reference to the dialog created in onCreateDialog, but that my fail with a InvalidArgumentException in the onFinished() method after the screen has been rotated (and the activity has been recreated):
public final class MyActivity extends Activity {
private static final int DIALOG_PROGRESS = 0;
private ProgressDialog progressDialog = null;
// [...]
#Override
protected Dialog onCreateDialog(int id) {
switch (id) {
case DIALOG_PROGRESS:
progressDialog = createProgressDialog();
return progressDialog;
default:
return super.onCreateDialog(id);
}
}
// [...]
public void updateProgress(int progress) {
progressDialog.setProgress(0);
}
public void onFinished() {
progressDialog.dismiss();
}
// [...]
}
I would have expected something like a getDialog(int) method in the Activity class to get a reference to a managed dialog, but this doesn't seem to exist. Any ideas?
I answer myself:
There really is no getDialog(int) method available in the Activity class.
Storing the reference like shown above works correctly -- the bug was something else...
The problem was, that the parallel thread, that called the onFinished() method called this method on the already destroyed activity, thus the accessed ProgressDialog instance is still existing but no longer a valid dialog. Instead another activity with another ProgressDialog has already been created by Android.
So all I needed to do was to make the background thread call the onFinished() method of the new activity and everything works fine. To switch the reference I override the onRetainNonConfigurationInstance() and getLastNonConfigurationInstance() methods of the Activity class.
The good thing of the shown example: Android really cares about recreating the new dialog after the screen orientation changed. So constructing the ProgressDialog that way is definitely easier than using ProgressDialog.show() where I would need to handle the dialog recreation on my own (the two methods described above would be a good place to do this.
I've been working with AsyncTasks in Android and I am dealing with an issue.
Take a simple example, an Activity with one AsyncTask. The task on the background does not do anything spectacular, it just sleeps for 8 seconds.
At the end of the AsyncTask in the onPostExecute() method I am just setting a button visibility status to View.VISIBLE, only to verify my results.
Now, this works great until the user decides to change his phones orientation while the AsyncTask is working (within the 8 second sleep window).
I understand the Android activity life cycle and I know the activity gets destroyed and recreated.
This is where the problem comes in. The AsyncTask is referring to a button and apparently holds a reference to the context that started the AsyncTask in the first place.
I would expect, that this old context (since the user caused an orientation change) to either become null and the AsyncTask to throw an NPE for the reference to the button it is trying to make visible.
Instead, no NPE is thrown, the AsyncTask thinks that the button reference is not null, sets it to visible. The result? Nothing is happening on the screen!
Update: I have tackled this by keeping a WeakReference to the activity and switching when a configuration change happens. This is cumbersome.
Here's the code:
public class Main extends Activity {
private Button mButton = null;
private Button mTestButton = null;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mButton = (Button) findViewById(R.id.btnStart);
mButton.setOnClickListener(new OnClickListener () {
#Override
public void onClick(View v) {
new taskDoSomething().execute(0l);
}
});
mTestButton = (Button) findViewById(R.id.btnTest);
}
private class TaskDoSomething extends AsyncTask<Long, Integer, Integer>
{
#Override
protected Integer doInBackground(Long... params) {
Log.i("LOGGER", "Starting...");
try {
Thread.sleep(8000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return 0;
}
#Override
protected void onPostExecute(Integer result) {
Log.i("LOGGER", "...Done");
mTestButton.setVisibility(View.VISIBLE);
}
}
}
Try executing it and while the AsyncTask is working change your phones orientation.
AsyncTask is not designed to be reused once an Activity has been torn down and restarted. The internal Handler object becomes stale, just like you stated. In the Shelves example by Romain Guy, he simple cancels any currently running AsyncTask's and then restarts new ones post-orientation change.
It is possible to hand off your Thread to the new Activity, but it adds a lot of plumbing. There is no generally agreed on way to do this, but you can read about my method here : http://foo.jasonhudgins.com/2010/03/simple-progressbar-tutorial.html
If you only need a context and won't use it for ui stuff you can simply pass the ApplicationContext to your AsyncTask.You often need the context for system resources, for example.
Don't try to update the UI from an AsyncTask and try to avoid handling configuration changes yourself as it can get messy. In order to update the UI you could register a Broadcast receiver and send a Broadcast.
You should also have the AsyncTask as a separate public class from the activity as mentioned above, it makes testing a lot easier. Unfortunately Android programming often reinforces bad practices and the official examples are not helping.
This is the type of thing that leads me to always prevent my Activity from being destroyed/recreated on orientation change.
To do so add this to your <Activity> tag in your manifest file:
android:configChanges="orientation|keyboardHidden"
And override onConfigurationChanged in your Activity class:
#Override
public void onConfigurationChanged(final Configuration newConfig)
{
// Ignore orientation change to keep activity from restarting
super.onConfigurationChanged(newConfig);
}
To avoid this you can use the answer givin here: https://stackoverflow.com/a/2124731/327011
But if you need to destroy the activity (different layouts for portrait and landscape) you can make the AsyncTask a public class (Read here why it shouldn't be private Android: AsyncTask recommendations: private class or public class?) and then create a method setActivity to set the reference to the current activity whenever it is destroyed/created.
You can see an example here: Android AsyncTask in external class