ProgressDialog not executing the first time - android

I've got an Activity that implements a retained fragment to perform a time consuming task. This allows me to rotate the screen without loosing a reference to my async task and without stopping it from being executed. I have a button on the activity to launch the Async task. Everytime the activity is created, for example due to a rotated screen, the activity checks if its retained fragment has an async task running. If so, it shows the progressDialog to let the user know there are still some tasks running on the background. I attach the code that does what I mentioned. Actually it works. However, what is strange is that it works everytime but the first! When I push the button to launch the Async process for the first time, the progressDialog is not shown, eventhough "onPreExecute()" is called and the line "progressDialog.show()" executed. If I rotate the screen after I push the button the progressDialog shows and when the process ends, If I push the button again then it works just fine. As I said, it is working all the time except the first one. Any idea why?
Thanks!
Activity
public class Activity extends AppCompatActivity implements ActivityTaskFragment.TaskCallbacks{
private ProgressDialog progressDialog;
private static final String TAG_TASK_FRAGMENT = "task_fragment";
private ActivityTaskFragment activityTaskFragment;
//Some additional code here
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_crear_turno);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
//------------------------------------------------------
setListeners();
setFragment();
createProgressDialog();
showProgressDialog();
}
private void showProgressDialog()
{
if(activityTaskFragment!=null)
{
if(activityTaskFragment.isRunning())
{
progressDialog.show();
}
}
}
private void setFragment()
{
FragmentManager fm = getFragmentManager();
activityTaskFragment = (ActivityTaskFragment) fm.findFragmentByTag(TAG_TASK_FRAGMENT);
// If the Fragment is non-null, then it is currently being
// retained across a configuration change.
if (activityTaskFragment == null) {
activityTaskFragment = new ActivityTaskFragment();
fm.beginTransaction().add(activityTaskFragment, TAG_TASK_FRAGMENT).commit();
}
}
private void setListeners() {
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
activityTaskFragment.execute();
}
});
}
//fragment interface implementation
#Override
public void onPreExecute()
{
if(progressDialog!=null)
progressDialog.show();
}
#Override
public void onCancelled() {
if(progressDialog!=null)
{
if(progressDialog.isShowing())
{
progressDialog.dismiss();
}
}
}
#Override
public void onPostExecute()
{
if(progressDialog!=null)
{
if(progressDialog.isShowing())
{
progressDialog.dismiss();
}
}
}
private void createProgressDialog()
{
if(progressDialog == null)
{
progressDialog = new ProgressDialog(Activity.this);
progressDialog.setTitle("Executing job");
progressDialog.setMessage("please wait...");
}
}
}
TaskFragment
/**
* This Fragment manages a single background task and retains
* itself across configuration changes.
*/
public class ActivityTaskFragment extends Fragment {
/**
* Callback interface through which the fragment will report the
* task's progress and results back to the Activity.
*/
interface TaskCallbacks {
void onPreExecute();
void onCancelled();
void onPostExecute();
}
private TaskCallbacks mCallbacks;
private CheckTask mTask;
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
mCallbacks = (TaskCallbacks) activity;
}
/**
* Hold a reference to the parent Activity so we can report the
* task's current progress and results. The Android framework
* will pass us a reference to the newly created Activity after
* each configuration change.
*/
public void execute()
{
mTask.cancel(true);
mTask = new CheckTask();
mTask.execute();
}
public void cancel()
{
if(mTask != null)
{
mTask.cancel(true);
}
}
#Override
public void onAttach(Context context) {
super.onAttach(context);
if(context instanceof Activity) {
Activity activity = (Activity) context;
mCallbacks = (TaskCallbacks) activity;
}
}
/**
* This method will only be called once when the retained
* Fragment is first created.
*/
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Retain this fragment across configuration changes.
setRetainInstance(true);
mTask = new CheckTask();
}
/**
* Set the callback to null so we don't accidentally leak the
* Activity instance.
*/
#Override
public void onDetach() {
super.onDetach();
mCallbacks = null;
}
/**
* A dummy task that performs some (dumb) background work and
* proxies progress updates and results back to the Activity.
*
* Note that we need to check if the callbacks are null in each
* method in case they are invoked after the Activity's and
* Fragment's onDestroy() method have been called.
*/
private class CheckTask extends AsyncTask<Void, Void, Void> {
#Override
protected void onPreExecute()
{
if (mCallbacks != null) {
mCallbacks.onPreExecute();
}
}
/**
* Note that we do NOT call the callback object's methods
* directly from the background thread, as this could result
* in a race condition.
*/
#Override
protected Void doInBackground(Void... ignore) {
/*
LONG TIME CONSUMING TASK
*/
return null;
}
#Override
protected void onCancelled() {
running = false;
if (mCallbacks != null) {
mCallbacks.onCancelled();
}
}
#Override
protected void onPostExecute(Void ignore)
{
running = false;
if (mCallbacks != null) {
mCallbacks.onPostExecute();
}
}
}
public boolean isRunning()
{
if(mTask!=null)
{
if(mTask.getStatus() == AsyncTask.Status.RUNNING)
{
return true;
}
else
{
return false;
}
}
else
{
return false;
}
}
}

