android - प्रगति संवाद और पृष्ठभूमि धागा सक्रिय होने पर स्क्रीन अभिविन्यास परिवर्तन को कैसे संभालें?




android-activity android-dialog (18)

मेरा प्रोग्राम पृष्ठभूमि थ्रेड में कुछ नेटवर्क गतिविधि करता है। शुरू करने से पहले, यह एक प्रगति संवाद पॉप अप करता है। संवाद हैंडलर पर खारिज कर दिया गया है। यह सब ठीक काम करता है, सिवाय इसके कि संवाद के दौरान स्क्रीन अभिविन्यास बदलता है (और पृष्ठभूमि धागा जा रहा है)। इस बिंदु पर ऐप या तो दुर्घटनाग्रस्त हो जाता है, या डेडलॉक्स, या एक अजीब चरण में आता है जहां ऐप तब तक काम नहीं करता जब तक कि सभी धागे मारे गए न हों।

मैं स्क्रीन अभिविन्यास परिवर्तन को गहन तरीके से कैसे संभाल सकता हूं?

नीचे दिया गया नमूना कोड मोटे तौर पर मेरे वास्तविक कार्यक्रम से मेल खाता है:

public class MyAct extends Activity implements Runnable {
    public ProgressDialog mProgress;

    // UI has a button that when pressed calls send

    public void send() {
         mProgress = ProgressDialog.show(this, "Please wait", 
                      "Please wait", 
                      true, true);
        Thread thread = new Thread(this);
        thread.start();
    }

    public void run() {
        Thread.sleep(10000);
        Message msg = new Message();
        mHandler.sendMessage(msg);
    }

    private final Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            mProgress.dismiss();
        }
    };
}

ढेर:

E/WindowManager(  244): Activity MyAct has leaked window [email protected] that was originally added here
E/WindowManager(  244): android.view.WindowLeaked: Activity MyAct has leaked window [email protected] that was originally added here
E/WindowManager(  244):     at android.view.ViewRoot.<init>(ViewRoot.java:178)
E/WindowManager(  244):     at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:147)
E/WindowManager(  244):     at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:90)
E/WindowManager(  244):     at android.view.Window$LocalWindowManager.addView(Window.java:393)
E/WindowManager(  244):     at android.app.Dialog.show(Dialog.java:212)
E/WindowManager(  244):     at android.app.ProgressDialog.show(ProgressDialog.java:103)
E/WindowManager(  244):     at android.app.ProgressDialog.show(ProgressDialog.java:91)
E/WindowManager(  244):     at MyAct.send(MyAct.java:294)
E/WindowManager(  244):     at MyAct$4.onClick(MyAct.java:174)
E/WindowManager(  244):     at android.view.View.performClick(View.java:2129)
E/WindowManager(  244):     at android.view.View.onTouchEvent(View.java:3543)
E/WindowManager(  244):     at android.widget.TextView.onTouchEvent(TextView.java:4664)
E/WindowManager(  244):     at android.view.View.dispatchTouchEvent(View.java:3198)

मैंने SaveInstanceState पर प्रगति संवाद को खारिज करने का प्रयास किया है, लेकिन यह केवल तत्काल दुर्घटना को रोकता है। पृष्ठभूमि धागा अभी भी जा रहा है, और यूआई आंशिक रूप से तैयार राज्य में है। फिर से काम करना शुरू करने से पहले पूरे ऐप को मारने की जरूरत है।


चाल सामान्य रूप से onPreExecute / onPostExecute के दौरान AsyncTask के भीतर संवाद को दिखाने / खारिज करना है, हालांकि अभिविन्यास-परिवर्तन के मामले में गतिविधि में संवाद का एक नया उदाहरण बनाएं / दिखाएं और कार्य के संदर्भ को पास करें।

public class MainActivity extends Activity {
    private Button mButton;
    private MyTask mTask = null;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        MyTask task = (MyTask) getLastNonConfigurationInstance();
        if(task != null){
            mTask = task;
            mTask.mContext = this;
            mTask.mDialog = ProgressDialog.show(this, "", "", true);        
        }

