How to handle screen orientation change when progress dialog and background thread active?

前端 未结 28 1199
轮回少年
轮回少年 2020-11-22 07:03

My program does some network activity in a background thread. Before starting, it pops up a progress dialog. The dialog is dismissed on the handler. This all works fine, exc

相关标签:
28条回答
  • 2020-11-22 07:50

    I faced this same problem, and I came up with a solution that didn't invole using the ProgressDialog and I get faster results.

    What I did was create a layout that has a ProgressBar in it.

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">
    <ProgressBar
        android:id="@+id/progressImage"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        />
    </RelativeLayout>
    

    Then in the onCreate method do the following

    public void onCreate(Bundle icicle) {
        super.onCreate(icicle);
        setContentView(R.layout.progress);
    }
    

    Then do the long task in a thread, and when that's finished have a Runnable set the content view to the real layout you want to use for this activity.

    For example:

    mHandler.post(new Runnable(){
    
    public void run() {
            setContentView(R.layout.my_layout);
        } 
    });
    

    This is what I did, and I've found that it runs faster than showing the ProgressDialog and it's less intrusive and has a better look in my opinion.

    However, if you're wanting to use the ProgressDialog, then this answer isn't for you.

    0 讨论(0)
  • 2020-11-22 07:50

    I discovered a solution to this that I haven't yet seen elsewhere. You can use a custom application object that knows if you have background tasks going, instead of trying to do this in the activity that gets destroyed and recreated on orientation change. I blogged about this in here.

    0 讨论(0)
  • 2020-11-22 07:51

    I faced the same situation. What I did was get only one instance for my progress dialog in the entire application.

    First, I created a DialogSingleton class to get only one instance (Singleton pattern)

    public class DialogSingleton
    {
        private static Dialog dialog;
    
        private static final Object mLock = new Object();
        private static DialogSingleton instance;
    
        private DialogSingleton()
        {
    
        }
    
        public static DialogSingleton GetInstance()
        {
            synchronized (mLock)
            {
                if(instance == null)
                {
                    instance = new DialogSingleton();
                }
    
                return instance;
            }
        }
    
        public void DialogShow(Context context, String title)
        {
            if(!((Activity)context).isFinishing())
            {
                dialog = new ProgressDialog(context, 2);
    
                dialog.setCanceledOnTouchOutside(false);
    
                dialog.setTitle(title);
    
                dialog.show();
            }
        }
    
        public void DialogDismiss(Context context)
        {
            if(!((Activity)context).isFinishing() && dialog.isShowing())
            {
                dialog.dismiss();
            }
        }
    }
    

    As I show in this class, I have the progress dialog as attribute. Every time I need to show a progress dialog, I get the unique instance and create a new ProgressDialog.

    DialogSingleton.GetInstance().DialogShow(this, "My title here!");
    

    When I am done with the background task, I call again the unique instance and dismiss its dialog.

    DialogSingleton.GetInstance().DialogDismiss(this);
    

    I save the background task status in my shared preferences. When I rotate the screen, I ask if I have a task running for this activity: (onCreate)

    if(Boolean.parseBoolean(preference.GetValue(IS_TASK_NAME_EXECUTED_KEY, "boolean").toString()))
    {
        DialogSingleton.GetInstance().DialogShow(this, "Checking credentials!");
    } // preference object gets the info from shared preferences (my own implementation to get and put data to shared preferences) and IS_TASK_NAME_EXECUTED_KEY is the key to save this flag (flag to know if this activity has a background task already running).
    

    When I start running a background task:

    preference.AddValue(IS_TASK_NAME_EXECUTED_KEY, true, "boolean");
    
    DialogSingleton.GetInstance().DialogShow(this, "My title here!");
    

    When I finish running a background task:

    preference.AddValue(IS_TASK_NAME_EXECUTED_KEY, false, "boolean");
    
    DialogSingleton.GetInstance().DialogDismiss(ActivityName.this);
    

    I hope it helps.

    0 讨论(0)
  • 2020-11-22 07:51

    This is a very old question that came up on the sidebar for some reason.

    If the background task only needs to survive while the activity is in the foreground, the "new" solution is to host the background thread (or, preferably, AsyncTask) in a retained fragment, as described in this developer guide and numerous Q&As.

    A retained fragment survives if the activity is destroyed for a configuration change, but not when the activity is destroyed in the background or back stack. Therefore, the background task should still be interrupted if isChangingConfigurations() is false in onPause().

    0 讨论(0)
  • 2020-11-22 07:51

    I've tried EVERYTHING. Spent days experimenting. I didn't want to block the activity from rotating. My scenario was:

    1. A progress dialog showing dynamic information to the user. E.g.: "Connecting to server...", "Downloading data...", etc.
    2. A thread doing the heavy stuff and updating the dialog
    3. Updating the UI with the results at the end.

    The problem was, when rotating the screen, every solution on the book failed. Even with the AsyncTask class, which is the correct Android way of dealing with this situations. When rotating the screen, the current Context that the starting thread is working with, is gone, and that messes up with the dialog that is showing. The problem was always the Dialog, no matter how many tricks I added to the code (passing new contexts to running threads, retaining thread states through rotations, etc...). The code complexity at the end was always huge and there was always something that could go wrong.

    The only solution that worked for me was the Activity/Dialog trick. It's simple and genius and it's all rotation proof:

    1. Instead of creating a Dialog and ask to show it, create an Activity that has been set in the manifest with android:theme="@android:style/Theme.Dialog". So, it just looks like a dialog.

    2. Replace showDialog(DIALOG_ID) with startActivityForResult(yourActivityDialog, yourCode);

    3. Use onActivityResult in the calling Activity to get the results from the executing thread (even the errors) and update the UI.

    4. On your 'ActivityDialog', use threads or AsyncTask to execute long tasks and onRetainNonConfigurationInstance to save "dialog" state when rotating the screen.

    This is fast and works fine. I still use dialogs for other tasks and the AsyncTask for something that doesn't require a constant dialog on screen. But with this scenario, I always go for the Activity/Dialog pattern.

    And, I didn't try it, but it's even possible to block that Activity/Dialog from rotating, when the thread is running, speeding things up, while allowing the calling Activity to rotate.

    0 讨论(0)
  • 2020-11-22 07:52

    Seems far too 'quick and dirty' to be true so please point out the flaws but what I found worked was...

    Within the onPostExecute method of my AsyncTask, I simply wrapped the '.dismiss' for the progress dialog in a try/catch block (with an empty catch) and then simply ignored the exception that was raised. Seems wrong to do but appears there are no ill effects (at least for what I am doing subsequently which is to start another activity passing in the result of my long running query as an Extra)

    0 讨论(0)
提交回复
热议问题