Related

When to stop an AsyncTask in a retained DialogFragment?

I'm using a retained DialogFragment which display a progression dialog during a TCP connection executed in an AsyncTask. The AsyncTask is executed in the fragmebt's onStart() method, and cancelled either when the DialogFragment is cancelled, or in the fragment's onStop() method.
When my task finished (i.e, tcp connection is established or failed), or is cancelled, I dismiss the dialog using the dismiss() method.
The problem is when I press "Home" when the AsyncTask is running. In this case the fragment onStop() method is called, which cancels the AsyncTask, and try to dismiss the dialog, but then I get the following error :
java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
I saw there is a method named dismissAllowingStateLoss() which will avoid this exception but I'm not sure it's the best solution, I don't want it to "hide" an issue.
The expected behavior is the following: if the user leaves the activity, I want to cancel the connection, that's why I stop it in onStop().
Here is the Fragment code for reference :
public class TcpConnectionFragment extends DialogFragment {
private static final Logger logger = LoggerFactory.getLogger(TcpConnectionFragment.class);
private TcpConnectionTask connectionTask;
private String host;
private int port;
public static TcpConnectionFragment newInstance(String host, int port) {
Bundle bundle = new Bundle();
bundle.putString("ADDRESS", host);
bundle.putInt("PORT", port);
TcpConnectionFragment fragment = new TcpConnectionFragment();
fragment.setArguments(bundle);
return fragment;
}
private void startAsyncTask() {
logger.info("Starting asynchronous connection task");
connectionTask = new TcpConnectionTask(this, host, port);
connectionTask.execute();
}
private void stopAsyncTask() {
if (connectionTask == null)
return;
logger.info("Stopping asynchronous connection task");
connectionTask.cancel(false);
try {
SocketChannel channel = connectionTask.getSocketChannel();
if (channel != null)
channel.close();
} catch (IOException ignored) {
// We don't care about this error here, we can't recover.
}
}
public TcpConnectionFragment() {
super();
}
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
if (getArguments() == null)
throw new IllegalArgumentException("Missing arguments");
host = getArguments().getString("ADDRESS");
port = getArguments().getInt("PORT");
}
#Override
public void onActivityCreated(#Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
}
#NonNull
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
return new MaterialDialog.Builder(getContext())
.title(R.string.tcp_connect_dialog_title)
.content(R.string.tcp_connect_dialog_text, host, port)
.progress(true, 0)
.build();
}
#Override
public void onStart() {
super.onStart();
startAsyncTask();
}
#Override
public void onStop() {
super.onStop();
stopAsyncTask();
}
#Override
public void onCancel(DialogInterface dialog) {
super.onCancel(dialog);
logger.info("Cancelled dialog.");
stopAsyncTask();
}
/**
* This override is required to avoid a bug in the support library.
*/
#Override
public void onDestroyView() {
if (getDialog() != null && getRetainInstance())
getDialog().setDismissMessage(null);
super.onDestroyView();
}
/**
* Called by the {#link TcpConnectionTask} once connected.
*
* #param transport The transport created during the conenction.
*/
void onConnected(Transport transport) {
connectionTask = null;
dismiss();
}
/**
* Called by the {#link TcpConnectionTask} once the connection is cancelled.
*/
void onConnectionCancelled() {
connectionTask = null;
dismiss();
}
/*
* Called by the {#link TcpConnectionTask} if an error occurs during the connection.
*/
void onConnectionError(Exception error) {
connectionTask = null;
dismiss();
if (getParentFragment() == null || getParentFragment().getView() == null)
return;
Snackbar.make(getParentFragment().getView().findViewById(R.id.frame_layout),
"Error: " + error.getMessage(), Snackbar.LENGTH_LONG).show();
}
}
Try to call dismiss from onPause() instead. That should do it.
Check out the DialogFragment diagram from this thread:
In a DialogFragment, what should onCreate do?
Example on cancellation callback:
mCancelObj = new CancellationObject();
new AsyncTask<Void, Void, Void>() {
// do something
// whenFinished check if !mCancelObj.isCancelled -> dialog.dismiss();
}.execute();
void onPause(){
mCancelObj.cancel();
}

