شظايا الروبوت.الاحتفاظ بـ AsyncTask أثناء تدوير الشاشة أو تغيير التكوين

StackOverflow https://stackoverflow.com/questions/8417885

سؤال

أنا أعمل على تطبيق هاتف ذكي/جهاز لوحي، باستخدام ملف APK واحد فقط، وأقوم بتحميل الموارد حسب الحاجة اعتمادًا على حجم الشاشة، ويبدو أن أفضل خيار للتصميم هو استخدام الأجزاء عبر قائمة التحكم بالوصول (ACL).

كان هذا التطبيق يعمل بشكل جيد حتى الآن ويعتمد فقط على النشاط.هذه فئة وهمية لكيفية التعامل مع AsyncTasks وProgressDialogs في الأنشطة من أجل جعلها تعمل حتى عند تدوير الشاشة أو حدوث تغيير في التكوين أثناء الاتصال.

لن أغير البيان لتجنب إعادة إنشاء النشاط، هناك العديد من الأسباب التي تجعلني لا أرغب في القيام بذلك، ولكن بشكل رئيسي لأن المستندات الرسمية تقول إنه غير موصى به وقد تمكنت من الاستغناء عنه حتى الآن، لذا يرجى عدم التوصية بذلك طريق.

public class Login extends Activity {

    static ProgressDialog pd;
    AsyncTask<String, Void, Boolean> asyncLoginThread;

    @Override
    public void onCreate(Bundle icicle) {
        super.onCreate(icicle);
        setContentView(R.layout.login);
        //SETUP UI OBJECTS
        restoreAsyncTask();
    }

    @Override
    public Object onRetainNonConfigurationInstance() {
        if (pd != null) pd.dismiss();
        if (asyncLoginThread != null) return (asyncLoginThread);
        return super.onRetainNonConfigurationInstance();
    }

    private void restoreAsyncTask();() {
        pd = new ProgressDialog(Login.this);
        if (getLastNonConfigurationInstance() != null) {
            asyncLoginThread = (AsyncTask<String, Void, Boolean>) getLastNonConfigurationInstance();
            if (asyncLoginThread != null) {
                if (!(asyncLoginThread.getStatus()
                        .equals(AsyncTask.Status.FINISHED))) {
                    showProgressDialog();
                }
            }
        }
    }

    public class LoginThread extends AsyncTask<String, Void, Boolean> {
        @Override
        protected Boolean doInBackground(String... args) {
            try {
                //Connect to WS, recieve a JSON/XML Response
                //Place it somewhere I can use it.
            } catch (Exception e) {
                return true;
            }
            return true;
        }

        protected void onPostExecute(Boolean result) {
            if (result) {
                pd.dismiss();
                //Handle the response. Either deny entry or launch new Login Succesful Activity
            }
        }
    }
}

يعمل هذا الرمز بشكل جيد، ولدي حوالي 10.000 مستخدم دون شكوى، لذلك يبدو من المنطقي نسخ هذا المنطق في التصميم الجديد القائم على الأجزاء، لكنه بالطبع لا يعمل.

هنا جزء تسجيل الدخول:

public class LoginFragment extends Fragment {

    FragmentActivity parentActivity;
    static ProgressDialog pd;
    AsyncTask<String, Void, Boolean> asyncLoginThread;

    public interface OnLoginSuccessfulListener {
        public void onLoginSuccessful(GlobalContainer globalContainer);
    }

    public void onSaveInstanceState(Bundle outState){
        super.onSaveInstanceState(outState);
        //Save some stuff for the UI State
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //setRetainInstance(true);
        //If I setRetainInstance(true), savedInstanceState is always null. Besides that, when loading UI State, a NPE is thrown when looking for UI Objects.
        parentActivity = getActivity();
    }

    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        try {
            loginSuccessfulListener = (OnLoginSuccessfulListener) activity;
        } catch (ClassCastException e) {
            throw new ClassCastException(activity.toString() + " must implement OnLoginSuccessfulListener");
        }
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        RelativeLayout loginLayout = (RelativeLayout) inflater.inflate(R.layout.login, container, false);
        return loginLayout;
    }

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        //SETUP UI OBJECTS
        if(savedInstanceState != null){
            //Reload UI state. Im doing this properly, keeping the content of the UI objects, not the object it self to avoid memory leaks.
        }
    }

    public class LoginThread extends AsyncTask<String, Void, Boolean> {
            @Override
            protected Boolean doInBackground(String... args) {
                try {
                    //Connect to WS, recieve a JSON/XML Response
                    //Place it somewhere I can use it.
                } catch (Exception e) {
                    return true;
                }
                return true;
            }

            protected void onPostExecute(Boolean result) {
                if (result) {
                    pd.dismiss();
                    //Handle the response. Either deny entry or launch new Login Succesful Activity
                }
            }
        }
    }
}