        mButton = (Button) findViewById(R.id.button1);
        mButton.setOnClickListener(new View.OnClickListener(){
            public void onClick(View v){
                mTask = new MyTask(MainActivity.this);
                mTask.execute();
            }
        });
    }


    @Override
    public Object onRetainNonConfigurationInstance() {
        String str = "null";
        if(mTask != null){
            str = mTask.toString();
            mTask.mDialog.dismiss();
        }
        Toast.makeText(this, str, Toast.LENGTH_SHORT).show();
        return mTask;
    }



    private class MyTask extends AsyncTask<Void, Void, Void>{
        private ProgressDialog mDialog;
        private MainActivity mContext;


        public MyTask(MainActivity context){
            super();
            mContext = context;
        }


        protected void onPreExecute() {
            mDialog = ProgressDialog.show(MainActivity.this, "", "", true);
        }

        protected void onPostExecute(Void result) {
            mContext.mTask = null;
            mDialog.dismiss();
        }


        @Override
        protected Void doInBackground(Void... params) {
            SystemClock.sleep(5000);
            return null;
        }       
    }
}

जब आप उन्मुखता स्विच करते हैं, तो एंड्रॉइड एक नया दृश्य तैयार करेगा। आपको शायद दुर्घटनाएं हो रही हैं क्योंकि आपका पृष्ठभूमि धागा पुरानी स्थिति को बदलने की कोशिश कर रहा है। (इसमें समस्या हो सकती है क्योंकि आपका पृष्ठभूमि थ्रेड UI थ्रेड पर नहीं है)

मैं सुझाव देता हूं कि उस mHandler को अस्थिर बनाना और अभिविन्यास बदलते समय इसे अपडेट करना।


मूल कथित समस्या यह थी कि कोड स्क्रीन अभिविन्यास परिवर्तन से बच नहीं पाएगा। स्पष्ट रूप से यह यूआई ढांचे को करने के बजाय (डेस्ट्रॉय को कॉल करने के माध्यम से) स्क्रीन स्क्रीन अभिविन्यास बदलने के कार्यक्रम को स्वयं हल करके "हल" किया गया था)।

मैं प्रस्तुत करता हूं कि अगर अंतर्निहित समस्या यह है कि कार्यक्रम डेस्ट्रॉय () पर नहीं टिकेगा, तो स्वीकार्य समाधान केवल एक कामकाज है जो गंभीर अन्य समस्याओं और कमजोरियों के साथ कार्यक्रम छोड़ देता है। याद रखें कि एंड्रॉइड फ्रेमवर्क विशेष रूप से बताता है कि आपकी गतिविधि को आपके नियंत्रण से बाहर की परिस्थितियों के कारण लगभग किसी भी समय नष्ट होने का जोखिम है। इसलिए, आपकी गतिविधि किसी भी कारण से, केवल स्क्रीन अभिविन्यास परिवर्तन न केवल Destroy () और बाद में ऑनक्रेट () पर जीवित रहने में सक्षम होना चाहिए।

यदि आप हैंडलिंग स्क्रीन अभिविन्यास को स्वीकार करने जा रहे हैं तो ओपी की समस्या को हल करने के लिए स्वयं को बदल दें, आपको यह सत्यापित करने की आवश्यकता है कि ऑनस्ट्राय () के अन्य कारणों के परिणामस्वरूप एक ही त्रुटि नहीं होती है। क्या आप ऐसा करने में सक्षम हैं? यदि नहीं, तो मैं सवाल करूंगा कि "स्वीकृत" उत्तर वास्तव में बहुत अच्छा है या नहीं।


मेरा समाधान ProgressDialog क्लास को अपना खुद का MyProgressDialog प्राप्त करने के लिए MyProgressDialog
मैंने Dialog दिखाने से पहले अभिविन्यास को लॉक करने के लिए show() और dismiss() विधियों को फिर से परिभाषित किया और Dialog को खारिज करते समय इसे अनलॉक कर दिया। तो जब Dialog दिखाया जाता है और डिवाइस का अभिविन्यास बदलता है, तब तक स्क्रीन का अभिविन्यास तब तक रहता है जब तक कि dismiss() को कॉल नहीं किया जाता है, फिर सेंसर-मान / डिवाइस-अभिविन्यास के अनुसार स्क्रीन-ओरिएंटेशन परिवर्तन होता है।

