سؤال

قرأت الكثير عن كيفية حفظ حالة المثيل الخاصة بي أو كيفية التعامل مع نشاطي الذي تم تدميره أثناء دوران الشاشة.

يبدو أن هناك الكثير من الاحتمالات ، لكنني لم أكن أتعرف على أي واحد يعمل بشكل أفضل لاسترداد نتائج Asynctask.

لدي بعض المخالفات المتزامنة التي بدأت ببساطة مرة أخرى واتصل isFinishing() طريقة النشاط وإذا كان النشاط ينتهي ، فلن يقوموا بتحديث أي شيء.

المشكلة هي أن لدي مهمة واحدة تقوم بطلب إلى خدمة ويب يمكن أن تفشل أو تنجح وستؤدي إعادة تشغيل المهمة إلى خسارة مالية للمستخدم.

كيف يمكنك حل هذا؟ ما هي مزايا أو عيوب الحلول الممكنة؟

هل كانت مفيدة؟

المحلول

إن اقتراحي الأول هو التأكد من أنك بحاجة فعليًا لإعادة تعيين نشاطك على دوران الشاشة (السلوك الافتراضي). في كل مرة واجهت مشكلات مع التناوب ، أضفت هذه السمة إلى بلدي <activity> علامة في AndroidManifest.xml ، وكان على ما يرام.

android:configChanges="keyboardHidden|orientation"

يبدو غريبًا ، لكن ما يفعله onConfigurationChanged() الطريقة ، إذا لم تقم بتزويدها ، فهي لا تفعل شيئًا سوى إعادة الإجراءات ، والتي يبدو أنها طريقة كافية تمامًا للتعامل مع الدوران معظم الوقت.

نصائح أخرى

يمكنك التحقق من كيفية التعامل مع AsyncTaskيتغير S والتوجيه في code.google.com/p/shelves. هناك طرق مختلفة للقيام بذلك ، الطريقة التي اخترتها في هذا التطبيق هي إلغاء أي مهمة تشغيل حاليًا ، وإنقاذ حالتها وبدء مهمة جديدة مع الحالة المحفوظة عندما تكون جديدة عندما تكون جديدة Activity أنشئ. من السهل القيام به ، إنه يعمل بشكل جيد وكمكافأة ، فإنه يعتني بإيقاف مهامك عندما يغادر المستخدم التطبيق.

تستطيع ايضا استخذام onRetainNonConfigurationInstance() لتمرير الخاص بك AsyncTask إلى الجديد Activity (كن حذرًا بشأن عدم تسرب السابق Activity بهذه الطريقة رغم ذلك.)

هذا هو السؤال الأكثر إثارة للاهتمام الذي رأيته فيما يتعلق بـ Android !!! في الواقع ، كنت أبحث بالفعل عن الحل خلال الأشهر الماضية. ما زلت لم يتم حلها.

كن حذرا ، ببساطة تجاوز

android:configChanges="keyboardHidden|orientation"

الأشياء ليست كافية.

ضع في اعتبارك الحالة عندما يتلقى المستخدم مكالمة هاتفية أثناء تشغيل Asynctask. تتم معالجة طلبك بالفعل بواسطة Server ، وبالتالي فإن Asynctask تنتظر الاستجابة. في هذه اللحظة ، يذهب تطبيقك إلى الخلفية ، لأن تطبيق الهاتف قد وصل للتو إلى الأمام. قد يقتل نظام التشغيل نشاطك لأنه في الخلفية.

لماذا لا تحافظ دائمًا على إشارة إلى Asynctask الحالية على Singleton المقدمة من Android؟

كلما بدأت المهمة ، على preexecute أو على المنشئ ، تحدد:

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

كلما انتهى الأمر ، تقوم بتعيينه على Null.

وبهذه الطريقة ، لديك دائمًا مرجع يتيح لك القيام بشيء مثل أو oncreate أو onresume كما هو مخصص لمنطقك المحدد:

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

إذا كان NULL تعلم أنه لا يوجد حاليا تشغيل!

:-)

الطريقة الأكثر ملاءمة لذلك هي استخدام جزء للحفاظ على مثيل مهمة ASYNC ، على الدورات.

فيما يلي رابط إلى مثال بسيط للغاية مما يجعل من السهل متابعة دمج هذه التقنية في تطبيقاتك.

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

في Pro android 4. يقترح المؤلف طريقة لطيفة ، يجب عليك استخدامها weak reference.

ملاحظة مرجعية ضعيفة

إلى وجهة نظري ، من الأفضل تخزين Asynctask عبر onRetainNonConfigurationInstance فصله عن كائن النشاط الحالي وربطه بكائن نشاط جديد بعد تغيير الاتجاه. هنا لقد وجدت مثالاً لطيفًا جدًا كيفية العمل مع Asynctask و ProgressDialog.

Android: معالجة الخلفية/opartion async مع تغيير التكوين

للحفاظ على حالات التزامين غير متزامن أثناء عملية الخلفية: يمكنك الحصول على مساعدة من الشظايا.

انظر الخطوات التالية:

الخطوة 1: قم بإنشاء جزء لا يتجزأ من رأسه ، دعنا نقول مهمة الخلفية وأضف فئة مهمة خاصة من Async معها.

الخطوة 2 (خطوة اختيارية): إذا كنت ترغب في وضع مؤشر تحميل أعلى نشاطك ، استخدم الكود أدناه:

الخطوة 3: في نشاطك الرئيسي ، قم بتنفيذ واجهة BackgroundTaskCallbacks المحددة في الخطوة 1

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
}

}

شيء واحد يجب مراعاته هو ما إذا كانت نتيجة Asynctask يجب أن تكون متاحة فقط للنشاط الذي بدأ المهمة. إذا كانت الإجابة بنعم ، إذن إجابة رومان جاي هو الأفضل. إذا كان يجب أن يكون متاحًا للأنشطة الأخرى لتطبيقك ، فعندئذ في onPostExecute يمكنك استخدام LocalBroadcastManager.

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

ستحتاج أيضًا إلى التأكد من أن النشاط يتعامل بشكل صحيح مع الموقف عند إرسال البث أثناء إيقاف النشاط.

الق نظرة على هذا بريد. يتضمن هذا المنشور إجراء عملية تشغيل طويلة الجري وتسرب الذاكرة عندما يحدث دوران الشاشة في تطبيق عينة واحد. تطبيق العينة متاح على المصدر Forge

بلدي الحل.

في حالتي ، لدي سلسلة من المخالفات المعزولة بنفس السياق. كان النشاط الوصول فقط إلى الأول. لإلغاء أي مهمة تشغيل فعلت ما يلي:

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

يمكنك أيضًا إضافة Android: ConfigChanges = "KeyboardHidden | الاتجاه | الشاشات"

إلى مثالك الواضح ، آمل أن يساعد ذلك

 <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">
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top