प्रगति संवाद और पृष्ठभूमि थ्रेड सक्रिय होने पर स्क्रीन ओरिएंटेशन परिवर्तन को कैसे संभालें?


524

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

मैं स्क्रीन ओरिएंटेशन को इनायत से कैसे बदल सकता हूं?

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

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 com.android.internal.policy.impl.PhoneWindow$DecorView@433b7150 that was originally added here
E/WindowManager(  244): android.view.WindowLeaked: Activity MyAct has leaked window com.android.internal.policy.impl.PhoneWindow$DecorView@433b7150 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)

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


1
आपके द्वारा प्राप्त किए गए उत्तरों को ध्यान में रखते हुए, आपको स्वीकार किए गए उत्तर को सर्वोत्तम के पक्ष में बदलना चाहिए, क्या आपने नहीं किया?
आरडीएस

यह भी देखें एक पूर्व प्रश्न stackoverflow.com/questions/456211/...
आरडीएस

3
सब, इस समस्या के लिए एक बहुत ही बढ़िया व्याख्या और संभव समाधान मिला है। Http://blog.doityourselfandroid.com/2010/11/14/handling-progress-dialogs-and-screen-orientation-changes/ Lemme के माध्यम से जाना अगर यह मदद की।
अर्कमाक्स

2
इस ब्लॉग पोस्ट में स्क्रीन ओरिएंटेशन में अतुल्यकालिक पृष्ठभूमि कार्यों को बनाए रखने के तरीके पर एक पूर्ण विवरण है । इसकी जांच - पड़ताल करें!
एड्रियन मोंक

बस Android सेट करें: configChanges = "अभिविन्यास | स्क्रीनाइज करें" को गतिविधि में प्रदर्शित करें। यह अपनी गतिविधि को फिर से बनाने के लिए android को रोक देगा
Jawad Zeb

जवाबों:


155

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

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


14
आपने दुर्घटना के कारण को इंगित किया हो सकता है। मुझे दुर्घटना से छुटकारा मिल गया, लेकिन मैंने अभी भी यह नहीं सोचा है कि एक विश्वसनीय तरीके से अभिविन्यास परिवर्तन से पहले यूआई को कैसे पुनर्स्थापित किया जाए। लेकिन आपके उत्तर ने मुझे आगे बढ़ाया, इसलिए इसे उत्तर के रूप में पुरस्कृत किया।
हिक्की टोईवोनेन

4
जब ओरिएंटेशन बदलता है, तो आपको अपनी गतिविधि में ऑनस्टार्ट मिलना चाहिए। अनिवार्य रूप से, आपको पुराने डेटा का उपयोग करके दृश्य को फिर से कॉन्फ़िगर करना होगा। तो मैं सुझाव दूंगा कि संख्यात्मक स्थिति को प्रगति पट्टी से udpates करने और एक नए दृश्य के पुनर्निर्माण के लिए जब आपको वह नया 'onStart' मिले तो मैं याद नहीं रख सकता अगर आपको एक नई गतिविधि मिलती है, लेकिन प्रलेखन के माध्यम से कुछ शिकार करने में मदद करनी चाहिए।
15:31 पर हैसमैन जूल

6
हाल ही में इसके साथ खेलने के बाद, मैं इस पर गुजर सकता हूं कि जब आपका ऐप ओरिएंटेशन बदलता है तो आपको एक नई गतिविधि मिलती है। (आपको एक नया दृश्य भी मिलता है) यदि आप पुराने दृश्य को अपडेट करने का प्रयास करते हैं तो आपको एक अपवाद मिलेगा क्योंकि पुराने दृश्य में एक अमान्य एप्लिकेशन संदर्भ (आपकी पुरानी गतिविधि) है। आप इसे myActivity.getApplicationContext () में पास करके प्राप्त कर सकते हैं गतिविधि के लिए एक संकेतक के बजाय।
हैसमैन जूल

1
क्या कोई इस संदर्भ में अस्थिरता के उपयोग / लाभ की व्याख्या कर सकता है
जावेद ज़ेब

2
@Nepster हाँ, मैं भी उस के बारे में सोच रहा था। यह बहुत अच्छा होगा अगर किसी ने अस्थिर के बारे में समझाया।
रेस्टइन्पीस

261

संपादित करें: Google इंजीनियर इस दृष्टिकोण की अनुशंसा नहीं करते हैं, जैसा कि स्टैकऑवरफ्लो पोस्ट में डायने हैकॉर्न (उर्फ हैकबॉड ) द्वारा वर्णित है । की जाँच करें इस ब्लॉग पोस्ट में अधिक जानकारी के लिए।


आपको इसे घोषणा में गतिविधि घोषणा में जोड़ना होगा:

android:configChanges="orientation|screenSize"

तो ऐसा लगता है

<activity android:label="@string/app_name" 
        android:configChanges="orientation|screenSize|keyboardHidden" 
        android:name=".your.package">

मामला यह है कि सिस्टम कॉन्फ़िगरेशन में परिवर्तन होने पर गतिविधि को नष्ट कर देता है। कॉन्फ़िगरेशन देखें ।

इसलिए विन्यास फाइल में डालने से सिस्टम आपकी गतिविधि को नष्ट करने से बचता है। इसके बजाय यह onConfigurationChanged(Configuration)विधि को आमंत्रित करता है।


21
यह निश्चित रूप से सबसे अच्छा समाधान है; के रूप में यह बस लेआउट घुमाता है (व्यवहार आप पहले स्थान पर उम्मीद)। बस Android डाल करने के लिए सुनिश्चित करें: configChanges = "अभिविन्यास | कीबोर्डहेड" (फोन की वजह से कि लैंडस्केप कीबोर्ड है)
nikib3ro

