स्क्रीन रोटेशन के दौरान एक AsyncTask कैसे संभालें?


88

मैंने अपनी आवृत्ति स्थिति को कैसे बचाया जाए या स्क्रीन रोटेशन के दौरान नष्ट होने वाली मेरी गतिविधि से कैसे निपटें, इस पर बहुत कुछ पढ़ा।

वहाँ बहुत सारी संभावनाएँ लगती हैं लेकिन मुझे यह पता नहीं चला है कि कौन सा एक AsyncTask के परिणाम प्राप्त करने के लिए सबसे अच्छा काम करता है।

मेरे पास कुछ AsyncTasks हैं जो बस फिर से शुरू किए जाते हैं और isFinishing()गतिविधि की विधि को कॉल करते हैं और यदि गतिविधि खत्म हो रही है तो वे कुछ भी अपडेट नहीं करेंगे।

समस्या यह है कि मेरे पास एक टास्क है जो एक वेब सेवा के लिए एक अनुरोध करता है जो विफल हो सकता है या सफल हो सकता है और कार्य को पुनरारंभ करने से उपयोगकर्ता के लिए वित्तीय नुकसान होगा।

आप इसे कैसे हल करेंगे? संभावित समाधान के फायदे या नुकसान क्या हैं?


1
मेरा जवाब यहां देखें । आपको यह जानकारीsetRetainInstance(true) भी मिल सकती है कि वास्तव में क्या मददगार है।
टिम्मम

मैं बस एक स्थानीय सेवा लागू करता हूं जो प्रसंस्करण (एक थ्रेड में) करता है जो आपके asyncTask कर रहा है। परिणाम प्रदर्शित करने के लिए, डेटा को अपनी गतिविधि पर प्रसारित करें। अब गतिविधि केवल डेटा दिखाने के लिए जिम्मेदार है और स्क्रीन रोटेशन से प्रसंस्करण कभी बाधित नहीं होता है।
कोई व्यक्ति

AsyncTask के बजाय AsyncTaskLoader का उपयोग करने के बारे में क्या ??
सौरांगशू बिस्वास

जवाबों:


6

मेरा पहला सुझाव यह सुनिश्चित करना होगा कि आपको वास्तव में स्क्रीन रोटेशन (डिफ़ॉल्ट व्यवहार) पर रीसेट करने के लिए आपकी गतिविधि की आवश्यकता होगी। हर बार जब मैंने रोटेशन के साथ समस्याएँ की हैं, तो मैंने इस विशेषता को <activity>AndroidManifest.xml में अपने टैग में जोड़ा है , और बस ठीक रहा।

android:configChanges="keyboardHidden|orientation"

यह अजीब लग रहा है, लेकिन यह आपकी onConfigurationChanged()विधि को क्या करता है , अगर आप इसकी आपूर्ति नहीं करते हैं तो यह लेआउट को फिर से मापने के अलावा और कुछ नहीं करता है, जो कि ज्यादातर समय घुमाव को संभालने का एक बिल्कुल पर्याप्त तरीका प्रतीत होता है। ।


5
लेकिन यह गतिविधि को लेआउट बदलने से रोकेगा। और इसलिए उपयोगकर्ता को आपके एप्लिकेशन द्वारा निर्धारित एक विशिष्ट अभिविन्यास में अपने डिवाइस का उपयोग करने के लिए मजबूर करता है न कि उसकी आवश्यकताओं के द्वारा।
Janusz

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

38
खैर यह सिर्फ यह है, रोमेन। "यदि आप अपने लेआउट या चित्र या तार या जो चित्र और परिदृश्य में अलग होना चाहते हैं, तो आप डिफ़ॉल्ट व्यवहार चाहते हैं", मेरा मानना ​​है कि आप की तुलना में यह बहुत दुर्लभ उपयोग का मामला है। जिसे आप "बहुत विशिष्ट मामले" कहते हैं, वह है अधिकांश डेवलपर्स जो मेरा मानना ​​है। सभी आयामों में काम करने वाले सापेक्ष लेआउट का उपयोग करना सबसे अच्छा अभ्यास है और यह उतना कठिन नहीं है। आलसीपन की बात अत्यधिक गुमराह करने वाली है, ये तकनीकें उपयोगकर्ता के अनुभव को बेहतर बनाने के लिए हैं ताकि विकास का समय कम न हो।
जिम ब्लैकलर