لا أستطيع استخدام onRetainNonConfigurationInstance() نظرًا لأنه يجب استدعاؤه من النشاط وليس من الجزء، الأمر نفسه ينطبق على getLastNonConfigurationInstance().لقد قرأت بعض الأسئلة المماثلة هنا دون إجابة.

أدرك أن الأمر قد يتطلب بعض العمل لتنظيم هذه الأشياء بشكل صحيح في أجزاء، ومع ذلك، أود الحفاظ على نفس منطق التصميم الأساسي.

ما هي الطريقة الصحيحة للاحتفاظ بـ AsyncTask أثناء تغيير التكوين، وإذا كان لا يزال قيد التشغيل، فاعرضprogressDialog، مع الأخذ في الاعتبار أن AsyncTask هي فئة داخلية للجزء وأن الجزء نفسه هو الذي يستدعي AsyncTask.execute ()؟

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

المحلول

شظايا يمكن أن تجعل هذا أسهل كثيرا. ما عليك سوى استخدام الطريقة incment.setretaininstance (منطقية) للحصول على جزءك مثيل المحتجز عبر تغييرات التكوين. لاحظ أن هذا هو الاستبدال الموصى به ل Activity.onretainnonconfigurationInstance () المستندات.

إذا لسبب ما لا ترغب في استخدام جزء محبوب، فهناك طرق أخرى يمكنك اتخاذها. لاحظ أن كل جزء لديه معرف فريد يتم إرجاعه بواسطة incment.getid () . يمكنك أيضا معرفة ما إذا كانت جزء ممزقة لتغيير التكوين من خلال جزء .getactactivity (). ISChangingConfigurations () . لذلك، عند النقطة التي ستقرر فيها إيقاف ASYNCTASK (في OnStop () أو OnDestrony () على الأرجح)، يمكنك على سبيل المثال، تحقق مما إذا كان التكوين يتغير وإذا كان الأمر كذلك، فقم بالتمسك به في SpareAray ثابت تحت معرف الشظية، ثم في OnCheate () أو OnStart ()، انظر لمعرفة ما إذا كان لديك ASYNCTASK في مجموعة Sparse المتاحة.

نصائح أخرى

أعتقد أنك سوف تتمتع بلدي شاملة للغاية ، على سبيل المثال العمل المفصلة أدناه.

  1. دوران يعمل الحوار على قيد الحياة.
  2. يمكنك إلغاء مهمة الحوار عن طريق الضغط على الزر مرة أخرى (إذا كنت تريد هذا السلوك).
  3. ويستخدم شظايا.
  4. تخطيط جزء تحت النشاط التغييرات بشكل صحيح عندما تدور الجهاز.
  5. هناك مجموعة كاملة من التعليمات البرمجية المصدر تحميل مجموعة المترجمة مسبقا APK لذلك يمكنك أن ترى إذا كان السلوك هو ما تريد.

تحرير

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

باستخدام AsyncTask مع التقدم الحوارات و جهاز الدوران.

حل العاملة!

أنا أخيرا حصلت على كل شيء من العمل.قانون بلدي لديه الميزات التالية:

  1. A Fragment الذي تخطيط التغييرات مع التوجه.
  2. وهو AsyncTask التي يمكنك القيام ببعض الأعمال.
  3. A DialogFragment مما يدل على التقدم المهمة في شريط التقدم (ليس فقط غير محدد الدوار).
  4. دوران يعمل دون انقطاع المهمة أو رفض الحوار.
  5. زر الرجوع يرفض الحوار ويلغي المهمة (يمكنك تغيير هذا السلوك بسهولة إلى حد ما على الرغم من).

أنا لا أعتقد أنه مزيج من workingness يمكن العثور عليها في أي مكان آخر.