24
यह मेरे द्वारा अपेक्षित व्यवहार प्रतीत होता है। हालांकि, प्रलेखन इंगित करता है कि गतिविधि "नष्ट हो गई है" क्योंकि लेआउट फ़ाइलों सहित कोई भी एप्लिकेशन संसाधन, किसी भी कॉन्फ़िगरेशन मान के आधार पर बदल सकता है। इस प्रकार कॉन्फ़िगरेशन परिवर्तन को संभालने का एकमात्र सुरक्षित तरीका सभी संसाधनों को फिर से प्राप्त करना है "। और इसके अलावा orientation, कॉन्फ़िगरेशन बदलने के लिए कई और कारण हैं: keyboardHidden(मैंने पहले से ही विकी उत्तर संपादित किया है), uiMode(उदाहरण के लिए, कार मोड में या बाहर जाना; रात मोड बदलना), आदि। मुझे आश्चर्य है कि अब यह वास्तव में है एक अच्छा जवाब।
rds

116
यह स्वीकार्य समाधान नहीं है। यह सिर्फ सच्चे मुद्दे पर काम करता है।
rf43

18
काम करता है, लेकिन Google द्वारा अनुशंसित नहीं है।
एड बर्नेट

21
कृपया यहाँ इस दृष्टिकोण का पालन न करें। DDosAttack पूरी तरह से सही है। कल्पना कीजिए कि आप एक डाउनलोड या कुछ और के लिए एक प्रगति संवाद बना रहे हैं जिसमें एक लंबा समय लगता है। एक उपयोगकर्ता के रूप में आप उस गतिविधि पर नहीं टिकेंगे और उसे घूरते रहेंगे। आप होम स्क्रीन या किसी अन्य ऐप जैसे गेम या फोन कॉल पर स्विच कर सकते हैं या कुछ और संसाधन भूखा हो सकता है जो अंततः आपकी गतिविधि को नष्ट कर देगा। और फिर क्या? आप उसी पुराने मुद्दे का सामना कर रहे हैं, जो उस छोटी सी छोटी चाल से हल नहीं हुआ है। उपयोगकर्ता के वापस आने पर गतिविधि फिर से पूरी हो जाएगी।
टिगुची

68

मैं इन मुद्दों के लिए एक ठोस-ठोस समाधान के साथ आया था जो चीजों के 'एंड्रॉइड वे' के अनुरूप है। IntentService पैटर्न का उपयोग करके मेरे सभी लंबे समय तक चलने वाले ऑपरेशन हैं।

यही है, मेरी गतिविधियाँ इंटेंट को प्रसारित करती हैं, इंटेंट सर्विस काम करती है, डीबी में डेटा को सेव करती है और फिर स्टिकी इंटेंट को प्रसारित करती है । चिपचिपा हिस्सा महत्वपूर्ण है, जैसे कि भले ही उपयोगकर्ता द्वारा काम शुरू करने के बाद समय के दौरान गतिविधि रोक दी गई थी और इंटेंटसेवर से प्रसारित वास्तविक समय को याद करता है, हम अभी भी कॉल कर सकते हैं और कॉलिंग गतिविधि से डेटा उठा सकते हैं। ProgressDialogs इस पैटर्न के साथ काफी अच्छी तरह से काम कर सकता है onSaveInstanceState()

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

अनिश्चितकालीन प्रगति संवादों के लिए यह आसान है। प्रगति बार शैली के लिए, आपको बंडल में अंतिम ज्ञात प्रगति और प्रगति की जानकारी रखने के लिए गतिविधि में स्थानीय रूप से उपयोग की जाने वाली जानकारी को रखना होगा। प्रगति को पुनर्स्थापित करने पर, आप इस जानकारी का उपयोग उसी स्थिति में प्रगति पट्टी को पहले की तरह फिर से करने के लिए करेंगे और फिर चीजों की वर्तमान स्थिति के आधार पर अपडेट करेंगे।

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

public void doSignIn(View view) {
    waiting=true;
    AppClass app=(AppClass) getApplication();
    String logingon=getString(R.string.signon);
    app.Dialog=new WeakReference<ProgressDialog>(ProgressDialog.show(AddAccount.this, "", logingon, true));
    ...
}

@Override
protected void onSaveInstanceState(Bundle saveState) {
    super.onSaveInstanceState(saveState);
    saveState.putBoolean("waiting",waiting);
}

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    if(savedInstanceState!=null) {
        restoreProgress(savedInstanceState);    
    }
    ...
}

private void restoreProgress(Bundle savedInstanceState) {
    waiting=savedInstanceState.getBoolean("waiting");
    if (waiting) {
        AppClass app=(AppClass) getApplication();
        ProgressDialog refresher=(ProgressDialog) app.Dialog.get();
        refresher.dismiss();
        String logingon=getString(R.string.signon);
        app.Dialog=new WeakReference<ProgressDialog>(ProgressDialog.show(AddAccount.this, "", logingon, true));
    }
}

एक अच्छा समाधान लगता है
डेरेकी

"मेरे पास IntentService पैटर्न का उपयोग करते हुए मेरे सभी लंबे समय तक चलने वाले ऑपरेशन हैं।" यह एक सही समाधान नहीं है क्योंकि यह तोप से बाहर गौरैयों और बहुत सारे बॉयलरप्लेट कोड में शूट करना पसंद करता है, अधिक के लिए आप youtube.com/watch?v=NJsq0TU0qeg
कामिल नेकानोवेज़

28

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

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

मैंने जो किया वह नीचे दिखाया गया है। लक्ष्य मेरे डेटा मॉडल को भरना है ( 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;

    // constructor with handler param
    public MyHandler(..., Handler h) {
        ...
        mHandler = h;
    }

    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.
    }
}

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

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


1
आपका बहुत बहुत धन्यवाद! संकेत onRetainNonConfigurationInstance()और getLastNonConfigurationInstance()मेरी समस्या को हल करने में मेरी मदद की। थम्स अप!
स्वेन

15

मूल कथित समस्या यह थी कि कोड स्क्रीन ओरिएंटेशन परिवर्तन से नहीं बचेगा। जाहिरा तौर पर यह "हल" था कार्यक्रम को संभालने के द्वारा स्क्रीन अभिविन्यास परिवर्तन स्वयं, यूआई फ्रेमवर्क को ऐसा करने के बजाय (onDestroy को कॉल करके)।

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

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


14