2
मैंने पाया है कि यह LinearLayout के लिए एकदम सही काम करता है, लेकिन RelativeLayout का उपयोग करते समय यह लैंडस्केप मोड में स्विच करते समय लेआउट को सही ढंग से नहीं बदलता है (कम से कम N1 पर नहीं)। इस प्रश्न को देखें: stackoverflow.com/questions/2987049/…
JohnRock

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

46

आप कैसे मैं संभाल की जांच कर सकते AsyncTaskमें है और ओरिएंटेशन परिवर्तन code.google.com/p/shelves । इसे करने के विभिन्न तरीके हैं, मैंने इस ऐप में जो भी चुना है वह किसी भी वर्तमान में चल रहे कार्य को रद्द करने, अपने राज्य को बचाने और नए बनाए जाने पर सहेजे गए राज्य के साथ एक नया प्रारंभ करना Activityहै। यह करना आसान है, यह अच्छी तरह से काम करता है और एक बोनस के रूप में यह उपयोगकर्ता को ऐप छोड़ने पर आपके कार्यों को रोकने का ख्याल रखता है।

आप onRetainNonConfigurationInstance()अपने AsyncTaskनए को पास करने के लिए भी उपयोग कर सकते हैं Activity( Activityहालांकि पिछले इस तरह से लीक नहीं करने के बारे में सावधान रहें ।)


1
मैंने इसे आज़माया और पुस्तक खोज के दौरान घूमने में बाधा उत्पन्न करता है और मुझे यह परिणाम देता है कि जब मैं नहीं घूमता हूं तो बहुत कम परिणाम देता है
अधिकतम 4

1
मुझे उस कोड में AsyncTask का एक भी उपयोग नहीं मिला। एक वर्ग UserTask है जो समान दिखता है। क्या यह परियोजना AsyncTask से पहले की है?
डेवोकॉन

7
उपयोगकर्ता से AsyncTask आया। मैंने मूल रूप से अपने स्वयं के ऐप्स के लिए UserTask लिखा और बाद में इसे AsyncTask में बदल दिया। इसके बारे में क्षमा करें कि मैं भूल गया इसका नाम बदल गया।
रोमेन गाइ

@RomainGuy हाय, आशा है कि आप ठीक हैं। आपके कोड के अनुसार 2 अनुरोध सर्वर पर भेजे जाते हैं, हालांकि पहले कार्य को रद्द कर दिया गया है लेकिन यह सफलतापूर्वक रद्द नहीं हुआ है। मुझे पता नहीं क्यों। क्या आप मुझे बताएंगे कि इसे हल करने का कोई तरीका है।
Iamcrypticcoder

10

यह सबसे दिलचस्प सवाल मैंने एंड्रॉइड के संबंध में देखा है !!! वास्तव में मैं पहले से ही पिछले महीनों के दौरान समाधान की तलाश कर रहा हूं। अभी भी हल नहीं हुआ है।

बस ओवरराइडिंग से सावधान रहें

android:configChanges="keyboardHidden|orientation"

सामान पर्याप्त नहीं है।

जब आपका AsyncTask चल रहा हो तो उपयोगकर्ता को एक फ़ोन कॉल प्राप्त होने पर उस स्थिति पर विचार करें। आपके अनुरोध को सर्वर द्वारा पहले ही संसाधित किया जा रहा है, इसलिए AsyncTask को प्रतिक्रिया का इंतजार है। इस क्षण में आपका ऐप बैकग्राउंड में चला जाता है, क्योंकि फोन ऐप अभी सामने आया है। ओएस आपकी गतिविधि को मार सकता है क्योंकि यह पृष्ठभूमि में है।


6

आप हमेशा Android द्वारा प्रदान किए गए सिंगलटन पर वर्तमान AsyncTask का संदर्भ क्यों नहीं रखते हैं?

जब भी कोई कार्य शुरू होता है, PreExecute पर या बिल्डर पर, आप परिभाषित करते हैं:

((Application) getApplication()).setCurrentTask(asyncTask);

जब भी यह खत्म होता है आप इसे शून्य करने के लिए सेट करते हैं।

इस तरह से आपके पास हमेशा एक संदर्भ होता है जो आपको कुछ ऐसा करने की अनुमति देता है, जैसे कि onCreate या onResume के रूप में आपके विशिष्ट कार्य के लिए विनियोजित:

this.asyncTaskReference = ((Application) getApplication()).getCurrentTask();

यदि यह शून्य है, तो आप जानते हैं कि वर्तमान में कोई भी नहीं चल रहा है!

:-)


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

6
Applicationउदाहरण का अपना जीवन चक्र है - यह ओएस द्वारा भी मारा जा सकता है, इसलिए यह समाधान एक कठिन-से-प्रजनन बग पैदा कर सकता है।
विट खूदेंको