Why does FragmentManager.findFragmentByTag(TAG_TASK_FRAGMENT) not return null

I read this http://www.androiddesignpatterns.com/2013/04/retaining-objects-across-config-changes.html. And I played the example code in the link. To my surprise, fm.findFragmentByTag(TAG_TASK_FRAGMENT) does not return null when I rotate the phone, if I remove setRetainInstance(true) in the TaskFragment.onCreate(). I copied the code here with one line change (remove setRetainInstance(true)).
Please explain why fm.findFragmentByTag(TAG_TASK_FRAGMENT) does not return null in this case.
public class MainActivity extends Activity implements TaskFragment.TaskCallbacks {
private static final String TAG_TASK_FRAGMENT = "task_fragment";
private TaskFragment mTaskFragment;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
FragmentManager fm = getFragmentManager();
mTaskFragment = (TaskFragment) fm.findFragmentByTag(TAG_TASK_FRAGMENT);
if (mTaskFragment == null) {
mTaskFragment = new TaskFragment();
fm.beginTransaction().add(mTaskFragment, TAG_TASK_FRAGMENT).commit();
}
}
#Override
public void onPreExecute() { }
#Override
public void onProgressUpdate(int percent) { }
#Override
public void onCancelled() { }
#Override
public void onPostExecute() { }
}
public class TaskFragment extends Fragment {
interface TaskCallbacks {
void onPreExecute();
void onProgressUpdate(int percent);
void onCancelled();
void onPostExecute();
}
private TaskCallbacks mCallbacks;
private DummyTask mTask;
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
mCallbacks = (TaskCallbacks) activity;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// I remove this call to produce the problem
// setRetainInstance(true);
mTask = new DummyTask();
mTask.execute();
}
#Override
public void onDetach() {
super.onDetach();
mCallbacks = null;
}
private class DummyTask extends AsyncTask<Void, Integer, Void> {
#Override
protected void onPreExecute() {
if (mCallbacks != null) {
mCallbacks.onPreExecute();
}
}
#Override
protected Void doInBackground(Void... ignore) {
for (int i = 0; !isCancelled() && i < 100; i++) {
SystemClock.sleep(100);
publishProgress(i);
}
return null;
}
#Override
protected void onProgressUpdate(Integer... percent) {
if (mCallbacks != null) {
mCallbacks.onProgressUpdate(percent[0]);
}
}
#Override
protected void onCancelled() {
if (mCallbacks != null) {
mCallbacks.onCancelled();
}
}
#Override
protected void onPostExecute(Void ignore) {
if (mCallbacks != null) {
mCallbacks.onPostExecute();
}
}
}
}
SetRetainInstance controls whether the entire fragment (and its contents) is retained in memory or whether it is recreated as a new Fragment from its Bundle.
The only time it would return null is the very first time the app is run. After that it has been added to the FragmentManager and is always available. (Rotating the device does not clear the FragmentManager regardless of whether you use SetRetainInstance or not)
You seem to think that SetRetainInstance controls whether the fragment is kept in the FragmentManager or not. It does not.
In your example, the AsyncTask starts running the first time the Fragment is created. SetRetainInstance is used to stop the OnDestroy method of the Fragment being called. After an orientation change, the fragment and its running task is still in the FragmentManager and the task is still running. Without SetRetainInstance, when the Orientation change occurs, the fragment is destroyed and recreated from its bundle when you retrieve it from the FragmentManager. This puts the AsyncTask in a delicate state as the task could still be running even if its hosting Fragment has been destroyed possibly leading to a crash.
See this question for a more in depth explanation.
Understanding Fragment's setRetainInstance(boolean)