मेरा समाधान यह था कि ProgressDialogअपना खुद का पाने के लिए कक्षा का विस्तार किया जाए MyProgressDialog
मैंने रीडिफाइंड show()और dismiss()तरीकों को दिखाने से पहले ओरिएंटेशन को लॉक करने Dialogऔर इसे 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);
}

}

8

मुझे इस समस्या का सामना करना पड़ा, और मैं एक समाधान के साथ आया जो कि ProgressDialog का उपयोग नहीं किया और मुझे तेजी से परिणाम मिले।

मैंने जो भी किया वह एक ऐसा लेआउट था, जिसमें एक प्रोग्रेसबार है।

<?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>

फिर onCreate मेथड में निम्नलिखित कार्य करें

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

फिर लंबे कार्य को एक थ्रेड में करें, और जब वह पूरा हो जाए तो एक रननेबल को उस वास्तविक लेआउट में सामग्री दृश्य सेट करें जिसे आप इस गतिविधि के लिए उपयोग करना चाहते हैं।

उदाहरण के लिए:

mHandler.post(new Runnable(){

public void run() {
        setContentView(R.layout.my_layout);
    } 
});

यह वही है जो मैंने किया था, और मैंने पाया है कि यह प्रोग्रेसडायलॉग दिखाने की तुलना में तेजी से चलता है और यह कम दखल देता है और मेरी राय में बेहतर है।

हालाँकि, यदि आप ProgressDialog का उपयोग करना चाहते हैं, तो यह उत्तर आपके लिए नहीं है।


यह समाधान एक साधारण उपयोग के मामले में सुरुचिपूर्ण है, लेकिन इसमें कमियां हैं। आपको पूर्ण सामग्री दृश्य के पुनर्निर्माण की आवश्यकता है। setContentView(R.layout.my_layout);काफी नहीं है; आपको सभी श्रोताओं को सेट करने की आवश्यकता है, डेटा रीसेट करें, आदि
rds

@ आप सही हैं। यह वास्तव में केवल एक साधारण मामले का समाधान है, या यदि आपको अपना दृष्टिकोण प्रदर्शित करने से पहले अपने ऑनक्रिट विधि में कुछ भारी उठाने की आवश्यकता है।
पजानो

मैं काफी नहीं मिलता। ऑनक्रीट () में सेटिंग श्रोताओं के बजाय, जैसा कि हम आम तौर पर करते हैं, हम उन्हें रन () में सेट कर सकते हैं। क्या मुझसे कोई चूक हो रही है?
कोड कवि

7

मैंने इसका एक हल खोजा है जो मैंने अभी तक कहीं और नहीं देखा है। आप कस्टम अनुप्रयोग ऑब्जेक्ट का उपयोग कर सकते हैं जो जानता है कि क्या आपके पास पृष्ठभूमि के कार्य हैं, जो कि गतिविधि में ऐसा करने की कोशिश करने के बजाय नष्ट हो जाते हैं और अभिविन्यास परिवर्तन पर पुनः निर्मित होते हैं। मैंने यहाँ इस बारे में ब्लॉग किया है


1
कस्टम बनाने के Applicationलिए आम तौर पर एक वैश्विक अनुप्रयोग स्थिति बनाए रखने के लिए उपयोग किया जाता है। मैं यह नहीं कहता कि यह काम नहीं करता है, लेकिन यह अधिक जटिल लगता है। डॉक्टर से "आम तौर पर उपवर्ग आवेदन करने की कोई आवश्यकता नहीं है।" मैं सोनक्सुरेक्सो के जवाब को काफी पसंद करता हूं।
आरडीएस

7

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

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

अपने में 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एक नई, पोस्ट-रोटेटेड गतिविधि से भी ऑब्जेक्ट तक पहुंचने की अनुमति देता है । दूसरा, यह मुझे घुमाने के बाद भी BackgroundLoginTaskउपयोग करने और खारिज करने की अनुमति देता है ProgressDialog

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

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

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

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

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

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

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();
            } 
        }
        }

    }
}

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


1
दिलचस्प! विशेष रूप से हम में से उन लोगों के लिए जो AsyncTask का उपयोग कर रहे हैं। बस अपने समाधान की कोशिश की, और यह ज्यादातर काम करने लगता है। वहाँ एक समस्या है: ProgressDialog एक रोटेशन के बाद थोड़ा जल्दी समाप्त करने के लिए लगता है कि जब ProgressDialog अभी भी सक्रिय है। मैं देखने जा रहा हूं कि वास्तव में क्या हो रहा है और इसे कैसे ठीक किया जाए। लेकिन मैं अब उन दुर्घटनाओं हो रही है!
स्कॉट बिग्स

1
एक तय पाया। ऐसा लगता है कि यहाँ समस्या स्थैतिक ProgressDialog है। जब घूर्णन ProgressDialog को बाधित करते हैं, तो यह कभी-कभी नई गतिविधि में फिर से शुरू होने के बाद इसका .dismiss () विधि कहलाता है। प्रत्येक गतिविधि के साथ बनाए गए ProgressDialog को बनाते हुए, हम यह सुनिश्चित करते हैं कि यह नया ProgressDialog पुरानी गतिविधि के साथ नहीं मारा गया है। मैंने यह भी सुनिश्चित किया कि जब भी खारिज किया जाए (कचरा संग्रहण में सहायता के लिए) प्रोग्रेसडायलॉग को बंद कर दिया जाए। तो हमारे पास यहाँ एक समाधान है! AsyncTask का उपयोग करने वालों के लिए चीयर्स!
स्कॉट बिग्स

4

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


1
मैं नहीं देखता कि यह कैसे मदद करेगा। क्या आप अधिक विस्तार से बता सकते हैं कि यह उन समस्याओं को कैसे रोकता है जो मैं देख रहा हूं।
हिक्की टिवोनेन