7
मैंने सोचा: यदि एप्लिकेशन मारा जाता है, तो पूरे ऐप को मार दिया जाता है (और इस प्रकार, सभी AsyncTasks भी)?
मणिमाल

मुझे लगता है कि सभी एसिंक्शंस के (बहुत दुर्लभ) होने के बिना आवेदन को मारा जा सकता है। लेकिन @ प्रत्येक asynctask के शुरू और अंत में कुछ आसान करने के लिए सत्यापन के साथ आप कीड़े से बच सकते हैं।
neteinstein

5

इसके लिए सबसे उचित तरीका है, सड़ांध से अधिक, एस्किंक कार्य के उदाहरण को बनाए रखने के लिए एक टुकड़े का उपयोग करना।

यहाँ बहुत ही सरल उदाहरण के लिए एक लिंक दिया गया है जिससे इस तकनीक को अपने ऐप्स में एकीकृत करना आसान हो जाता है।

https://gist.github.com/daichan4649/2480065


यहां एक और ट्यूटोरियल है जो बनाए गए टुकड़ों का उपयोग करता है: blogactivity.wordpress.com/2011/09/01/proper-use-of-asynctask
devconsole


3

मेरे दृष्टिकोण से, यह एसिंक्टस्क को onRetainNonConfigurationInstanceवर्तमान गतिविधि ऑब्जेक्ट से हटाने और अभिविन्यास परिवर्तन के बाद एक नई गतिविधि ऑब्जेक्ट से बाइंड करना बेहतर है । यहाँ मुझे एक बहुत अच्छा उदाहरण मिला कि कैसे AsyncTask और ProgressDialog के साथ काम करना है।


2

Android: कॉन्फ़िगरेशन परिवर्तन के साथ बैकग्राउंड प्रोसेसिंग / Async ओपरेशन

पृष्ठभूमि प्रक्रिया के दौरान async opeartion की स्थिति बनाए रखने के लिए: आप टुकड़ों की मदद ले सकते हैं।

निम्नलिखित चरण देखें:

चरण 1: एक शीर्षक रहित टुकड़ा बनाएँ पृष्ठभूमि कार्य को कहने दें और इसमें एक निजी async कार्य वर्ग जोड़ें।

चरण 2 (वैकल्पिक चरण): यदि आप कोड के नीचे अपनी गतिविधि के उपयोग के लिए लोडिंग कर्सर रखना चाहते हैं:

चरण 3: आपकी मुख्य गतिविधि में चरण 1 में परिभाषित BackgroundTaskCallbacks इंटरफ़ेस लागू करें

class BackgroundTask extends Fragment {
public BackgroundTask() {

}

// Add a static interface 

static interface BackgroundTaskCallbacks {
    void onPreExecute();

    void onCancelled();

    void onPostExecute();

    void doInBackground();
}

private BackgroundTaskCallbacks callbacks;
private PerformAsyncOpeation asyncOperation;
private boolean isRunning;
private final String TAG = BackgroundTask.class.getSimpleName();

/**
 * Start the async operation.
 */
public void start() {
    Log.d(TAG, "********* BACKGROUND TASK START OPERATION ENTER *********");
    if (!isRunning) {
        asyncOperation = new PerformAsyncOpeation();
        asyncOperation.execute();
        isRunning = true;
    }
    Log.d(TAG, "********* BACKGROUND TASK START OPERATION EXIT *********");
}

/**
 * Cancel the background task.
 */
public void cancel() {
    Log.d(TAG, "********* BACKGROUND TASK CANCEL OPERATION ENTER *********");
    if (isRunning) {
        asyncOperation.cancel(false);
        asyncOperation = null;
        isRunning = false;
    }
    Log.d(TAG, "********* BACKGROUND TASK CANCEL OPERATION EXIT *********");
}

/**
 * Returns the current state of the background task.
 */
public boolean isRunning() {
    return isRunning;
}

/**
 * Android passes us a reference to the newly created Activity by calling
 * this method after each configuration change.
 */
public void onAttach(Activity activity) {
    Log.d(TAG, "********* BACKGROUND TASK ON ATTACH ENTER *********");
    super.onAttach(activity);
    if (!(activity instanceof BackgroundTaskCallbacks)) {
        throw new IllegalStateException(
                "Activity must implement the LoginCallbacks interface.");
    }

    // Hold a reference to the parent Activity so we can report back the
    // task's
    // current progress and results.
    callbacks = (BackgroundTaskCallbacks) activity;
    Log.d(TAG, "********* BACKGROUND TASK ON ATTACH EXIT *********");
}

public void onCreate(Bundle savedInstanceState) {
    Log.d(TAG, "********* BACKGROUND TASK ON CREATE ENTER *********");
    super.onCreate(savedInstanceState);
    // Retain this fragment across configuration changes.
    setRetainInstance(true);
    Log.d(TAG, "********* BACKGROUND TASK ON CREATE EXIT *********");
}

public void onDetach() {
    super.onDetach();
    callbacks = null;
}

private class PerformAsyncOpeation extends AsyncTask<Void, Void, Void> {
    protected void onPreExecute() {
        Log.d(TAG,
                "********* BACKGROUND TASK :-> ASYNC OPERATION :- > ON PRE EXECUTE ENTER *********");
        if (callbacks != null) {
            callbacks.onPreExecute();
        }
        isRunning = true;
        Log.d(TAG,
                "********* BACKGROUND TASK :-> ASYNC OPERATION :- > ON PRE EXECUTE EXIT *********");
    }