मेरा कोड यहाँ है:

public class MyProgressDialog extends ProgressDialog {
private Context mContext;

public MyProgressDialog(Context context) {
    super(context);
    mContext = context;
}

public MyProgressDialog(Context context, int theme) {
    super(context, theme);
    mContext = context;
}

public void show() {
    if (mContext.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT)
        ((Activity) mContext).setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
    else
        ((Activity) mContext).setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
    super.show();
}

public void dismiss() {
    super.dismiss();
    ((Activity) mContext).setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR);
}

}

मैं इस घूर्णन मुद्दे को संभालने के लिए अपना दृष्टिकोण योगदान करने जा रहा हूं। यह ओपी के लिए प्रासंगिक नहीं हो सकता है क्योंकि वह AsyncTask का उपयोग नहीं कर AsyncTask , लेकिन शायद अन्य इसे उपयोगी पाएंगे। यह बहुत आसान है लेकिन ऐसा लगता है कि मेरे लिए नौकरी है:

मेरे पास BackgroundLoginTask लॉग इन AsyncTask नामक एक नेस्टेड AsyncTask क्लास के साथ एक लॉगिन गतिविधि है।

मेरे BackgroundLoginTask मैं ProgressDialog की बर्खास्तगी को कॉल करने पर एक शून्य जांच को छोड़कर साधारण से कुछ भी नहीं करता हूं:

@Override
protected void onPostExecute(Boolean result)
{    
if (pleaseWaitDialog != null)
            pleaseWaitDialog.dismiss();
[...]
}

यह उस मामले को संभालने के लिए है जहां पृष्ठभूमि कार्य समाप्त नहीं होता है, जबकि Activity दिखाई नहीं दे रही है और इसलिए, प्रगति संवाद को पहले से रोक दिया गया है onPause() विधि।

इसके बाद, मेरे मूल Activity वर्ग में, मैं अपने AsyncTask कक्षा और मेरे ProgressDialog ( AsyncTask , नेस्टेड होने पर, इन चरों तक पहुंच सकते हैं) पर वैश्विक स्थैतिक हैंडल बनाते हैं:

private static BackgroundLoginTask backgroundLoginTask;
private static ProgressDialog pleaseWaitDialog;

यह दो उद्देश्यों को पूरा करता है: सबसे पहले, यह मेरी Activity को हमेशा एक नई, पोस्ट- AsyncTask Activity से भी AsyncTask ऑब्जेक्ट तक पहुंचने की अनुमति देता है। दूसरा, यह मेरे BackgroundLoginTask को घुमाने के बाद भी ProgressDialog तक पहुंचने और खारिज करने की अनुमति देता है।