1
जैसा कि हैसमैन ने कहा कि यह बैकएंड को यूआई तत्वों तक पहुंचने से रोकता है और हम यूआई को बैकएंड से अलग कर सकते हैं, बैकएंड सेपरेट थ्रेड में चलता है और स्टेटस अपडेट के लिए बैकएंड कार्य के साथ स्क्रीन के फिर से उन्मुख होने और रजिस्टर-UnRegister के बाद भी यह चलता रहता है। । इसका उपयोग करके मैंने जो वास्तविक उदाहरण हल किया है, वह यह है कि मेरे पास एक डाउनलोड टास्क है, मैंने इसे एक अलग थ्रेड में स्थानांतरित कर दिया है, जब भी थ्रेड बनाया जाता है मैं इसके साथ पंजीकृत-अपंजीकृत करता हूं।
विनय

ठीक है, मैं इस मुद्दे पर फिर से विचार कर रहा हूं और मुझे नहीं लगता कि मैं अभी भी इस उत्तर को पूरी तरह से समझ पाया हूं। मान लें कि हमारे पास एक मुख्य गतिविधि है जो एक लंबे समय से चल रहे नेटवर्क ऑपरेशन को करने के लिए एक AsyncTask शुरू करती है जिसे हम स्क्रीन ओरिएंटेशन परिवर्तन के दौरान बाधित नहीं करना चाहते हैं। मैं यह नहीं देखता कि नई गतिविधि पुरानी गतिविधि द्वारा शुरू किए गए AsyncTask पर एक संदेश कैसे भेज सकती है। क्या आप एक कोड उदाहरण दे सकते हैं?
हीकी टिवोनेन

@ हिक्की, क्या मेरा मतलब यह है कि आपका क्या मतलब है?
चुकंदर

4

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

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;
        }       
    }
}

4

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

    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 {

                }
            }
        }

    }

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


OnDestroy कोड को डेवलपर के पेज से बिल्कुल भी निष्पादित नहीं किया जा सकता है : "उपरोक्त तालिका में" किल करने योग्य "कॉलम को नोट करें - उन तरीकों के लिए जिन्हें मारने योग्य के रूप में चिह्नित किया गया है, उस विधि के बाद प्रक्रिया को होस्ट करता है जो गतिविधि की मेजबानी करने की प्रक्रिया देता है। किसी भी समय अपने कोड की एक और लाइन को निष्पादित किए बिना सिस्टम "
ilomambo

मेरा दृढ़ विश्वास है कि "onRetainNonConfigurationInstance ()" इस तरह के मामलों के लिए इस्तेमाल की जाने वाली विधि है ... nyc work
Nitin Bansal

मैं भी इसी तरह की समस्या का सामना कर रहा हूं, क्योंकि सचिन गुरनानी अपनी समस्या को ठीक करने के लिए स्थैतिक घोषणा का उपयोग करते हैं। stackoverflow.com/questions/12058774/…
स्टीवन डू

2

यदि आप एक ऐसी पृष्ठभूमि बनाते हैं जो Serviceसभी भारी उठाने (टीसीपी अनुरोधों / प्रतिक्रिया, अनमर्सहेलिंग) का निर्माण करती है, Viewऔर Activityखिड़की को खोए या डेटा खोए बिना नष्ट किया जा सकता है और फिर से बनाया जा सकता है। यह एंड्रॉइड अनुशंसित व्यवहार की अनुमति देता है, जो प्रत्येक कॉन्फ़िगरेशन परिवर्तन (जैसे प्रत्येक अभिविन्यास परिवर्तन के लिए) पर एक गतिविधि को नष्ट करना है

यह थोड़ा अधिक जटिल है, लेकिन यह सर्वर अनुरोध, डेटा प्री / पोस्ट-प्रोसेसिंग आदि को लागू करने का सबसे अच्छा तरीका है।

आप Serviceप्रत्येक अनुरोध को किसी सर्वर पर कतार में लगाने के लिए भी उपयोग कर सकते हैं , इसलिए यह उन चीज़ों को संभालने के लिए आसान और कुशल बनाता है।

देव गाइड पर एक पूर्ण अध्याय हैServices


A Serviceसे अधिक काम है, AsyncTaskलेकिन कुछ स्थितियों में बेहतर दृष्टिकोण हो सकता है। यह जरूरी बेहतर नहीं है, क्या यह है? यह कहा जा रहा है, मुझे समझ में नहीं आता है कि यह किस तरह से उस समस्या को हल करता है ProgressDialogजो मुख्य से लीक होती है Activity। आप कहां से भड़काते हैं ProgressDialog? आप इसे कहाँ खारिज करते हैं?
आरडीएस

2

मेरे पास एक कार्यान्वयन है जो गतिविधि को स्क्रीन अभिविन्यास परिवर्तन पर नष्ट करने की अनुमति देता है, लेकिन फिर भी फिर से सक्रिय गतिविधि में संवाद को नष्ट कर देता है। मैं उपयोग करता हूं...NonConfigurationInstance बैकग्राउंड टास्क को रिक्रिएटेड एक्टिविटी में अटैच करने के लिए करता । सामान्य एंड्रॉइड फ्रेमवर्क स्वयं संवाद को फिर से संभालता है, वहां कुछ भी नहीं बदला जाता है।

मैंने 'मालिक' गतिविधि के लिए एक क्षेत्र और इस मालिक को अद्यतन करने के लिए एक विधि जोड़ते हुए AsyncTask को उप-वर्गित किया।

class MyBackgroundTask extends AsyncTask<...> {
  MyBackgroundTask (Activity a, ...) {
    super();
    this.ownerActivity = a;
  }

  public void attach(Activity a) {
    ownerActivity = a;
  }

  protected void onPostExecute(Integer result) {
    super.onPostExecute(result);
    ownerActivity.dismissDialog(DIALOG_PROGRESS);
  }

  ...
}

अपनी गतिविधि कक्षा में मैंने backgroundTask'स्वामित्व' पृष्ठभूमि के संदर्भ में एक फ़ील्ड जोड़ा , और मैं इस क्षेत्र का उपयोग करके अद्यतन करता हूं onRetainNonConfigurationInstanceऔरgetLastNonConfigurationInstance

class MyActivity extends Activity {
  public void onCreate(Bundle savedInstanceState) {
    ...
    if (getLastNonConfigurationInstance() != null) {
      backgroundTask = (MyBackgroundTask) getLastNonConfigurationInstance();
      backgroundTask.attach(this);
    }
  }