    protected Void doInBackground(Void... params) {
        Log.d(TAG,
                "********* BACKGROUND TASK :-> ASYNC OPERATION :- > DO IN BACKGROUND ENTER *********");
        if (callbacks != null) {
            callbacks.doInBackground();
        }
        Log.d(TAG,
                "********* BACKGROUND TASK :-> ASYNC OPERATION :- > DO IN BACKGROUND EXIT *********");
        return null;
    }

    protected void onCancelled() {
        Log.d(TAG,
                "********* BACKGROUND TASK :-> ASYNC OPERATION :- > ON CANCEL ENTER *********");
        if (callbacks != null) {
            callbacks.onCancelled();
        }
        isRunning = false;
        Log.d(TAG,
                "********* BACKGROUND TASK :-> ASYNC OPERATION :- > ON CANCEL EXIT *********");
    }

    protected void onPostExecute(Void ignore) {
        Log.d(TAG,
                "********* BACKGROUND TASK :-> ASYNC OPERATION :- > ON POST EXECUTE ENTER *********");
        if (callbacks != null) {
            callbacks.onPostExecute();
        }
        isRunning = false;
        Log.d(TAG,
                "********* BACKGROUND TASK :-> ASYNC OPERATION :- > ON POST EXECUTE EXIT *********");
    }
}

public void onActivityCreated(Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);
    setRetainInstance(true);
}

public void onStart() {
    super.onStart();
}

public void onResume() {
    super.onResume();
}

public void onPause() {
    super.onPause();
}

public void onStop() {
    super.onStop();
}

public class ProgressIndicator extends Dialog {

public ProgressIndicator(Context context, int theme) {
    super(context, theme);
}

private ProgressBar progressBar;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    requestWindowFeature(Window.FEATURE_NO_TITLE);
    setContentView(R.layout.progress_indicator);
    this.setCancelable(false);
    progressBar = (ProgressBar) findViewById(R.id.progressBar);
    progressBar.getIndeterminateDrawable().setColorFilter(R.color.DarkBlue, android.graphics.PorterDuff.Mode.SCREEN);
}

@Override
public void show() {
    super.show();
}

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

@Override
public void cancel() {
    super.cancel();
}