الفكرة الأساسية هي كما يلي.هناك MainActivity الطبقة التي تحتوي على واحد جزء - MainFragment. MainFragment وقد تخطيطات مختلفة التوجه الأفقي والرأسي ، setRetainInstance() هي كاذبة جدا أن التخطيط يمكن أن تتغير.وهذا يعني أنه عندما يكون الجهاز تغيير اتجاه ، سواء MainActivity و MainFragment دمرت تماما و صوغه.

بشكل منفصل لدينا MyTask (تمتد من AsyncTask) الذي يعمل كل عمل.ونحن لا يمكن تخزينه في MainFragment لأن هذا سوف يكون تدمير جوجل قد انتقدت استخدام أي شيء مثل setRetainNonInstanceConfiguration().هذا ليس متاح دائما على أي حال و هو الإختراق القبيح في أحسن الأحوال.بدلا من ذلك سوف نقوم بتخزين MyTask في جزء آخر ، DialogFragment دعا TaskFragment. هذا جزء سوف لديك setRetainInstance() تعيين إلى true ، حتى تدور الجهاز هذا جزء لا تدميرها ، MyTask يتم الاحتفاظ.

وأخيرا نحن بحاجة إلى أن أقول TaskFragment الذين إعلام عندما يتم الانتهاء من ذلك ، ونحن نفعل ذلك باستخدام setTargetFragment(<the MainFragment>) عندما نخلق ذلك.عند تدوير الجهاز ، MainFragment دمرت جديد يتم إنشاء مثيل, نستخدم FragmentManager العثور على الحوار (على أساس الوسم) والقيام setTargetFragment(<the new MainFragment>).هذا جميل جدا.

كان هناك اثنين من الأشياء الأخرى التي كنت بحاجة إلى القيام به:الأولى إلغاء مهمة عند الحوار هو رفض و المجموعة الثانية اقالة رسالة إلى null, وإلا فإن الحوار هو غريب رفض عند تدوير الجهاز.

رمز

لن قائمة تخطيطات ، فهي واضحة جدا و يمكنك العثور عليها في المشروع تحميل أدناه.

MainActivity

هذا هو بسيط جدا.أضفت رد في هذا النشاط حتى يعرف أنه عندما تم الانتهاء من المهمة, ولكن قد لا تحتاج ذلك.أساسا أنا فقط أردت أن تظهر جزء-نشاط آلية الاستدعاء لأنه أنيق جدا و ربما لم أر ذلك من قبل.

public class MainActivity extends Activity implements MainFragment.Callbacks
{
    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
    @Override
    public void onTaskFinished()
    {
        // Hooray. A toast to our success.
        Toast.makeText(this, "Task finished!", Toast.LENGTH_LONG).show();
        // NB: I'm going to blow your mind again: the "int duration" parameter of makeText *isn't*
        // the duration in milliseconds. ANDROID Y U NO ENUM? 
    }
}

MainFragment

هو طويل ولكن يستحق كل هذا العناء!

public class MainFragment extends Fragment implements OnClickListener
{
    // This code up to onDetach() is all to get easy callbacks to the Activity. 
    private Callbacks mCallbacks = sDummyCallbacks;

    public interface Callbacks
    {
        public void onTaskFinished();
    }
    private static Callbacks sDummyCallbacks = new Callbacks()
    {
        public void onTaskFinished() { }
    };

    @Override
    public void onAttach(Activity activity)
    {
        super.onAttach(activity);
        if (!(activity instanceof Callbacks))
        {
            throw new IllegalStateException("Activity must implement fragment's callbacks.");
        }
        mCallbacks = (Callbacks) activity;
    }

    @Override
    public void onDetach()
    {
        super.onDetach();
        mCallbacks = sDummyCallbacks;
    }

    // Save a reference to the fragment manager. This is initialised in onCreate().
    private FragmentManager mFM;

    // Code to identify the fragment that is calling onActivityResult(). We don't really need
    // this since we only have one fragment to deal with.
    static final int TASK_FRAGMENT = 0;

    // Tag so we can find the task fragment again, in another instance of this fragment after rotation.
    static final String TASK_FRAGMENT_TAG = "task";

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

        // At this point the fragment may have been recreated due to a rotation,
        // and there may be a TaskFragment lying around. So see if we can find it.
        mFM = getFragmentManager();
        // Check to see if we have retained the worker fragment.
        TaskFragment taskFragment = (TaskFragment)mFM.findFragmentByTag(TASK_FRAGMENT_TAG);