Android AsyncTask re-runs when activity is opened

In my App the user has to login.
They open the app on the login page
They enter email/password and hit login
A LoadingScreenActivity is opened that has a swirly circle and is running an AsyncTask that goes to my database and retrieves all the users info
After the AsyncTask is completed it starts an intent to launch MainPageActivity.
There are two problems with this at the moment:
If the user logs in and then goes to the home screen while the app loads the MainPageActivity will open as soon as it is ready (on top of the existing home page) even though the app has been minimised
If the user logs in and then goes to the home screen while the app loads and then returns to the loading screen the AsyncTask will complete twice over
For problem 1. At the moment my onPostExecute() method in LoadingScreenActivity looks like this:
#Override
public void onPostExecute() {
//open the main page
Intent mainPage = new Intent(getApplicationContext(), MainPageActivity.class);
mainPage.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
mainPage.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK );
startActivity(mainPage);
}
Is there a way I could detect in this method if the main page activity should be opened yet?
For problem 2. I've hit a complete road block on this, is there a way to detect if the activity has simply been re opened rather than started for the first time? I'd really appreciate any tips on this, I'm quite new to android so I'm not even convinced an Async task is the way to go with this.
Thanks for your time
LoadingScreenActivity.java
public class LoadingScreenActivity extends Activity implements TaskFragment.TaskCallbacks {
private static final String TAG_TASK_FRAGMENT = "task_fragment";
private TaskFragment mTaskFragment;
#Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
FragmentManager fm = getFragmentManager();
mTaskFragment = (TaskFragment) fm.findFragmentByTag(TAG_TASK_FRAGMENT);
// If the Fragment is non-null, then it is currently being
// retained across a configuration change.
if (mTaskFragment == null) {
mTaskFragment = new TaskFragment();
fm.beginTransaction().add(mTaskFragment, TAG_TASK_FRAGMENT).commit();
}
setContentView(R.layout.loading_screen);
ActionBar actionBar = getActionBar();
actionBar.hide();
TextView title = (TextView) findViewById(R.id.loading_title);
TextView progress = (TextView) findViewById(R.id.loading_progress);
title.setText(R.string.app_name);
progress.setText("Loading your info");
}
#Override
public Context onPreExecute() {
return getApplicationContext();
}
#Override
public void onProgressUpdate(int percent) {
}
#Override
public void onCancelled() {
Intent login = new Intent(getApplicationContext(), LoginActivity.class);
login.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(login);
finish();
}
#Override
public void onPostExecute() {
//open the main page
Intent mainPage = new Intent(getApplicationContext(), MainPageActivity.class);
mainPage.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
mainPage.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK );
startActivity(mainPage);
}
}
and TaskFragment.java
public class TaskFragment extends Fragment {
static interface TaskCallbacks {
Context onPreExecute();
void onProgressUpdate(int percent);
void onCancelled();
void onPostExecute();
}
private TaskCallbacks mCallbacks;
private DummyTask mTask;
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
mCallbacks = (TaskCallbacks) activity;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Retain this fragment across configuration changes.
setRetainInstance(true);
// Create and execute the background task.
mTask = new DummyTask();
mTask.execute();
}
#Override
public void onDetach() {
super.onDetach();
mCallbacks = null;
}
private class DummyTask extends AsyncTask<Void, Integer, Void> {
Context context;
boolean running = true;
#Override
protected void onPreExecute() {
if (mCallbacks != null) {
context = mCallbacks.onPreExecute();
}
}
#Override
protected Void doInBackground(Void... ignore) {
//Get the current thread's token
synchronized (this)
{
if(running){
DatabaseHandler dbHandler = new DatabaseHandler(context);
dbHandler.populateSQLiteDatabase();
}
}
return null;
}
#Override
protected void onProgressUpdate(Integer... percent) {
if (mCallbacks != null) {
mCallbacks.onProgressUpdate(percent[0]);
}
}
#Override
protected void onCancelled() {
if (mCallbacks != null) {
mCallbacks.onCancelled();
}
}
#Override
protected void onPostExecute(Void ignore) {
if (mCallbacks != null) {
mCallbacks.onPostExecute();
}
}
}
}
in your activity in the manifest just add android:configChanges="keyboardHidden|orientation|screenSize"
and in the activity implement this
#Override
public void onConfigurationChanged(Configuration newConfig) {
};
I'm afraid, i don't really understand your first problem.
About the second one, there are a couple of ways depending on your minimum API level. Starting from API 14 you may register ActivityLifecycle Callbacks inside an Android Application. To do this, i would recommend:
Inherit Android application with a custom one
Replace the Android application in your manifest
inside your custom application register itself as activity lifecycle listener
inside the abstract methods you get the instance of the currently applying activity (may safe object.name() in a String)
depending on your handling you may safe a boolean value or whatever to identify the behaviour
methods inside your custom application are accessible by casting (MyCustomApplication)getApplication()
Heres a snippet:
package com.example.preferencestest;
import android.app.Activity;
import android.app.Application;
import android.app.Application.ActivityLifecycleCallbacks;
import android.os.Bundle;
public class MyCustomApplication extends Application implements ActivityLifecycleCallbacks {
private String storedActivity;
private Boolean doOrNot = false;
public MyCustomApplication() {}
public Boolean getDoOrNot() { return doOrNot;}
public void setDoOrNot(Boolean doOrNot) { this.doOrNot = doOrNot; }
#Override
public void onCreate() {
super.onCreate();
registerActivityLifecycleCallbacks(this);
}
// these two are the most important ones since they will be fired everytime
#Override
public void onActivityResumed(Activity activity) {
if (activity.getClass().getName().equals(storedActivity)) {
doOrNot = true;
}
}
#Override
public void onActivityPaused(Activity activity) {
storedActivity = activity.getClass().getName();
}
#Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) { }
#Override
public void onActivityStarted(Activity activity) { }
#Override
public void onActivityStopped(Activity activity) { }
#Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) { }
#Override
public void onActivityDestroyed(Activity activity) { }
}
inside your Manifest you MUST declare this new Applicationclass like this:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.preferencestest"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="16"
android:targetSdkVersion="21" />
<application
android:name="com.example.preferencestest.MyCustomApplication"
{...}
Then inside your Activity you may do this:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Boolean whatToDo = ((MyCustomApplication)getApplication()).getDoOrNot();
}
Using onConfigurationChanged has a couple of disadvantages. There are a couple of actions (placing device in dock, turning display and so on) which restart the Activity. You should rather save the current state of the Activity with
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
}
#Override
protected void onRestoreInstanceState(Bundle state) {
super.onRestoreInstanceState(state);
}