  void startBackgroundTask() {
    backgroundTask = new MyBackgroundTask(this, ...);
    showDialog(DIALOG_PROGRESS);
    backgroundTask.execute(...);
  }

  public Object onRetainNonConfigurationInstance() {
    if (backgroundTask != null && backgroundTask.getStatus() != Status.FINISHED)
      return backgroundTask;
    return null;
  }
  ...
}

और सुधार के सुझाव:

  • इसे क्लियर करें backgroundTaskकिसी भी मेमोरी या इससे जुड़े अन्य संसाधनों को छोड़ने के लिए कार्य समाप्त होने के बाद गतिविधि में संदर्भ को ।
  • इसे क्लियर करें ownerActivityगतिविधि के नष्ट होने से पहले बैकग्राउंड में संदर्भ को अगर यह तुरंत फिर से नहीं बनाया जाएगा।
  • एक BackgroundTaskही मालिक गतिविधि से चलाने के लिए विभिन्न प्रकार के कार्यों की अनुमति देने के लिए एक इंटरफ़ेस और / या संग्रह बनाएं ।

2

यदि आप दो लेआउट बनाए रखते हैं, तो सभी UI थ्रेड को समाप्त कर दिया जाना चाहिए।

यदि आप AsynTask का उपयोग करते हैं, तो आप वर्तमान गतिविधि के .cancel()तरीके के अंदर आसानी से कॉल कर सकते हैं onDestroy()

@Override
protected void onDestroy (){
    removeDialog(DIALOG_LOGIN_ID); // remove loading dialog
    if (loginTask != null){
        if (loginTask.getStatus() != AsyncTask.Status.FINISHED)
            loginTask.cancel(true); //cancel AsyncTask
    }
    super.onDestroy();
}

AsyncTask के लिए, यहाँ पर "एक कार्य को रद्द करना" अनुभाग में अधिक पढ़ें ।

अपडेट: स्थिति की जांच करने के लिए जोड़ा गया शर्त, क्योंकि यह केवल तभी रद्द किया जा सकता है जब वह चालू स्थिति में हो। यह भी ध्यान दें कि AsyncTask को केवल एक बार निष्पादित किया जा सकता है।


2

जिप्ट्रेलोन के समाधान को लागू करने की कोशिश की गई क्योंकि यह इन मुद्दों का एक " ठोस समाधान है जो 'चीजों के एंड्रॉइड वे' के अनुरूप है " लेकिन इसे देखने के लिए और वर्णित सभी तत्वों को एक साथ रखने में कुछ समय लगा। यह थोड़ा अलग के साथ समाप्त हुआ, और मुझे लगता है कि अधिक सुरुचिपूर्ण, समाधान यहां संपूर्णता में पोस्ट किया गया है।

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

गतिविधि वर्ग:

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);
}}

इरादे सेवा वर्ग:

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);
    }
}}

प्रकट फ़ाइल प्रविष्टियाँ:

आवेदन अनुभाग से पहले:

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

आवेदन अनुभाग के अंदर

service android:name=".MyService"

2

यह मेरा प्रस्तावित समाधान है:

  • जैसा कि यहां बताया गया है, AsyncTask या थ्रेड को बनाए रखने के लिए ले जाएँ । मेरा मानना ​​है कि सभी नेटवर्क कॉल को टुकड़ों में स्थानांतरित करना एक अच्छा अभ्यास है। यदि आप पहले से ही टुकड़ों का उपयोग कर रहे हैं, तो उनमें से एक को कॉल के लिए जिम्मेदार बनाया जा सकता है। अन्यथा, आप केवल अनुरोध करने के लिए एक टुकड़ा बना सकते हैं, जैसा कि जुड़ा हुआ लेख प्रस्तावित करता है।
  • टुकड़ा कार्य पूरा होने / विफलता का संकेत देने के लिए एक श्रोता इंटरफ़ेस का उपयोग करेगा। आपको वहाँ अभिविन्यास परिवर्तन के लिए चिंता करने की ज़रूरत नहीं है। टुकड़े में हमेशा वर्तमान गतिविधि के लिए सही लिंक होगा और प्रगति संवाद सुरक्षित रूप से फिर से शुरू किया जा सकता है।
  • अपनी प्रगति संवाद को अपनी कक्षा का सदस्य बनाएं। वास्तव में आपको ऐसा सभी संवादों के लिए करना चाहिए। ऑनपॉज विधि में आपको उन्हें खारिज कर देना चाहिए, अन्यथा आप कॉन्फ़िगरेशन परिवर्तन पर एक खिड़की को लीक कर देंगे। व्यस्त अवस्था को खंड द्वारा रखा जाना चाहिए। जब खंड गतिविधि से जुड़ा होता है, तो आप फिर से प्रगति संवाद ला सकते हैं, अगर कॉल अभी भी चल रहा है। void showProgressDialog()इस उद्देश्य के लिए एक विधि को खंड-गतिविधि श्रोता इंटरफ़ेस में जोड़ा जा सकता है।

सही समाधान है, लेकिन समझ में नहीं आता कि यह उत्तर दूसरों से अलग क्यों है !!!
कालाकरा

2

मैंने उसी स्थिति का सामना किया। मैंने जो किया वह पूरे एप्लिकेशन में मेरी प्रगति संवाद के लिए केवल एक उदाहरण है।

सबसे पहले, मैंने केवल एक उदाहरण (सिंग्लटन पैटर्न) प्राप्त करने के लिए एक डायलॉग्सलिंगटन क्लास बनाया

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();
        }
    }
}

जैसा कि मैंने इस कक्षा में दिखाया है, मेरे पास प्रगति के रूप में संवाद है। जब भी मुझे एक प्रोग्रेस डायलॉग दिखाने की जरूरत होती है, मुझे अनोखा उदाहरण मिलता है और एक नया प्रोग्रेसडायलॉग बनाता है।

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

जब मैं पृष्ठभूमि कार्य के साथ किया जाता है, तो मैं फिर से अद्वितीय उदाहरण कहता हूं और इसके संवाद को खारिज करता हूं।