        if (taskFragment != null)
        {
            // Update the target fragment so it goes to this fragment instead of the old one.
            // This will also allow the GC to reclaim the old MainFragment, which the TaskFragment
            // keeps a reference to. Note that I looked in the code and setTargetFragment() doesn't
            // use weak references. To be sure you aren't leaking, you may wish to make your own
            // setTargetFragment() which does.
            taskFragment.setTargetFragment(this, TASK_FRAGMENT);
        }
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState)
    {
        return inflater.inflate(R.layout.fragment_main, container, false);
    }

    @Override
    public void onViewCreated(View view, Bundle savedInstanceState)
    {
        super.onViewCreated(view, savedInstanceState);

        // Callback for the "start task" button. I originally used the XML onClick()
        // but it goes to the Activity instead.
        view.findViewById(R.id.taskButton).setOnClickListener(this);
    }

    @Override
    public void onClick(View v)
    {
        // We only have one click listener so we know it is the "Start Task" button.

        // We will create a new TaskFragment.
        TaskFragment taskFragment = new TaskFragment();
        // And create a task for it to monitor. In this implementation the taskFragment
        // executes the task, but you could change it so that it is started here.
        taskFragment.setTask(new MyTask());
        // And tell it to call onActivityResult() on this fragment.
        taskFragment.setTargetFragment(this, TASK_FRAGMENT);

        // Show the fragment.
        // I'm not sure which of the following two lines is best to use but this one works well.
        taskFragment.show(mFM, TASK_FRAGMENT_TAG);
//      mFM.beginTransaction().add(taskFragment, TASK_FRAGMENT_TAG).commit();
    }

    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data)
    {
        if (requestCode == TASK_FRAGMENT && resultCode == Activity.RESULT_OK)
        {
            // Inform the activity. 
            mCallbacks.onTaskFinished();
        }
    }

TaskFragment

    // This and the other inner class can be in separate files if you like.
    // There's no reason they need to be inner classes other than keeping everything together.
    public static class TaskFragment extends DialogFragment
    {
        // The task we are running.
        MyTask mTask;
        ProgressBar mProgressBar;

        public void setTask(MyTask task)
        {
            mTask = task;

            // Tell the AsyncTask to call updateProgress() and taskFinished() on this fragment.
            mTask.setFragment(this);
        }

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

            // Retain this instance so it isn't destroyed when MainActivity and
            // MainFragment change configuration.
            setRetainInstance(true);

            // Start the task! You could move this outside this activity if you want.
            if (mTask != null)
                mTask.execute();
        }

        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container,
                Bundle savedInstanceState)
        {
            View view = inflater.inflate(R.layout.fragment_task, container);
            mProgressBar = (ProgressBar)view.findViewById(R.id.progressBar);

            getDialog().setTitle("Progress Dialog");

            // If you're doing a long task, you probably don't want people to cancel
            // it just by tapping the screen!
            getDialog().setCanceledOnTouchOutside(false);

            return view;
        }

        // This is to work around what is apparently a bug. If you don't have it
        // here the dialog will be dismissed on rotation, so tell it not to dismiss.
        @Override
        public void onDestroyView()
        {
            if (getDialog() != null && getRetainInstance())
                getDialog().setDismissMessage(null);
            super.onDestroyView();
        }

        // Also when we are dismissed we need to cancel the task.
        @Override
        public void onDismiss(DialogInterface dialog)
        {
            super.onDismiss(dialog);
            // If true, the thread is interrupted immediately, which may do bad things.
            // If false, it guarantees a result is never returned (onPostExecute() isn't called)
            // but you have to repeatedly call isCancelled() in your doInBackground()
            // function to check if it should exit. For some tasks that might not be feasible.
            if (mTask != null) {
                mTask.cancel(false);
            }

            // You don't really need this if you don't want.
            if (getTargetFragment() != null)
                getTargetFragment().onActivityResult(TASK_FRAGMENT, Activity.RESULT_CANCELED, null);
        }

        @Override
        public void onResume()
        {
            super.onResume();
            // This is a little hacky, but we will see if the task has finished while we weren't
            // in this activity, and then we can dismiss ourselves.
            if (mTask == null)
                dismiss();
        }

        // This is called by the AsyncTask.
        public void updateProgress(int percent)
        {
            mProgressBar.setProgress(percent);
        }

        // This is also called by the AsyncTask.
        public void taskFinished()
        {
            // Make sure we check if it is resumed because we will crash if trying to dismiss the dialog
            // after the user has switched to another app.
            if (isResumed())
                dismiss();

            // If we aren't resumed, setting the task to null will allow us to dimiss ourselves in
            // onResume().
            mTask = null;

            // Tell the fragment that we are done.
            if (getTargetFragment() != null)
                getTargetFragment().onActivityResult(TASK_FRAGMENT, Activity.RESULT_OK, null);
        }
    }