इसके बाद, मैं इसे onPause() जोड़ता हूं, जब हमारी Activity अग्रभूमि छोड़ रही है (उस बदसूरत "बल बंद" क्रैश को रोकती है onPause() प्रगति संवाद गायब हो जाता है:

    if (pleaseWaitDialog != null)
    pleaseWaitDialog.dismiss();

अंत में, मेरे पास मेरे onResume() विधि में निम्न है:

if ((backgroundLoginTask != null) && (backgroundLoginTask.getStatus() == Status.RUNNING))
        {
           if (pleaseWaitDialog != null)
             pleaseWaitDialog.show();
        }

यह Activity को फिर से बनाया जाने के बाद Dialog को फिर से प्रकट करने की अनुमति देता है।

यहां पूरी कक्षा है:

public class NSFkioskLoginActivity extends NSFkioskBaseActivity {
    private static BackgroundLoginTask backgroundLoginTask;
    private static ProgressDialog pleaseWaitDialog;
    private Controller cont;

    // This is the app entry point.
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        if (CredentialsAvailableAndValidated())
        {
        //Go to main menu and don't run rest of onCreate method.
            gotoMainMenu();
            return;
        }
        setContentView(R.layout.login);
        populateStoredCredentials();   
    }

    //Save current progress to options when app is leaving foreground
    @Override
    public void onPause()
    {
        super.onPause();
        saveCredentialsToPreferences(false);
        //Get rid of progress dialog in the event of a screen rotation. Prevents a crash.
        if (pleaseWaitDialog != null)
        pleaseWaitDialog.dismiss();
    }

    @Override
    public void onResume()
    {
        super.onResume();
        if ((backgroundLoginTask != null) && (backgroundLoginTask.getStatus() == Status.RUNNING))
        {
           if (pleaseWaitDialog != null)
             pleaseWaitDialog.show();
        }
    }

    /**
     * Go to main menu, finishing this activity
     */
    private void gotoMainMenu()
    {
        startActivity(new Intent(getApplicationContext(), NSFkioskMainMenuActivity.class));
        finish();
    }

    /**
     * 
     * @param setValidatedBooleanTrue If set true, method will set CREDS_HAVE_BEEN_VALIDATED to true in addition to saving username/password.
     */
    private void saveCredentialsToPreferences(boolean setValidatedBooleanTrue)
    {
        SharedPreferences settings = getSharedPreferences(APP_PREFERENCES, MODE_PRIVATE);
        SharedPreferences.Editor prefEditor = settings.edit();
        EditText usernameText = (EditText) findViewById(R.id.editTextUsername);
        EditText pswText = (EditText) findViewById(R.id.editTextPassword);
        prefEditor.putString(USERNAME, usernameText.getText().toString());
        prefEditor.putString(PASSWORD, pswText.getText().toString());
        if (setValidatedBooleanTrue)
        prefEditor.putBoolean(CREDS_HAVE_BEEN_VALIDATED, true);
        prefEditor.commit();
    }

    /**
     * Checks if user is already signed in
     */
    private boolean CredentialsAvailableAndValidated() {
        SharedPreferences settings = getSharedPreferences(APP_PREFERENCES,
                MODE_PRIVATE);
        if (settings.contains(USERNAME) && settings.contains(PASSWORD) && settings.getBoolean(CREDS_HAVE_BEEN_VALIDATED, false) == true)
         return true;   
        else
        return false;
    }

    //Populate stored credentials, if any available
    private void populateStoredCredentials()
    {
        SharedPreferences settings = getSharedPreferences(APP_PREFERENCES,
            MODE_PRIVATE);
        settings.getString(USERNAME, "");
       EditText usernameText = (EditText) findViewById(R.id.editTextUsername);
       usernameText.setText(settings.getString(USERNAME, ""));
       EditText pswText = (EditText) findViewById(R.id.editTextPassword);
       pswText.setText(settings.getString(PASSWORD, ""));
    }

    /**
     * Validate credentials in a seperate thread, displaying a progress circle in the meantime
     * If successful, save credentials in preferences and proceed to main menu activity
     * If not, display an error message
     */
    public void loginButtonClick(View view)
    {
        if (phoneIsOnline())
        {
        EditText usernameText = (EditText) findViewById(R.id.editTextUsername);
        EditText pswText = (EditText) findViewById(R.id.editTextPassword);
           //Call background task worker with username and password params
           backgroundLoginTask = new BackgroundLoginTask();
           backgroundLoginTask.execute(usernameText.getText().toString(), pswText.getText().toString());
        }
        else
        {
        //Display toast informing of no internet access
        String notOnlineMessage = getResources().getString(R.string.noNetworkAccessAvailable);
        Toast toast = Toast.makeText(getApplicationContext(), notOnlineMessage, Toast.LENGTH_SHORT);
        toast.show();
        }
    }

    /**
     * 
     * Takes two params: username and password
     *
     */
    public class BackgroundLoginTask extends AsyncTask<Object, String, Boolean>
    {       
       private Exception e = null;

       @Override
       protected void onPreExecute()
       {
           cont = Controller.getInstance();
           //Show progress dialog
           String pleaseWait = getResources().getString(R.string.pleaseWait);
           String commWithServer = getResources().getString(R.string.communicatingWithServer);
            if (pleaseWaitDialog == null)
              pleaseWaitDialog= ProgressDialog.show(NSFkioskLoginActivity.this, pleaseWait, commWithServer, true);

       }

        @Override
        protected Boolean doInBackground(Object... params)
        {
        try {
            //Returns true if credentials were valid. False if not. Exception if server could not be reached.
            return cont.validateCredentials((String)params[0], (String)params[1]);
        } catch (Exception e) {
            this.e=e;
            return false;
        }
        }

        /**
         * result is passed from doInBackground. Indicates whether credentials were validated.
         */
        @Override
        protected void onPostExecute(Boolean result)
        {
        //Hide progress dialog and handle exceptions
        //Progress dialog may be null if rotation has been switched
        if (pleaseWaitDialog != null)
             {
            pleaseWaitDialog.dismiss();
                pleaseWaitDialog = null;
             }

        if (e != null)
        {
         //Show toast with exception text
                String networkError = getResources().getString(R.string.serverErrorException);
                Toast toast = Toast.makeText(getApplicationContext(), networkError, Toast.LENGTH_SHORT);
            toast.show();
        }
        else
        {
            if (result == true)
            {
            saveCredentialsToPreferences(true);
            gotoMainMenu();
            }
            else
            {
            String toastText = getResources().getString(R.string.invalidCredentialsEntered);
                Toast toast = Toast.makeText(getApplicationContext(), toastText, Toast.LENGTH_SHORT);
            toast.show();
            } 
        }
        }

    }
}