DialogSingleton.GetInstance().DialogDismiss(this);

मैं अपनी साझा प्राथमिकताओं में पृष्ठभूमि कार्य की स्थिति को बचाता हूं। जब मैं स्क्रीन को घुमाता हूं, तो मैं पूछता हूं कि क्या मेरे पास इस गतिविधि के लिए कोई कार्य चल रहा है: (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).

जब मैं एक पृष्ठभूमि कार्य चलाना शुरू करता हूं:

preference.AddValue(IS_TASK_NAME_EXECUTED_KEY, true, "boolean");

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

जब मैं एक पृष्ठभूमि का काम पूरा कर रहा हूं:

preference.AddValue(IS_TASK_NAME_EXECUTED_KEY, false, "boolean");

DialogSingleton.GetInstance().DialogDismiss(ActivityName.this);

मुझे उम्मीद है यह मदद करेगा।


2

यह एक बहुत पुराना सवाल है जो किसी कारण से साइडबार पर आया है।

यदि पृष्ठभूमि कार्य को केवल जीवित रहने की आवश्यकता है, जब गतिविधि अग्रभूमि में है, तो "नया" समाधान पृष्ठभूमि के धागे (या, अधिमानतः AsyncTask) को एक बनाए हुए टुकड़े में होस्ट करने के लिए है , जैसा कि इस डेवलपर गाइड और कई क्यू एंड अस में वर्णित है ।

यदि एक कॉन्फ़िगरेशन परिवर्तन के लिए गतिविधि नष्ट हो जाती है, तो एक बरकरार टुकड़ा बच जाता है, लेकिन तब नहीं जब गतिविधि पृष्ठभूमि या बैक स्टैक में नष्ट हो जाती है। इसलिए, पृष्ठभूमि का कार्य अभी भी बाधित होना चाहिए, यदि isChangingConfigurations()वह गलत है onPause()


2

मैं एंड्रॉइड में एक फ्रेशर हूं और मैंने यह कोशिश की और यह काम कर रहा है।

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);
        }
}

1

मैंने कभी कोशिश की है। बिताए हुए दिन। मैं गतिविधि को घूमने से रोकना नहीं चाहता था। मेरा परिदृश्य था:

  1. उपयोगकर्ता को गतिशील जानकारी दिखाने वाला एक प्रगति संवाद। जैसे: "सर्वर से जुड़ना ...", "डेटा डाउनलोड करना ...", आदि।
  2. एक थ्रेड भारी सामान कर रहा है और संवाद को अपडेट कर रहा है
  3. अंत में परिणाम के साथ यूआई को अपडेट करना।

समस्या यह थी कि स्क्रीन को घुमाते समय, पुस्तक का प्रत्येक समाधान विफल हो गया। यहां तक ​​कि AsyncTask वर्ग के साथ, जो इस स्थितियों से निपटने का सही Android तरीका है। स्क्रीन को घुमाते समय, वर्तमान प्रसंग जो कि शुरुआती थ्रेड के साथ काम कर रहा है, चला गया है, और यह उस डायलॉग के साथ गड़बड़ करता है जो दिखा रहा है। समस्या हमेशा डायलॉग थी, चाहे मैंने कोड में कितनी भी तरकीबें जोड़ दी हों (दौड़ने के लिए नए संदर्भों को पारित करना, रोटेशन स्टेट्स को रोटेशन के माध्यम से बनाए रखना, आदि ...)। अंत में कोड की जटिलता हमेशा बहुत बड़ी थी और हमेशा कुछ ऐसा था जो गलत हो सकता है।

मेरे लिए काम करने वाला एकमात्र समाधान गतिविधि / संवाद चाल थी। यह सरल और प्रतिभाशाली है और यह सभी रोटेशन प्रूफ है:

  1. डायलॉग बनाने और उसे दिखाने के लिए कहने के बजाय, एक गतिविधि बनाएँ जो Android: theme = "@ android: style / Theme.Dialog" के साथ प्रकट हो। तो, यह सिर्फ एक संवाद की तरह दिखता है।

  2. ShowDialog (DIALOG_ID) को startActivityForResult (yourActivityDialog, yourCode) से बदलें;

  3. निष्पादन थ्रेड (यहां तक ​​कि त्रुटियों) से परिणाम प्राप्त करने और UI को अपडेट करने के लिए कॉलिंग गतिविधि में onActivityResult का उपयोग करें।

  4. अपने 'एक्टिविटीडायलॉग' पर, स्क्रीन को घुमाते समय "संवाद" स्थिति को बचाने के लिए लंबे कार्यों और onRetainNonConfigurationInstance को निष्पादित करने के लिए थ्रेड्स या AsyncTask का उपयोग करें।

यह तेज है और ठीक काम करता है। मैं अभी भी अन्य कार्यों के लिए संवाद और कुछ के लिए AsyncTask का उपयोग करता हूं जिनके लिए स्क्रीन पर निरंतर संवाद की आवश्यकता नहीं है। लेकिन इस परिदृश्य के साथ, मैं हमेशा गतिविधि / संवाद पैटर्न के लिए जाता हूं।

और, मैंने इसे आज़माया नहीं, लेकिन यह भी संभव है कि उस गतिविधि / संवाद को घुमाने से रोक दिया जाए, जब धागा चल रहा हो, चीजों को गति दे रहा हो, जबकि कॉलिंग गतिविधि को घुमाने की अनुमति हो।


कि मानकों को छोड़कर अच्छा एक के माध्यम से पारित किया जाना चाहिए Intent, जो अधिक प्रतिबंधात्मक से है Objectद्वारा अनुमतिAsyncTask
आरडीएस

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

1

इन दिनों इस प्रकार के मुद्दों को संभालने के लिए एक अधिक विशिष्ट तरीका है। विशिष्ट दृष्टिकोण है:

1. सुनिश्चित करें कि आपका डेटा UI से ठीक से अलग है:

कुछ भी जो एक पृष्ठभूमि प्रक्रिया है, उसे बरकरार रखा जाना चाहिए Fragment(इसे इसके साथ सेट करें Fragment.setRetainInstance()। यह आपका 'लगातार डेटा स्टोरेज' बन जाता है, जहाँ आप जिस डेटा को बनाए रखना चाहते हैं, उसके आधार पर कुछ भी रखा जाता है। ओरिएंटेशन चेंज ईवेंट के बाद, यह Fragmentअभी भी इसके मूल में उपलब्ध होगा। FragmentManager.findFragmentByTag()कॉल के माध्यम से बताएं (जब आप इसे बनाते हैं तो आपको इसे एक टैग नहीं आईडी देना चाहिए क्योंकि यह संलग्न नहीं हैView )।

हैंडलिंग रनटाइम परिवर्तन देखें इस सही ढंग से कर रही है और क्यों यह सबसे अच्छा विकल्प है के बारे में जानकारी के लिए विकसित की गाइड।

2. सुनिश्चित करें कि आप पृष्ठभूमि प्रक्रियाओं और अपने UI के बीच सही और सुरक्षित रूप से हस्तक्षेप कर रहे हैं:

आपको अपनी लिंकिंग प्रक्रिया को उलट देना चाहिए । फिलहाल आपका बैकग्राउंड प्रोसेस खुद से अटेच होता है View- इसके बजाय आपका Viewबैकग्राउंड प्रोसेस से खुद को अटैच करना चाहिए। यह अधिक समझ में आता है? यह Viewक्रिया पृष्ठभूमि प्रक्रिया पर निर्भर है, जबकि पृष्ठभूमि प्रक्रिया Viewइस पर निर्भर नहीं है । इसका मतलब लिंक को मानक Listenerइंटरफ़ेस में बदलना है। अपनी प्रक्रिया कहें (जो भी वर्ग है - चाहे वह एक हो AsyncTask, Runnableया जो भी हो) एक को परिभाषित करता है OnProcessFinishedListener, जब प्रक्रिया पूरी हो जाती है तो उसे उस श्रोता को कॉल करना चाहिए यदि वह मौजूद है।

यह उत्तर कस्टम श्रोताओं को कैसे करना है, का एक अच्छा संक्षिप्त विवरण है।

3. जब भी UI बनाया जाए (अभिविन्यास परिवर्तन सहित) डेटा प्रक्रिया में अपने UI को लिंक करें:

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

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

यह तय करने के लिए फ्रैगमेंट जीवनचक्र देखें कि श्रोता की सेटिंग आपके व्यक्तिगत कार्यान्वयन में सबसे उपयुक्त है।

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


1

अभिविन्यास बदलने पर मैंने थ्रेड को संभालने के लिए आसान और आसान उपाय खोजा है। आप बस अपनी गतिविधि / खंडन के लिए एक स्थिर संदर्भ रख सकते हैं और सत्यापित कर सकते हैं कि क्या ui पर अभिनय करने से पहले इसकी अशक्तता है। मेरा सुझाव है कि एक कोशिश पकड़ने का भी उपयोग करें:

 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;
    }


}

1

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

सेट अप

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

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

        //DO SOMETHING HERE!! :D
    }
}

कार्यान्वयन 1 - संवाद

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

कार्यान्वयन 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();

कार्यान्वयन 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();

1

जब मैंने इसका सामना किया तो यह मेरा समाधान है: ProgressDialogएक Fragmentबच्चा नहीं है , इसलिए मेरा कस्टम वर्ग " ProgressDialogFragment" DialogFragmentविन्यास के बदलाव के लिए दिखाए गए संवाद को बनाए रखने के लिए इसके बजाय विस्तार कर सकता है ।

import androidx.annotation.NonNull;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.os.Bundle; 
import androidx.fragment.app.DialogFragment;
import androidx.fragment.app.FragmentManager;

 /**
 * Usage:
 * To display the dialog:
 *     >>> ProgressDialogFragment.showProgressDialogFragment(
 *              getSupportFragmentManager(), 
 *              "fragment_tag", 
 *              "my dialog title", 
 *              "my dialog message");
 *              
 * To hide the dialog
 *     >>> ProgressDialogFragment.hideProgressDialogFragment();
 */ 


public class ProgressDialogFragment extends DialogFragment {

    private static String sTitle, sMessage;
    private static ProgressDialogFragment sProgressDialogFragment;

    public ProgressDialogFragment() {
    }

    private ProgressDialogFragment(String title, String message) {
        sTitle = title;
        sMessage = message;
    }


    @NonNull
    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        return ProgressDialog.show(getActivity(), sTitle, sMessage);
    }

    public static void showProgressDialogFragment(FragmentManager fragmentManager, String fragmentTag, String title, String message) {
        if (sProgressDialogFragment == null) {
            sProgressDialogFragment = new ProgressDialogFragment(title, message);
            sProgressDialogFragment.show(fragmentManager, fragmentTag);

        } else { // case of config change (device rotation)
            sProgressDialogFragment = (ProgressDialogFragment) fragmentManager.findFragmentByTag(fragmentTag); // sProgressDialogFragment will try to survive its state on configuration as much as it can, but when calling .dismiss() it returns NPE, so we have to reset it on each config change
            sTitle = title;
            sMessage = message;
        }

    }

    public static void hideProgressDialogFragment() {
        if (sProgressDialogFragment != null) {
            sProgressDialogFragment.dismiss();
        }
    }
}

यह चुनौती स्क्रीन के रोटेशन के दौरान डायलॉग शीर्षक और संदेश को बनाए रखने के लिए थी क्योंकि वे डिफ़ॉल्ट खाली स्ट्रिंग पर रीसेट हो गए, हालांकि संवाद अभी भी दिखा

इसे हल करने के लिए 2 दृष्टिकोण हैं:

पहला तरीका: वह गतिविधि करें, जो डायलॉग फाइल को बदलने के लिए डायलॉग का इस्तेमाल करती है।

android:configChanges="orientation|screenSize|keyboardHidden"

यह दृष्टिकोण Google द्वारा पसंद नहीं किया गया है।