public class MyActivity extends FragmentActivity implements BackgroundTaskCallbacks,{

private static final String KEY_CURRENT_PROGRESS = "current_progress";

ProgressIndicator progressIndicator = null;

private final static String TAG = MyActivity.class.getSimpleName();

private BackgroundTask task = null;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(//"set your layout here");
    initialize your views and widget here .............



    FragmentManager fm = getSupportFragmentManager();
    task = (BackgroundTask) fm.findFragmentByTag("login");

    // If the Fragment is non-null, then it is currently being
    // retained across a configuration change.
    if (task == null) {
        task = new BackgroundTask();
        fm.beginTransaction().add(task, "login").commit();
    }

    // Restore saved state
    if (savedInstanceState != null) {
        Log.i(TAG, "KEY_CURRENT_PROGRESS_VALUE ON CREATE :: "
                + task.isRunning());
        if (task.isRunning()) {
            progressIndicator = new ProgressIndicator(this,
                    R.style.TransparentDialog);
            if (progressIndicator != null) {
                progressIndicator.show();
            }
        }
    }
}

@Override
protected void onPause() {
    // TODO Auto-generated method stub
    super.onPause();

}

@Override
protected void onSaveInstanceState(Bundle outState) {
    // save the current state of your operation here by saying this 

    super.onSaveInstanceState(outState);
    Log.i(TAG, "KEY_CURRENT_PROGRESS_VALUE ON SAVE INSTANCE :: "
            + task.isRunning());
    outState.putBoolean(KEY_CURRENT_PROGRESS, task.isRunning());
    if (progressIndicator != null) {
        progressIndicator.dismiss();
        progressIndicator.cancel();
    }
    progressIndicator = null;
}


private void performOperation() {

            if (!task.isRunning() && progressIndicator == null) {
                progressIndicator = new ProgressIndicator(this,
                        R.style.TransparentDialog);
                progressIndicator.show();
            }
            if (task.isRunning()) {
                task.cancel();
            } else {
                task.start();
            }
        }


@Override
protected void onDestroy() {
    super.onDestroy();
    if (progressIndicator != null) {
        progressIndicator.dismiss();
        progressIndicator.cancel();
    }
    progressIndicator = null;
}

@Override
public void onPreExecute() {
    Log.i(TAG, "CALLING ON PRE EXECUTE");
}

@Override
public void onCancelled() {
    Log.i(TAG, "CALLING ON CANCELLED");
    if (progressIndicator != null) {
        progressIndicator.dismiss();
        progressIndicator.cancel();

}

public void onPostExecute() {
    Log.i(TAG, "CALLING ON POST EXECUTE");
    if (progressIndicator != null) {
        progressIndicator.dismiss();
        progressIndicator.cancel();
        progressIndicator = null;
    }
}

@Override
public void doInBackground() {
    // put your code here for background operation
}

}


1

एक बात पर विचार करना है कि क्या AsyncTask का परिणाम केवल उस गतिविधि के लिए उपलब्ध होना चाहिए जिसने कार्य शुरू किया था। यदि हाँ, तो रोमेन गाइ का जवाब सबसे अच्छा है। यदि यह आपके एप्लिकेशन की अन्य गतिविधियों के लिए उपलब्ध होना चाहिए, तो onPostExecuteआप उपयोग कर सकते हैं LocalBroadcastManager

LocalBroadcastManager.getInstance(getContext()).sendBroadcast(new Intent("finished"));

आपको यह भी सुनिश्चित करना होगा कि गतिविधि सही ढंग से स्थिति को संभालती है जब प्रसारण को भेजा जाता है जबकि गतिविधि को रोक दिया जाता है।


1

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


0

मेरा समाधान।

मेरे मामले में मुझे एक ही संदर्भ में AsyncTasks की एक श्रृंखला मिली है। गतिविधि की पहुँच केवल पहले वाले तक थी। किसी भी चल रहे कार्य को रद्द करने के लिए मैंने निम्नलिखित कार्य किए:

public final class TaskLoader {

private static AsyncTask task;

     private TaskLoader() {
         throw new UnsupportedOperationException();
     }

     public static void setTask(AsyncTask task) {
         TaskLoader.task = task;
     }

    public static void cancel() {
         TaskLoader.task.cancel(true);
     }
}

कार्य doInBackground():

protected Void doInBackground(Params... params) {
    TaskLoader.setTask(this);
    ....
}

गतिविधि onStop()या onPause():

protected void onStop() {
    super.onStop();
    TaskLoader.cancel();
}

0
@Override
protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    final AddTask task = mAddTask;
    if (task != null && task.getStatus() != UserTask.Status.FINISHED) {
        final String bookId = task.getBookId();
        task.cancel(true);

        if (bookId != null) {
            outState.putBoolean(STATE_ADD_IN_PROGRESS, true);
            outState.putString(STATE_ADD_BOOK, bookId);
        }

        mAddTask = null;
    }
}

@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
        super.onRestoreInstanceState(savedInstanceState);
    if (savedInstanceState.getBoolean(STATE_ADD_IN_PROGRESS)) {
        final String id = savedInstanceState.getString(STATE_ADD_BOOK);
        if (!BooksManager.bookExists(getContentResolver(), id)) {
            mAddTask = (AddTask) new AddTask().execute(id);
        }
    }
}

0

आप android भी जोड़ सकते हैं: configChanges = "कीबोर्डहेड | ओरिएंटेशन | स्क्रीनसाइज़"

आपके प्रकट उदाहरण से मुझे आशा है कि यह मदद करेगा

 <application
    android:name=".AppController"
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:roundIcon="@mipmap/ic_launcher_round"
    android:supportsRtl="true"
    android:configChanges="keyboardHidden|orientation|screenSize"
    android:theme="@style/AppTheme">
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.