मैं किसी भी तरह से अनुभवी एंड्रॉइड डेवलपर नहीं हूं, इसलिए टिप्पणी करने में संकोच न करें।


मैं एक ही समस्या से मुलाकात की। मेरी गतिविधि को यूआरएल से कुछ डेटा पर्स करने की जरूरत है और यह धीमा है। तो मैं ऐसा करने के लिए एक धागा बनाते हैं तो एक प्रगतिशील दिखाओ। जब मैं समाप्त हो जाता हूं तो मैंने थ्रेड को हैंडलर के माध्यम से यूआई थ्रेड पर एक संदेश पोस्ट करने दिया। हैंडलर.handleMessage में, मुझे डेटा ऑब्जेक्ट (अब तैयार) थ्रेड से मिलता है और इसे यूआई में पॉप्युलेट करता है। तो यह आपके उदाहरण के समान ही है।

कई परीक्षण और त्रुटि के बाद ऐसा लगता है कि मुझे समाधान मिला। कम से कम अब मैं थ्रेड के पहले या बाद में, किसी भी पल में स्क्रीन घुमा सकता हूं। सभी परीक्षणों में, संवाद ठीक से बंद हो जाता है और सभी व्यवहार अपेक्षित होते हैं।

मैंने जो किया वह नीचे दिखाया गया है। लक्ष्य मेरा डेटा मॉडल (mDataObject) भरना है और फिर इसे UI पर पॉप्युलेट करना है। आश्चर्य के बिना किसी भी पल में स्क्रीन रोटेशन की अनुमति देनी चाहिए।

class MyActivity {

private MyDataObject mDataObject = null;
private static MyThread mParserThread = null; // static, or make it singleton

OnCreate() {
        ...
        Object retained = this.getLastNonConfigurationInstance();
        if(retained != null) {
            // data is already completely obtained before config change
            // by my previous self.
            // no need to create thread or show dialog at all
            mDataObject = (MyDataObject) retained;
            populateUI();
        } else if(mParserThread != null && mParserThread.isAlive()){
            // note: mParserThread is a static member or singleton object.
            // config changed during parsing in previous instance. swap handler
            // then wait for it to finish.
            mParserThread.setHandler(new MyHandler());
        } else {
            // no data and no thread. likely initial run
            // create thread, show dialog
            mParserThread = new MyThread(..., new MyHandler());
            mParserThread.start();
            showDialog(DIALOG_PROGRESS);
        }
}

// http://android-developers.blogspot.com/2009/02/faster-screen-orientation-change.html
public Object onRetainNonConfigurationInstance() {
        // my future self can get this without re-downloading
        // if it's already ready.
        return mDataObject;
}

// use Activity.showDialog instead of ProgressDialog.show
// so the dialog can be automatically managed across config change
@Override
protected Dialog onCreateDialog(int id) {
    // show progress dialog here
}

// inner class of MyActivity
private class MyHandler extends Handler {
    public void handleMessage(msg) {
        mDataObject = mParserThread.getDataObject();
        populateUI();
        dismissDialog(DIALOG_PROGRESS);
    }
}
}

class MyThread extends Thread {
    Handler mHandler;
    MyDataObject mDataObject;