MyTask

    // This is a fairly standard AsyncTask that does some dummy work.
    public static class MyTask extends AsyncTask<Void, Void, Void>
    {
        TaskFragment mFragment;
        int mProgress = 0;

        void setFragment(TaskFragment fragment)
        {
            mFragment = fragment;
        }

        @Override
        protected Void doInBackground(Void... params)
        {
            // Do some longish task. This should be a task that we don't really
            // care about continuing
            // if the user exits the app.
            // Examples of these things:
            // * Logging in to an app.
            // * Downloading something for the user to view.
            // * Calculating something for the user to view.
            // Examples of where you should probably use a service instead:
            // * Downloading files for the user to save (like the browser does).
            // * Sending messages to people.
            // * Uploading data to a server.
            for (int i = 0; i < 10; i++)
            {
                // Check if this has been cancelled, e.g. when the dialog is dismissed.
                if (isCancelled())
                    return null;

                SystemClock.sleep(500);
                mProgress = i * 10;
                publishProgress();
            }
            return null;
        }

        @Override
        protected void onProgressUpdate(Void... unused)
        {
            if (mFragment == null)
                return;
            mFragment.updateProgress(mProgress);
        }

        @Override
        protected void onPostExecute(Void unused)
        {
            if (mFragment == null)
                return;
            mFragment.taskFinished();
        }
    }
}

تحميل سبيل المثال المشروع

هنا التعليمات البرمجية المصدر و APK.آسف, ADT أصر على إضافة دعم المكتبة قبل أن اسمحوا لي أن جعل المشروع.أنا متأكد من أنك يمكن إزالته.

لقد قمت مؤخرًا نشرت مقالا وصف كيفية التعامل مع تغييرات التكوين باستخدام الاحتفاظ بها Fragmentس.يحل مشكلة الاحتفاظ ب AsyncTask عبر تغيير التناوب بشكل جيد.

TL;DR هو استخدام المضيف الخاص بك AsyncTask داخل Fragment, ، يتصل setRetainInstance(true) على ال Fragment, ، والإبلاغ عن AsyncTaskتقدم/نتائج العودة إلى ما كانت عليه Activity (أو هو الهدف Fragment, ، إذا اخترت استخدام النهج الموضح بواسطةTimmmm) من خلال الاحتفاظ به Fragment.

اقتراحي الأول هو تجنب الداخلية AsyncTasks, يمكنك قراءة السؤال الذي سألت عن هذا أجوبة: الروبوت:AsyncTask التوصيات:الفصل الخاص أو العام الدراسي ؟

بعد أن بدأت باستخدام غير الداخلية و...الآن أرى الكثير من الفوائد.

والثاني هو الحفاظ على المرجعية الخاصة بك تشغيل AsyncTask في Application الدرجة http://developer.android.com/reference/android/app/Application.html

في كل مرة كنت بدء AsyncTask على تطبيق و عندما ينتهي تعيين إلى null.

عندما يكون جزء/يبدأ النشاط يمكنك التحقق من ما إذا كان أي AsyncTask قيد التشغيل (عن طريق التحقق من إذا كان فارغة أو لا على التطبيق) ثم قم بتعيين مرجع داخل إلى ما تريد (النشاط جزء الخ حتى تتمكن من القيام رد).

هذا سوف يحل مشكلتك:إذا كان لديك فقط 1 AsyncTask تشغيل في أي تحديد الوقت يمكنك إضافة إشارة بسيطة:

AsyncTask<?,?,?> asyncTask = null;

آخر في Aplication على HashMap مع إشارات إليها.

مربع حوار التقدم يمكن اتباع نفس المبدأ.

توصلت إلى طريقة لاستخدام AsyncTaskloaders لهذا الغرض. من السهل الاستخدام وتتطلب IMO أقل من ذلك ..

أساسا يمكنك إنشاء ASYNCTASKLoader مثل هذا: giveacodicetagpre.

ثم في نشاطك يستخدم ASYNCTASKLODER أعلاه عند النقر فوق زر: giveacodicetagpre.