Using progress Dialog instead of progress bar in asyncTask

I am trying to implement this tutorial, for handling Configuration changes while running background tasks. Everything works fine, and the app does not crash after a configuration change. In the tutorial, a progress bar is used to display progress. But in my own implementation i want to use a Progress Dialog.
I have used progress Dialog's lots of times, so calling it and getting to appear is not the problem. My problem is that unlike the progress Bar, the progress dialog gets dismissed on configuration change. Just like that.
Here is my code:
My MainActivity:
private TaskFragment mTaskFragment;
private ProgressDialog mProgressDialog;
private TextView mPercent;
private Button mButton;
#Override
protected void onCreate(Bundle savedInstanceState) {
Log.i(TAG, "onCreate(Bundle)");
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// Initialize views
mButton = (Button) findViewById(R.id.task_button);
mButton.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
if (mTaskFragment.isRunning()) {
mTaskFragment.cancel();
} else {
mTaskFragment.start();
}
}
});
mProgressBar = new ProgressDialog(this);
FragmentManager fm = getSupportFragmentManager();
mTaskFragment = (TaskFragment) fm.findFragmentByTag("task");
// If the Fragment is non-null, then it is currently being
// retained across a configuration change.
if (mTaskFragment == null) {
mTaskFragment = new TaskFragment();
fm.beginTransaction().add(mTaskFragment, "task").commit();
}
if (mTaskFragment.isRunning()) {
mButton.setText(getString(R.string.cancel));
} else {
mButton.setText(getString(R.string.start));
}
}
/****************************/
/***** CALLBACK METHODS *****/
/****************************/
#Override
public void onPreExecute() {
Log.i(TAG, "onPreExecute()");
mProgressBar.setTitle("Wacky");
mProgressBar.setMessage("wack");
mProgressBar.setIndeterminate(true);
mProgressBar.show();
mButton.setText(getString(R.string.cancel));
mButton.setText(getString(R.string.cancel));
Toast.makeText(this, R.string.task_started_msg, Toast.LENGTH_SHORT).show();
}
#Override
public void onProgressUpdate(int percent) {
//Log.i(TAG, "onProgressUpdate(" + percent + "%)");
}
#Override
public void onCancelled() {
Log.i(TAG, "onCancelled()");
mButton.setText(getString(R.string.start));
Toast.makeText(this, R.string.task_cancelled_msg, Toast.LENGTH_SHORT).show();
}
#Override
public void onPostExecute() {
Log.i(TAG, "onPostExecute()");
mButton.setText(getString(R.string.start));
Toast.makeText(this, R.string.task_complete_msg, Toast.LENGTH_SHORT).show();
}
My headless Fragment that holds my asyncTask
/**
* This Fragment manages a single background task and retains itself across
* configuration changes.
*/
public class TaskFragment extends Fragment {
public static final String TAG = TaskFragment.class.getSimpleName();
/**
* Callback interface through which the fragment can report the task's
* progress and results back to the Activity.
*/
public static interface TaskCallbacks {
public void onPreExecute();
public void onProgressUpdate(int percent);
public void onCancelled();
public void onPostExecute();
}
public TaskCallbacks mCallbacks;
public DummyTask mTask;
public boolean mRunning;
/**
* Android passes us a reference to the newly created Activity by calling this
* method after each configuration change.
*/
#Override
public void onAttach(Activity activity) {
Log.i(TAG, "onAttach(Activity)");
super.onAttach(activity);
if (!(activity instanceof TaskCallbacks)) {
throw new IllegalStateException("Activity must implement the TaskCallbacks interface.");
}
// Hold a reference to the parent Activity so we can report back the task's
// current progress and results.
mCallbacks = (TaskCallbacks) activity;
}
/**
* This method is called only once when the Fragment is first created.
*/
#Override
public void onCreate(Bundle savedInstanceState) {
Log.i(TAG, "onCreate(Bundle)");
super.onCreate(savedInstanceState);
setRetainInstance(true);
}
/**
* This method is <em>not</em> called when the Fragment is being retained
* across Activity instances.
*/
#Override
public void onDestroy() {
Log.i(TAG, "onDestroy()");
super.onDestroy();
cancel();
}
/*****************************/
/***** TASK FRAGMENT API *****/
/*****************************/
/**
* Start the background task.
*/
public void start() {
if (!mRunning) {
mTask = new DummyTask(this, mCallbacks);
mTask.execute();
mRunning = true;
}
}
/**
* Cancel the background task.
*/
public void cancel() {
if (mRunning) {
mTask.cancel(false);
mTask = null;
mRunning = false;
}
}
/**
* Returns the current state of the background task.
*/
public boolean isRunning() {
return mRunning;
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
Log.i(TAG, "onActivityCreated(Bundle)");
super.onActivityCreated(savedInstanceState);
}
}
My Background Task (in a seperate outer class)
/**
* A dummy task that performs some (dumb) background work and proxies progress
* updates and results back to the Activity.
*/
public class DummyTask extends AsyncTask<Void, Integer, Void> {
private TaskFragment fragment;
private TaskCallbacks callbacks;
private ProgressDialog mProgressBar;
MainActivity activity;
public DummyTask(TaskFragment taskFragment, TaskCallbacks mCallbacks) {
// TODO Auto-generated constructor stub
this.fragment = taskFragment;
this.callbacks = mCallbacks;
}
#Override
protected void onPreExecute() {
// Proxy the call to the Activity
fragment.mCallbacks.onPreExecute();
fragment.mRunning = true;
}
#Override
protected Void doInBackground(Void... ignore) {
for (int i = 0; !isCancelled() && i < 100; i++) {
//Log.i(TAG, "publishProgress(" + i + "%)");
SystemClock.sleep(100);
publishProgress(i);
}
return null;
}
#Override
protected void onProgressUpdate(Integer... percent) {
// Proxy the call to the Activity
fragment.mCallbacks.onProgressUpdate(percent[0]);
}
#Override
protected void onCancelled() {
// Proxy the call to the Activity
fragment.mCallbacks.onCancelled();
fragment.mRunning = false;
}
#Override
protected void onPostExecute(Void ignore) {
// Proxy the call to the Activity
fragment.mCallbacks.onPostExecute();
fragment.mRunning = false;
}
}
I am thinking it is the context which i am passing the progress dialog in the onCreate method of my Main Activity. Thanks for your help.
First, Activity is subclass of Context, you should know this already. Second, if Activity is destroyed, it is don't have a window anymore. Third, Dialog uses Context (read Activity) not because it wants so, but because it uses window associated with Activity to display itself.
It should be perfectly understandable, why after destroying activity during configuration change, Dialog no longer visible to you.
Method of preserving objects you using is good, but it can't preserve anything that would be destroyed during configuration change, such as any object that related to non-application Context, all this objects you need create manually every time Context changes.
You should use onSaveInstanceState(Bundle) to store state of ProgressDialog (shown or not) and show it again in your onCreate(Bunde) using value stored in Bundle.
Just an example
#Override
protected void onSaveInstanceState(Bundle outState){
super.onSaveInstanceState(outState);
outState.putBoolean("SHOW_DIALOG", mProgressBar.isShowing());
}
//...
#Override
protected void onCreate(Bundle savedInstanceState) {
//...
if (savedInstanceState != null){
if (savedInstanceState.getBoolean("SHOW_DIALOG") && mTaskFragment.isRunning()){
mProgressBar.setTitle("Wacky");
mProgressBar.setMessage("wack");
mProgressBar.setIndeterminate(true);
mProgressBar.show();
}
}
//...

How to determine in onPostExecute that activity is not paused now or destroyed?

I start AsyncTask and put Context in constructor of the Task.
On onPostExecute I want to know is my activity active now and was not recreated. How it's better to do?
For this I can:
create randomNumber in onCreate Activity and then put it in Application class;
onPause/onDestroy set randomNumber to 0 and in onResume restore randomNumber;
in onPreExecute() get randomNumber and in onPostExecute compare randomNumber with Application randomNumber.
May be I can use Context for making decision ... ?
There are many approaches to check if the activity is still there.
I usually create a SkeletonActivity with the following structure:
public class SkeletonActivity extends FragmentActivity {
private boolean paused = false;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
paused = false;
}
#Override
public void onPause() {
super.onPause();
paused = true;
}
#Override
public void onResume() {
super.onResume();
paused = false;
}
#Override
public void onStart() {
super.onStart();
paused = false;
}
public boolean isPaused() {
return paused;
}
}
Now let all your activities extend this SkeletonActivity. Finally change this Base Class to change the paused flag as you wish (For example update it in onDestroy()
Another way would be to have a Context instance inside your SkeletonActivity:
public class SkeletonActivity extends FragmentActivity {
private Context mContext;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mContext = this;
}
#Override
protected void onDestroy() {
mContext = null;
super.onDestroy();
}
public boolean isPaused() {
return mContext==null;
}
}
Please have a look at this example. I do store the Context in the AsyncTask. During onRetainNonConfigurationInstance() I set it to null and during onCreate() I patch the Context to the currently running Activity. You can enhance that by setting "task = null" in onDestroy():
public class MyActivity extends Activity {
/* package */ MyActivity context;
private class MyAsyncTask extends AsyncTask<Void, Void, Void> {
public MyAsyncTask(final MyActivity context) {
super();
this.context = context;
}
#Override
protected Cursor doInBackground(/* ... */) {
// ...
}
#Override
protected void onPostExecute(/* ... */) {
if (context != null) {
// ...
}
}
#Override
protected void onPreExecute (/* ... */) {
// ...
}
}
/* package */ MyAsyncTask task;
#Override
public void onCreate(final Bundle bundle) {
// ...
Bundle bundleExtras = getIntent().getExtras();
if (bundleExtras != null) {
task = (MyAsyncTask) getLastNonConfigurationInstance();
if (task != null) {
task.context = this;
// ...
} else {
task = new MyAsyncTask(this);
task.execute();
}
}
}
#Override
public Object onRetainNonConfigurationInstance() {
if (task != null) {
// ...
task.context = null;
}
return task;
}
}
It will be help full for other guys.
if you press the home button or back button onStop() will called.so declare a variable inside the activity like
boolean isActivityclosed=false;
During the onStop() method set value to true.
#Override
protected void onStop() {
super.onStop();
isActivityclosed=true;
}
now, at postExcecute method check the variable is true or not. if its true then your activity is in background.

Categories

Resources