    public MyHandler(..., Handler h) {...; mHandler = h;} // constructor with handler param
    public void setHandler(Handler h) { mHandler = h; } // for handler swapping after config change
    public MyDataObject getDataObject() { return mDataObject; } // return data object (completed) to caller
    public void run() {
        mDataObject = new MyDataObject();
        // do the lengthy task to fill mDataObject with data
        lengthyTask(mDataObject);
        // done. notify activity
        mHandler.sendEmptyMessage(0); // tell activity: i'm ready. come pick up the data.
    }
}

यही मेरे लिए काम करता है। मुझे नहीं पता कि यह एंड्रॉइड द्वारा डिज़ाइन की गई "सही" विधि है - वे दावा करते हैं कि यह "स्क्रीन रोटेशन के दौरान गतिविधि को नष्ट / पुन: सक्रिय करें" वास्तव में चीजों को आसान बनाता है, इसलिए मुझे लगता है कि यह बहुत मुश्किल नहीं होना चाहिए।

अगर आपको मेरे कोड में कोई समस्या दिखाई दे तो मुझे बताएं। जैसा ऊपर बताया गया है, मुझे वास्तव में पता नहीं है कि कोई दुष्प्रभाव है या नहीं।


मैंने इसे ऐसा किया है:

    package com.palewar;
    import android.app.Activity;
    import android.app.ProgressDialog;
    import android.os.Bundle;
    import android.os.Handler;
    import android.os.Message;

    public class ThreadActivity extends Activity {


        static ProgressDialog dialog;
        private Thread downloadThread;
        final static Handler handler = new Handler() {

            @Override
            public void handleMessage(Message msg) {

                super.handleMessage(msg);

                dialog.dismiss();

            }

        };

        protected void onDestroy() {
    super.onDestroy();
            if (dialog != null && dialog.isShowing()) {
                dialog.dismiss();
                dialog = null;
            }

        }

        /** Called when the activity is first created. */
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.main);

            downloadThread = (Thread) getLastNonConfigurationInstance();
            if (downloadThread != null && downloadThread.isAlive()) {
                dialog = ProgressDialog.show(ThreadActivity.this, "",
                        "Signing in...", false);
            }

            dialog = ProgressDialog.show(ThreadActivity.this, "",
                    "Signing in ...", false);

            downloadThread = new MyThread();
            downloadThread.start();
            // processThread();
        }

        // Save the thread
        @Override
        public Object onRetainNonConfigurationInstance() {
            return downloadThread;
        }


        static public class MyThread extends Thread {
            @Override
            public void run() {

                try {
                    // Simulate a slow network
                    try {
                        new Thread().sleep(5000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    handler.sendEmptyMessage(0);

                } finally {

                }
            }
        }

    }

आप यह भी कोशिश कर सकते हैं और मुझे बता सकते हैं कि यह आपके लिए काम करता है या नहीं


लंबे कार्य को एक अलग वर्ग में ले जाएं। इसे एक विषय-पर्यवेक्षक पैटर्न के रूप में कार्यान्वित करें। जब भी गतिविधि बनाई जाती है और कार्य वर्ग के साथ पंजीकरण रद्द करने के दौरान। कार्य वर्ग AsyncTask का उपयोग कर सकते हैं।


I am a fresher in android and I tried this and it's worked.

public class loadTotalMemberByBranch extends AsyncTask<Void, Void,Void> {
        ProgressDialog progressDialog = new ProgressDialog(Login.this);
        int ranSucess=0;
        @Override
        protected void onPreExecute() {
            // TODO Auto-generated method stub
            super.onPreExecute();
            progressDialog.setTitle("");    
            progressDialog.isIndeterminate();
            progressDialog.setCancelable(false);
            progressDialog.show();
            setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_NOSENSOR);

        }
        @Override
        protected Void doInBackground(Void... params) {
            // TODO Auto-generated method stub

            return null;
        }
        @Override
        protected void onPostExecute(Void result) {
            // TODO Auto-generated method stub
            super.onPostExecute(result);
            progressDialog.dismiss();
            setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_NOSENSOR);
        }
}

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'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. Eg: "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.


If you create a background Service that does all the heavy lifting (tcp requests/response, unmarshalling), the View and Activity can be destroyed and re-created without leaking window or losing data. This allows the Android recommended behavior, which is to destroy an Activity on each configuration change (eg. for each orientation change).