दूसरा तरीका: गतिविधि के onCreate()तरीके पर, आपको शीर्षक और संदेश के साथ फिर DialogFragmentसे पुनर्निर्माण करके अपने को बनाए रखने की आवश्यकता है ProgressDialogFragmentजैसे कि यदि savedInstanceStateयह शून्य नहीं है:

@Override
protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_deal);

 if (savedInstanceState != null) {
      ProgressDialogFragment saveProgressDialog = (ProgressDialogFragment) getSupportFragmentManager()
              .findFragmentByTag("fragment_tag");
      if (saveProgressDialog != null) {
          showProgressDialogFragment(getSupportFragmentManager(), "fragment_tag", "my dialog title", "my dialog message");
      }
  }
}

0

अब तक 'जल्दी और गंदे' सच लगता है तो कृपया खामियों को इंगित करें लेकिन मुझे जो मिला वह काम किया ...

अपने AsyncTask के onPostExecute मेथड के भीतर, मैंने एक कोशिश / कैच ब्लॉक (एक खाली कैच के साथ) में प्रोग्रेस डायलॉग के लिए '.डिज्मिस' को लपेट दिया और फिर जो अपवाद उठाया गया था, उसे अनदेखा कर दिया। ऐसा करने में गलत लगता है, लेकिन ऐसा प्रतीत नहीं होता है कि कोई बुरा प्रभाव नहीं है (कम से कम जो मैं बाद में कर रहा हूं उसके लिए जो एक लंबे समय तक चलने वाली क्वेरी के परिणाम में गुजरने वाली एक अन्य गतिविधि शुरू करना है)


क्या आप कह रहे हैं कि वास्तव में कोई मेमोरी लीक नहीं है जब एंड्रॉइड प्लेटफॉर्म बताता है कि खिड़की लीक है?
आरडीएस

मेरी प्रगति संवाद गतिविधि वर्ग का एक सदस्य चर है इसलिए मैंने माना कि जब गतिविधि नष्ट हो जाती है और फिर से बनाया जाता है तो यह कचरा एकत्र हो जाएगा और कोई रिसाव नहीं होता है। क्या मै गलत हु?
साइमन

हां, मुझे लगता है कि यह गलत है। जैसा कि आप कहते हैं, का Activityसंदर्भ है Dialog। जब कॉन्फ़िगरेशन बदल जाता है, तो पहले Activityनष्ट हो जाता है, जिसका अर्थ है कि सभी फ़ील्ड सेट हैं null। लेकिन निम्न-स्तर का WindowManagerभी संदर्भ है Dialog(क्योंकि यह अभी तक खारिज नहीं किया गया है)। नया Activityएक नया Dialog(इन preExecute()) बनाने की कोशिश करता है और विंडो मैनेजर एक घातक अपवाद को उठाता है जो आपको ऐसा करने से रोकता है। वास्तव में, यदि ऐसा होता है, तो Dialogप्रारंभिक रूप से संदर्भ को ध्यान में रखते हुए सफाई को नष्ट करने का कोई तरीका नहीं होगा Activity। क्या मैं सही हू?
आरडीएस

0

सबसे सरल और सबसे लचीला समाधान प्रोग्रेसबार के लिए एक स्थिर संदर्भ के साथ एक AsyncTask का उपयोग करना है । यह उन्मुखीकरण परिवर्तन की समस्याओं के लिए एक समझाया और पुन: प्रयोज्य समाधान प्रदान करता है। इस समाधान ने मुझे इंटरनेट डाउनलोड, सेवाओं के साथ संचार , और फाइलसिस्टम स्कैन सहित विभिन्न प्रकार के असंगत कार्यों के लिए अच्छी तरह से सेवा दी है । समाधान को कई एंड्रॉइड संस्करणों और फोन मॉडल पर अच्छी तरह से परीक्षण किया गया है। एक पूर्ण डेमो यहां DownloadFile.java में विशिष्ट रुचि के साथ पाया जा सकता है

मैं एक अवधारणा उदाहरण के रूप में निम्नलिखित प्रस्तुत करता हूं

public class SimpleAsync extends AsyncTask<String, Integer, String> {
    private static ProgressDialog mProgressDialog = null;
    private final Context mContext;

    public SimpleAsync(Context context) {
        mContext = context;
        if ( mProgressDialog != null ) {
            onPreExecute();
        }
    }

    @Override
    protected void onPreExecute() {
        mProgressDialog = new ProgressDialog( mContext );
        mProgressDialog.show();
    }

    @Override
    protected void onPostExecute(String result) {
        if ( mProgressDialog != null ) {
            mProgressDialog.dismiss();
            mProgressDialog = null;
        }
    }

    @Override
    protected void onProgressUpdate(Integer... progress) {
        mProgressDialog.setProgress( progress[0] );
    }

    @Override
    protected String doInBackground(String... sUrl) {
        // Do some work here
        publishProgress(1);
        return null;
    }

    public void dismiss() {
        if ( mProgressDialog != null ) {
            mProgressDialog.dismiss();
        }
    }
}

Android गतिविधि में उपयोग सरल है

public class MainActivity extends Activity {
    DemoServiceClient mClient = null;
    DownloadFile mDownloadFile = null;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate( savedInstanceState );
        setContentView( R.layout.main );
        mDownloadFile = new DownloadFile( this );

        Button downloadButton = (Button) findViewById( R.id.download_file_button );
        downloadButton.setOnClickListener( new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                mDownloadFile.execute( "http://www.textfiles.com/food/bakebred.txt");
            }
        });
    }

    @Override
    public void onPause() {
        super.onPause();
        mDownloadFile.dismiss();
    }
}

0

जब आप ओरिएंटेशन बदलते हैं, तो एंड्रॉइड उस गतिविधि को मारता है और नई गतिविधि बनाता है। मैं आरएक्स जावा के साथ रेट्रोफिट का उपयोग करने का सुझाव देता हूं। जो स्वचालित रूप से क्रैश को हैंडल करता है।

रेट्रोफिट कॉल करते समय इन विधि का उपयोग करें।

.subscribeOn (Schedulers.io ()) .observeOn (AndroidSchedulers.mainThread ())

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.