يبدو أن هذا يتعامل مع التوجيه يتغير بشكل جيد وسوف تستمر مهمة الخلفية أثناء الدوران.

بعض الأشياء التي يجب ملاحظتها:

  1. إذا كنت في oncomee you reattach to asynctaskloader ستحصل على ما يسمى مرة أخرى في OnloadFinished () مع النتيجة السابقة (حتى لو كنت قد أخبرت بالفعل أن الطلب كان بالكامل). هذا هو في الواقع سلوك جيد معظم الوقت ولكن في بعض الأحيان يمكن أن يكون صعبا. بينما أتصور أن هناك الكثير من الطرق للتعامل مع هذا ما فعلته هو أنني أسمى Loader.Abandon () في OnloadFinished. ثم أضفت تسجيل الوصول في OnCreate إلى Reattach فقط إلى Loader إذا لم يتم التخلي عنه بالفعل. إذا كنت بحاجة إلى البيانات الناتجة مرة أخرى، فلن ترغب في القيام بذلك. في معظم الحالات تريد البيانات.

    لدي المزيد من التفاصيل حول استخدام هذه المكالمات HTTP هنا

لقد قمت بإنشاء مكتبة مهام خلفية صغيرة جدًا ومفتوحة المصدر والتي تعتمد بشكل كبير على Marshmallow AsyncTask ولكن مع وظائف إضافية مثل:

  1. الاحتفاظ بالمهام تلقائيًا عبر تغييرات التكوين؛
  2. رد اتصال واجهة المستخدم (المستمعين)؛
  3. لا يتم إعادة تشغيل المهمة أو إلغائها عند دوران الجهاز (مثلما تفعل أدوات التحميل)؛

تستخدم المكتبة داخليًا ملف Fragment بدون أي واجهة مستخدم، والتي يتم الاحتفاظ بها عبر تغييرات التكوين (setRetainInstance(true)).

يمكنك العثور عليه على جيثب: https://github.com/NeoTech-Software/Android-Retainable-Tasks

المثال الأساسي (الإصدار 0.2.0):

يحتفظ هذا المثال بالمهمة بشكل كامل، باستخدام كمية محدودة جدًا من التعليمات البرمجية.

مهمة:

private class ExampleTask extends Task<Integer, String> {

    public ExampleTask(String tag){
        super(tag);
    }

    protected String doInBackground() {
        for(int i = 0; i < 100; i++) {
            if(isCancelled()){
                break;
            }
            SystemClock.sleep(50);
            publishProgress(i);
        }
        return "Result";
    }
}

نشاط:

public class Main extends TaskActivityCompat implements Task.Callback {

    @Override
    public void onClick(View view){
        ExampleTask task = new ExampleTask("activity-unique-tag");
        getTaskManager().execute(task, this);
    }

    @Override
    public Task.Callback onPreAttach(Task<?, ?> task) {
        //Restore the user-interface based on the tasks state
        return this; //This Activity implements Task.Callback
    }

    @Override
    public void onPreExecute(Task<?, ?> task) {
        //Task started
    }

    @Override
    public void onPostExecute(Task<?, ?> task) {
        //Task finished
        Toast.makeText(this, "Task finished", Toast.LENGTH_SHORT).show();
    }
}