It is a bit more complex, but it is the best way for invoking server request, data pre/post-processing, etc.

You may even use your Service to queue each request to a server, so it makes it easy and efficient to handle those things.

The dev guide has a full chapter on Services .


If you're struggling with detecting orientation change events of a dialog INDEPENDENT OF AN ACTIVITY REFERENCE , this method works excitingly well. I use this because I have my own dialog class that can be shown in multiple different Activities so I don't always know which Activity it's being shown in. With this method you don't need to change the AndroidManifest, worry about Activity references, and you don't need a custom dialog (as I have). You do need, however, a custom content view so you can detect the orientation changes using that particular view. मेरा उदाहरण यहां दिया गया है:

Setup

public class MyContentView extends View{
    public MyContentView(Context context){
        super(context);
    }

    @Override
    public void onConfigurationChanged(Configuration newConfig){
        super.onConfigurationChanged(newConfig);

        //DO SOMETHING HERE!! :D
    }
}

Implementation 1 - Dialog

Dialog dialog = new Dialog(context);
//set up dialog
dialog.setContentView(new MyContentView(context));
dialog.show();

Implementation 2 - AlertDialog.Builder

AlertDialog.Builder builder = new AlertDialog.Builder(context);
//set up dialog builder
builder.setView(new MyContentView(context));        //Can use this method
builder.setCustomTitle(new MycontentView(context)); // or this method
builder.build().show();

Implementation 3 - ProgressDialog / AlertDialog

ProgressDialog progress = new ProgressDialog(context);
//set up progress dialog
progress.setView(new MyContentView(context));        //Can use this method
progress.setCustomTitle(new MyContentView(context)); // or this method
progress.show();

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)


These days there is a much more distinct way to handle these types of issues. The typical approach is:

1. Ensure your data is properly seperated from the UI:

Anything that is a background process should be in a retained Fragment (set this with Fragment.setRetainInstance() . This becomes your 'persistent data storage' where anything data based that you would like retained is kept. After the orientation change event, this Fragment will still be accessible in its original state through a FragmentManager.findFragmentByTag() call (when you create it you should give it a tag not an ID as it is not attached to a View ).

See the Handling Runtime Changes developed guide for information about doing this correctly and why it is the best option.

2. Ensure you are interfacing correctly and safely between the background processs and your UI:

You must reverse your linking process. At the moment your background process attaches itself to a View - instead your View should be attaching itself to the background process. It makes more sense right? The View 's action is dependent on the background process, whereas the background process is not dependent on the View .This means changing the link to a standard Listener interface. Say your process (whatever class it is - whether it is an AsyncTask , Runnable or whatever) defines a OnProcessFinishedListener , when the process is done it should call that listener if it exists.

This answer is a nice concise description of how to do custom listeners.

3. Link your UI into the data process whenever the UI is created (including orientation changes):

Now you must worry about interfacing the background task with whatever your current View structure is. If you are handling your orientation changes properly (not the configChanges hack people always recommend), then your Dialog will be recreated by the system. This is important, it means that on the orientation change, all your Dialog 's lifecycle methods are recalled. So in any of these methods ( onCreateDialog is usually a good place), you could do a call like the following:

DataFragment f = getActivity().getFragmentManager().findFragmentByTag("BACKGROUND_TAG");
if (f != null) {
    f.mBackgroundProcess.setOnProcessFinishedListener(new OnProcessFinishedListener() {
        public void onProcessFinished() {
            dismiss();
        }
    });
 }

See the Fragment lifecycle for deciding where setting the listener best fits in your individual implementation.

This is a general approach to providing a robust and complete solution to the generic problem asked in this question. There is probably a few minor pieces missing in this answer depending on your individual scenario, but this is generally the most correct approach for properly handling orientation change events.


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() .


Tried to implement jfelectron 's solution because it is a " rock-solid solution to these issues that conforms with the 'Android Way' of things " but it took some time to look up and put together all the elements mentioned. Ended up with this slightly different, and I think more elegant, solution posted here in it's entirety.

Uses an IntentService fired from an activity to perform the long running task on a separate thread. The service fires back sticky Broadcast Intents to the activity which update the dialog. The Activity uses showDialog(), onCreateDialog() and onPrepareDialog() to eliminate the need to have persistent data passed in the application object or the savedInstanceState bundle. This should work no matter how your application is interrupted.

Activity Class:

public class TesterActivity extends Activity {
private ProgressDialog mProgressDialog;
private static final int PROGRESS_DIALOG = 0;

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    Button b = (Button) this.findViewById(R.id.test_button);
    b.setOnClickListener(new OnClickListener() {
        public void onClick(View v) {
            buttonClick();
        }
    });
}

private void buttonClick(){
    clearPriorBroadcast();
    showDialog(PROGRESS_DIALOG);
    Intent svc = new Intent(this, MyService.class);
    startService(svc);
}

protected Dialog onCreateDialog(int id) {
    switch(id) {
    case PROGRESS_DIALOG:
        mProgressDialog = new ProgressDialog(TesterActivity.this);
        mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
        mProgressDialog.setMax(MyService.MAX_COUNTER);
        mProgressDialog.setMessage("Processing...");
        return mProgressDialog;
    default:
        return null;
    }
}

@Override
protected void onPrepareDialog(int id, Dialog dialog) {
    switch(id) {
    case PROGRESS_DIALOG:
        // setup a broadcast receiver to receive update events from the long running process
        IntentFilter filter = new IntentFilter();
        filter.addAction(MyService.BG_PROCESS_INTENT);
        registerReceiver(new MyBroadcastReceiver(), filter);
        break;
    }
}

public class MyBroadcastReceiver extends BroadcastReceiver{
    @Override
    public void onReceive(Context context, Intent intent) {
        if (intent.hasExtra(MyService.KEY_COUNTER)){
            int count = intent.getIntExtra(MyService.KEY_COUNTER, 0);
            mProgressDialog.setProgress(count);
            if (count >= MyService.MAX_COUNTER){
                dismissDialog(PROGRESS_DIALOG);
            }
        }
    }
}

/*
 * Sticky broadcasts persist and any prior broadcast will trigger in the 
 * broadcast receiver as soon as it is registered.
 * To clear any prior broadcast this code sends a blank broadcast to clear 
 * the last sticky broadcast.
 * This broadcast has no extras it will be ignored in the broadcast receiver 
 * setup in onPrepareDialog()
 */
private void clearPriorBroadcast(){
    Intent broadcastIntent = new Intent();
    broadcastIntent.setAction(MyService.BG_PROCESS_INTENT);
    sendStickyBroadcast(broadcastIntent);
}}

IntentService Class:

public class MyService extends IntentService {

public static final String BG_PROCESS_INTENT = "com.mindspiker.Tester.MyService.TEST";
public static final String KEY_COUNTER = "counter";
public static final int MAX_COUNTER = 100;

public MyService() {
  super("");
}

@Override
protected void onHandleIntent(Intent intent) {
    for (int i = 0; i <= MAX_COUNTER; i++) {
        Log.e("Service Example", " " + i);
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        Intent broadcastIntent = new Intent();
        broadcastIntent.setAction(BG_PROCESS_INTENT);
        broadcastIntent.putExtra(KEY_COUNTER, i);
        sendStickyBroadcast(broadcastIntent);
    }
}}

Manifest file entries:

before application section:

uses-permission android:name="com.mindspiker.Tester.MyService.TEST"
uses-permission android:name="android.permission.BROADCAST_STICKY"

inside application section

service android:name=".MyService"

i have found and easier solution to handle threads when orientation change. You can just keep an static reference to your activity/fragment and verify if its null before acting on the ui. I suggest using a try catch too:

 public class DashListFragment extends Fragment {
     private static DashListFragment ACTIVE_INSTANCE;

     @Override
     public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        ACTIVE_INSTANCE = this;

        new Handler().postDelayed(new Runnable() {
            public void run() {
                try {
                        if (ACTIVE_INSTANCE != null) {
                            setAdapter(); // this method do something on ui or use context
                        }
                }
                catch (Exception e) {}


            }
        }, 1500l);

    }

    @Override
    public void onDestroy() {
        super.onDestroy();

        ACTIVE_INSTANCE = null;
    }


}




android-dialog