نهجي هو استخدام نمط تصميم الوفود، بشكل عام، يمكننا عزل منطق الأعمال الفعلي (قراءة البيانات من الإنترنت أو قاعدة البيانات أو على الإطلاق) من ASYNCTASK (المفوض) إلى BusinessDao (المندوب)، في AySnctask.DoBackground () طريقة، منمق المهمة الفعلية إلى BusinessDao، ثم تنفيذ آلية عملية Singleton في BusinessDao، بحيث ستؤدي مكالمة متعددة إلى BusinessDao.DoSomething () فقط المهمة الفعلية التي تعمل في كل مرة وتنتظر نتيجة المهمة. تحتفظ الفكرة بالمندوب (I.E. BusinessDao) أثناء تغيير التكوين، بدلا من المفوض (أي Asynctask).

  1. إنشاء / تنفيذ التطبيق الخاص بنا، والغرض هو إنشاء / تهيئة BusinessDao هنا، بحيث يكون دورة حياة رجال الأعمال لدينا هو تطبيق Scoped، وليس النشاط Scoped، لاحظ أنك تحتاج إلى تغيير Androidmanifest.xml لاستخدام myapplication: giveacodicetagpre

  2. نشاطنا الحالي / تفضيلنا دون تغيير في الغالب، لا تزال تنفذ ASYNCTASK كطبقة داخلية وإشراك ASYNCTASK.EXECUTE () من النشاط / القمالة، والفرق الآن هو ASYNCTASK سوف يفوض المهمة الفعلية إلى BusinessDao، لذلك خلال تغيير التكوين، سيتم تهيئة ASYNCTASK ثانية وتنفيذها، وتنفيذ BusinessDao.Dosomething () مرة أخرى، ومع ذلك، لن تؤدي الدعوة الثانية إلى BusinessDao.Dosomething () مهمة تشغيل جديدة، بدلا من ذلك، في انتظار المهمة الجارية الحالية لإنهاء: giveacodicetagpre

  3. داخل BusinessDao، وينفذ آلية عملية Singleton، على سبيل المثال: giveacodicetagpre

    أنا لست متأكدا 100٪ إذا كان هذا سيعمل، علاوة على ذلك، يجب اعتبار Sample Code Snippet كائتلاف. أنا فقط أحاول أن أعطيك بعض الأدلة من مستوى التصميم. أي ردود فعل أو اقتراحات هي موضع ترحيب وتقدير.

يمكنك جعل ASYNCTASK حقل ثابت.إذا كنت بحاجة إلى سياق، يجب عليك شحن سياق التطبيق الخاص بك.سيؤدي ذلك إلى تجنب تسرب الذاكرة، وإلا فإنك تظل مرجعا إلى نشاطك بالكامل.

إذا وجد أي شخص طريقه إلى هذا الموضوع، فقد وجدت أن الطريقة النظيفة هي تشغيل مهمة Async من ملف app.Service (بدأ بـ START_STICKY) ثم قم بإعادة الإنشاء عبر الخدمات قيد التشغيل لمعرفة ما إذا كانت الخدمة (وبالتالي المهمة غير المتزامنة) لا تزال قيد التشغيل؛

    public boolean isServiceRunning(String serviceClassName) {
    final ActivityManager activityManager = (ActivityManager) Application.getContext().getSystemService(Context.ACTIVITY_SERVICE);
    final List<RunningServiceInfo> services = activityManager.getRunningServices(Integer.MAX_VALUE);

    for (RunningServiceInfo runningServiceInfo : services) {
        if (runningServiceInfo.service.getClassName().equals(serviceClassName)){
            return true;
        }
    }
    return false;
 }

إذا كان كذلك، قم بإعادة إضافة DialogFragment (أو أيًا كان) وإذا لم يكن الأمر كذلك فتأكد من رفض مربع الحوار.

وهذا مهم بشكل خاص إذا كنت تستخدم v4.support.* المكتبات منذ (وقت كتابة هذا التقرير) كانت تعرف مشكلات تتعلق بـ setRetainInstance طريقة وعرض الترحيل. علاوة على ذلك، من خلال عدم الاحتفاظ بالمثيل، يمكنك إعادة إنشاء نشاطك باستخدام مجموعة مختلفة من الموارد (على سبيل المثال.تخطيط عرض مختلف للاتجاه الجديد)

أكتب كود sampl لحل هذه المشكلة

الخطوة الأولى هي جعل فئة التطبيق: giveacodicetagpre.

في Androidmanifest.xml giveacodicetagpre.

code في النشاط: giveacodicetagpre.

عند تغيير اتجاه النشاط المتغير mtask من سياق التطبيق.عند الانتهاء من المهمة يتم تعيين المتغير إلى NULL وإزالة من الذاكرة.

بالنسبة لي بما فيه الكفاية.

إلقاء نظرة على المثال أدناه، وكيفية استخدام جزء المحتجوب للاحتفاظ بمهمة الخلفية: giveacodicetagpre.

الق نظرة هنا.

هناك حل على أساس تيممم حل.

لكني قمت بتحسينه:

  • الآن أصبح الحل قابلاً للتمديد - ما عليك سوى التمديد FragmentAbleToStartTask

  • يمكنك الاستمرار في تشغيل العديد من المهام في نفس الوقت.

    وفي رأيي، الأمر سهل مثل startActivityForResult والحصول على النتيجة

  • يمكنك أيضًا إيقاف مهمة قيد التشغيل والتحقق مما إذا كانت مهمة معينة قيد التشغيل

اسف للغتى الانجليزيه

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top