En iyi yöntem: Yön değiştirme sırasında AsyncTask


151

AsyncTask başka bir iş parçacığında karmaşık görevleri çalıştırmak için harika bir şeydir.

Ancak, çalışırken hala bir yönelim değişikliği veya başka bir yapılandırma değişikliği AsyncTaskolduğunda, akım Activityyok edilir ve yeniden başlatılır. Ve örneği AsyncTaskbu etkinliğe bağlandığından başarısız olur ve "kapatmaya zorla" ileti penceresine neden olur.

Bu nedenle, bu hataları önlemek ve AsyncTask'ın başarısız olmasını önlemek için bir tür "en iyi uygulama" arıyorum.

Şimdiye kadar gördüğüm şey:

  • Yön değişikliklerini devre dışı bırakın. (Bunu nasıl ele almanız gerektiği kesin değil.)
  • Görevin devam etmesine izin vermek ve yeni etkinlik örneği ile güncellemek onRetainNonConfigurationInstance
  • Sadece Activityimha edildiğinde görevi iptal etmek ve yeniden Activityoluşturulduğunda yeniden başlatmak .
  • Görevi etkinlik örneği yerine uygulama sınıfına bağlama.
  • "Raflar" projesinde kullanılan bazı yöntemler (onRestoreInstanceState üzerinden)

Bazı kod örnekleri:

Ekran dönüşü sırasında Android AsyncTasks, Bölüm I ve Bölüm II

ShelvesActivity.java

Sorunu en iyi çözen ve uygulaması kolay olan en iyi yaklaşımı bulmama yardımcı olabilir misiniz? Kodun kendisi de önemlidir, çünkü bunu nasıl doğru bir şekilde çözeceğimizi bilmiyorum.


Bir kopya var, bu stackoverflow.com/questions/4584015/… adresini kontrol edin .
TeaCupApp

Bu Mark Murphy'nin Blogundan ... AsyncTask ve ScreenRotation yardımcı olabilir ... link
Gopal

Bu eski bir yazı olmasına rağmen, bu IMO, çok daha kolay (ve daha iyi?) Bir yaklaşımdır.
DroidDev

Belgelerin neden bu kadar önemsiz durumlar hakkında konuşmadığını merak ediyorum.
Sreekanth Karumanaghat

Yanıtlar:


140

Do DEĞİL kullanmak android:configChangesbu sorunu gidermek için. Bu çok kötü bir uygulamadır.

Do DEĞİL kullanmak Activity#onRetainNonConfigurationInstance()ya. Bu daha az modülerdir ve Fragmenttabanlı uygulamalar için çok uygun değildir .

Sen edebilirsiniz benim makaleyi okuyun tutulan kullanarak değişiklikler yapılandırmayı nasıl işleneceğini açıklayan Fragments. AsyncTaskBir rotasyon değişikliğinde bir değişiklik tutulması sorununu güzelce çözer . Sen temelde ev sahipliği yapmaya gerek AsyncTaskiçeride a Fragment, çağrı setRetainInstance(true)üzerine Fragmentve rapor AsyncTasköyle geri 'ın ilerleme / sonuçlarını Activitymuhafaza aracılığıyla Fragment.


26
Güzel fikir, ama herkes Fragment kullanmıyor. Parçalar bir seçenek olmadan çok önce yazılmış çok sayıda eski kod var.
Scott Biggs

14
@ ScottBiggs Parçaları, Android 1.6'ya kadar tüm destek kütüphanesi aracılığıyla edinilebilir. Ve hala aktif olarak kullanılmakta olan ve destek kütüphanesi Parçalarını kullanmakta sorun yaşayabilecek bazı eski kodlara bir örnek verebilir misiniz? Çünkü dürüstçe bunun bir sorun olduğunu düşünmüyorum.
Alex Lockwood

4
@tactoth Yanıtımda bu sorunları ele alma gereğini hissetmedim, çünkü insanların% 99,9'u artık kullanmıyor TabActivity. Dürüst olmak gerekirse, neden bu konu hakkında konuştuğumuza emin değilim ... herkes Fragmentbunun yolunun olduğunu kabul ediyor . :)
Alex Lockwood

2
Yuvalanmış bir Parçadan AsyncTask çağrılırsa ne olur?
Eduardo Naveda

3
@AlexLockwood - "Herkes Parçaların gidilecek yol olduğunu kabul ediyor." Squared'deki Devs aynı fikirde değil!
JBeckton

36

Ben genellikle AsyncTasks yangın yayın Intents .onPostExecute () geri arama, böylece doğrudan onları başlatan etkinliği değiştirmek yok çözerek. Faaliyetler bu yayınları dinamik BroadcastReceivers ile dinler ve buna göre hareket eder.

Bu şekilde AsyncTasks, sonuçlarını işleyen belirli Etkinlik örneğini önemsemek zorunda kalmaz. İş bittikten sonra sadece "bağırıyorlar" ve eğer bir Faaliyet o zaman civarındaysa (görevin sonuçlarıyla ilgilenen (aktif ve odaklanmış / devam ettirilmiş durumundaysa), o zaman işlenir.

Çalışma zamanının yayını işlemesi gerektiğinden, bu biraz daha fazla yük içerir, ancak genellikle umursamıyorum. Ben varsayılan sistem geniş bir yerine LocalBroadcastManager kullanarak şeyler biraz hızlandırır düşünüyorum.


6
u cevaba bir örnek ekleyebilirseniz daha yararlı olacaktır
Sankar V

1
Bence bu, faaliyetler ve fragmanlar arasında daha az bağlantı sağlayan bir çözüm
Roger Garzon Nieto

7
Bu bir çözümün parçası olabilir, ancak AsyncTask'ın oryantasyon değişikliğinden sonra yeniden yaratılması sorununu çözecek gibi görünmüyor.
miguel

4
Şanssızsanız ve yayın sırasında hiçbir etkinlik bulunmuyorsa ne olur? (yani orta
Sam

24

Aşağıda, Fragmentçalışma zamanı yapılandırma değişikliklerini (kullanıcı ekranı döndürdüğü gibi) işlemek için bir kullanan bir AsyncTask örneği verilmiştir setRetainInstance(true). Belirleyici (düzenli olarak güncellenen) bir ilerleme çubuğu da gösterilir.

Örnek, kısmen, bir Yapılandırma Değişikliği Sırasında Nesneyi Saklamak için resmi belgelere dayanmaktadır .

Bu örnekte, arka plan iş parçacığı gerektiren iş, yalnızca bir görüntünün internetten kullanıcı arayüzüne yüklenmesidir.

Alex Lockwood, AsyncTasks ile çalışma zamanı yapılandırma değişikliklerini işlemeye gelince "Tutulan Parça" kullanarak en iyi yöntem olduğu doğru görünüyor. onRetainNonConfigurationInstance()Android Studio'da Lint'te kullanımdan kaldırıldı. Resmi dokümanlar , Yapılandırma Değişikliğini Kendinizi Kullanarak android:configChanges, bizi uyarıyor ...

Yapılandırma değişikliğini kendiniz halletmek, alternatif kaynakların kullanımını çok daha zor hale getirebilir, çünkü sistem bunları sizin için otomatik olarak uygulamaz. Bu teknik, bir yapılandırma değişikliği nedeniyle yeniden başlatmalardan kaçınmanız gerektiğinde son çare olarak düşünülmelidir ve çoğu uygulama için önerilmez.

Sonra bir arka plan iş parçacığı için hiç bir AsyncTask kullanmak gerekip gerekmediği sorunu var.

AsyncTask için resmi başvuru uyarıyor ...

AsyncTasks ideal olarak kısa işlemler için kullanılmalıdır (en fazla birkaç saniye). İplikleri uzun süre çalışmaya devam etmeniz gerekiyorsa, java.util.concurrent pacakge tarafından sağlanan çeşitli API'ları kullanmanız önemle tavsiye edilir. Yürütücü, ThreadPoolExecutor ve FutureTask.

Alternatif olarak, bir hizmet, yükleyici (bir CursorLoader veya AsyncTaskLoader kullanarak) veya senkronize olmayan işlemleri gerçekleştirmek için içerik sağlayıcısı kullanılabilir.

Gönderinin geri kalanını:

  • Prosedür; ve
  • Yukarıdaki prosedür için tüm kod.

Prosedür

  1. Bir etkinliğin iç sınıfı olarak temel bir AsyncTask ile başlayın (bir iç sınıf olması gerekmez, ancak muhtemelen uygun olacaktır). Bu aşamada AsyncTask çalışma zamanı yapılandırma değişikliklerini işlemez.

    public class ThreadsActivity extends ActionBarActivity {
    
        private ImageView mPictureImageView;
    
        private class LoadImageFromNetworkAsyncTask
                              extends AsyncTask<String, Void, Bitmap> {
    
            @Override
            protected Bitmap doInBackground(String... urls) {
                return loadImageFromNetwork(urls[0]);
            }
    
            @Override
            protected void onPostExecute(Bitmap bitmap) {
                mPictureImageView.setImageBitmap(bitmap);
            }
        }
    
        /**
         * Requires in AndroidManifext.xml
         *  <uses-permission android:name="android.permission.INTERNET" />
         */
        private Bitmap loadImageFromNetwork(String url) {
            Bitmap bitmap = null;
            try {
                bitmap = BitmapFactory.decodeStream((InputStream)
                                              new URL(url).getContent());
            } catch (Exception e) {
                e.printStackTrace();
            }
            return bitmap;
        }
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_threads);
    
            mPictureImageView =
                (ImageView) findViewById(R.id.imageView_picture);
        }
    
        public void getPicture(View view) {
            new LoadImageFromNetworkAsyncTask()
                .execute("http://i.imgur.com/SikTbWe.jpg");
        }
    
    }
  2. Fragement sınıfını genişleten ve kendi kullanıcı arayüzüne sahip olmayan bir iç içe sınıf RetainedFragment ekleyin. Bu Parçanın onCreate olayına setRetainInstance (true) öğesini ekleyin. Verilerinizi ayarlamak ve almak için prosedürler sağlayın.

    public class ThreadsActivity extends Activity {
    
        private ImageView mPictureImageView;
        private RetainedFragment mRetainedFragment = null;
        ...
    
        public static class RetainedFragment extends Fragment {
    
            private Bitmap mBitmap;
    
            @Override
            public void onCreate(Bundle savedInstanceState) {
                super.onCreate(savedInstanceState);
    
                // The key to making data survive
                // runtime configuration changes.
                setRetainInstance(true);
            }
    
            public Bitmap getData() {
                return this.mBitmap;
            }
    
            public void setData(Bitmap bitmapToRetain) {
                this.mBitmap = bitmapToRetain;
            }
        }
    
        private class LoadImageFromNetworkAsyncTask
                        extends AsyncTask<String, Integer,Bitmap> {
        ....
  3. En dıştaki Activity sınıfının onCreate () yöntemi RetainedFragment öğesini işler: Zaten varsa (Activity yeniden başlatılırsa); yoksa oluşturun ve ekleyin; Ardından, zaten varsa, RetainedFragment'tan veri alın ve kullanıcı arayüzünüzü bu verilerle ayarlayın.

    public class ThreadsActivity extends Activity {
    
        ...
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_threads);
    
            final String retainedFragmentTag = "RetainedFragmentTag";
    
            mPictureImageView =
                      (ImageView) findViewById(R.id.imageView_picture);
            mLoadingProgressBar =
                    (ProgressBar) findViewById(R.id.progressBar_loading);
    
            // Find the RetainedFragment on Activity restarts
            FragmentManager fm = getFragmentManager();
            // The RetainedFragment has no UI so we must
            // reference it with a tag.
            mRetainedFragment =
              (RetainedFragment) fm.findFragmentByTag(retainedFragmentTag);
    
            // if Retained Fragment doesn't exist create and add it.
            if (mRetainedFragment == null) {
    
                // Add the fragment
                mRetainedFragment = new RetainedFragment();
                fm.beginTransaction()
                    .add(mRetainedFragment, retainedFragmentTag).commit();
    
            // The Retained Fragment exists
            } else {
    
                mPictureImageView
                    .setImageBitmap(mRetainedFragment.getData());
            }
        }
  4. Kullanıcı arayüzünden AsyncTask'ı başlatın

    public void getPicture(View view) {
        new LoadImageFromNetworkAsyncTask().execute(
                "http://i.imgur.com/SikTbWe.jpg");
    }
  5. Belirli bir ilerleme çubuğu ekleyin ve kodlayın:

    • Kullanıcı arayüzü düzenine bir ilerleme çubuğu ekleyin;
    • Etkinlik oncreate ();
    • Sürecin başında ve sonunda görünür ve görünmez hale getirin;
    • OnProgressUpdate'de kullanıcı arayüzüne rapor verme ilerlemesini tanımlayın.
    • AsyncTask 2nd Generic parametresini Void'den ilerleme güncelleştirmelerini işleyebilecek bir türe değiştirin (örn. Tamsayı).
    • doInBackground () içindeki düzenli noktalarda publishProgress.

Yukarıdaki prosedürün tüm kodu

Etkinlik Düzeni.

<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.mysecondapp.ThreadsActivity">

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:paddingBottom="@dimen/activity_vertical_margin"
        android:paddingLeft="@dimen/activity_horizontal_margin"
        android:paddingRight="@dimen/activity_horizontal_margin"
        android:paddingTop="@dimen/activity_vertical_margin">

        <ImageView
            android:id="@+id/imageView_picture"
            android:layout_width="300dp"
            android:layout_height="300dp"
            android:background="@android:color/black" />

        <Button
            android:id="@+id/button_get_picture"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentLeft="true"
            android:layout_alignParentStart="true"
            android:layout_below="@id/imageView_picture"
            android:onClick="getPicture"
            android:text="Get Picture" />

        <Button
            android:id="@+id/button_clear_picture"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignBottom="@id/button_get_picture"
            android:layout_toEndOf="@id/button_get_picture"
            android:layout_toRightOf="@id/button_get_picture"
            android:onClick="clearPicture"
            android:text="Clear Picture" />

        <ProgressBar
            android:id="@+id/progressBar_loading"
            style="?android:attr/progressBarStyleHorizontal"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_below="@id/button_get_picture"
            android:progress="0"
            android:indeterminateOnly="false"
            android:visibility="invisible" />

    </RelativeLayout>
</ScrollView>

Etkinlik: alt sınıf AsyncTask iç sınıfı; çalışma zamanı yapılandırma değişikliklerini işleyen alt sınıf RetainedFragment iç sınıfı (ör. kullanıcı ekranı döndürdüğünde); ve düzenli aralıklarla belirli bir ilerleme çubuğu güncellemesi. ...

public class ThreadsActivity extends Activity {

    private ImageView mPictureImageView;
    private RetainedFragment mRetainedFragment = null;
    private ProgressBar mLoadingProgressBar;

    public static class RetainedFragment extends Fragment {

        private Bitmap mBitmap;

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

            // The key to making data survive runtime configuration changes.
            setRetainInstance(true);
        }

        public Bitmap getData() {
            return this.mBitmap;
        }

        public void setData(Bitmap bitmapToRetain) {
            this.mBitmap = bitmapToRetain;
        }
    }

    private class LoadImageFromNetworkAsyncTask extends AsyncTask<String,
            Integer, Bitmap> {

        @Override
        protected Bitmap doInBackground(String... urls) {
            // Simulate a burdensome load.
            int sleepSeconds = 4;
            for (int i = 1; i <= sleepSeconds; i++) {
                SystemClock.sleep(1000); // milliseconds
                publishProgress(i * 20); // Adjust for a scale to 100
            }

            return com.example.standardapplibrary.android.Network
                    .loadImageFromNetwork(
                    urls[0]);
        }

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

        @Override
        protected void onPostExecute(Bitmap bitmap) {
            publishProgress(100);
            mRetainedFragment.setData(bitmap);
            mPictureImageView.setImageBitmap(bitmap);
            mLoadingProgressBar.setVisibility(View.INVISIBLE);
            publishProgress(0);
        }
    }

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

        final String retainedFragmentTag = "RetainedFragmentTag";

        mPictureImageView = (ImageView) findViewById(R.id.imageView_picture);
        mLoadingProgressBar = (ProgressBar) findViewById(R.id.progressBar_loading);

        // Find the RetainedFragment on Activity restarts
        FragmentManager fm = getFragmentManager();
        // The RetainedFragment has no UI so we must reference it with a tag.
        mRetainedFragment = (RetainedFragment) fm.findFragmentByTag(
                retainedFragmentTag);

        // if Retained Fragment doesn't exist create and add it.
        if (mRetainedFragment == null) {

            // Add the fragment
            mRetainedFragment = new RetainedFragment();
            fm.beginTransaction().add(mRetainedFragment,
                                      retainedFragmentTag).commit();

            // The Retained Fragment exists
        } else {

            mPictureImageView.setImageBitmap(mRetainedFragment.getData());
        }
    }

    public void getPicture(View view) {
        mLoadingProgressBar.setVisibility(View.VISIBLE);
        new LoadImageFromNetworkAsyncTask().execute(
                "http://i.imgur.com/SikTbWe.jpg");
    }

    public void clearPicture(View view) {
        mRetainedFragment.setData(null);
        mPictureImageView.setImageBitmap(null);
    }
}

Bu örnekte, gerçek işi yapan kütüphane işlevi (yukarıda açık paket com.example.standardapplibrary.android.Network paket öneki ile başvurulmaktadır) ...

public static Bitmap loadImageFromNetwork(String url) {
    Bitmap bitmap = null;
    try {
        bitmap = BitmapFactory.decodeStream((InputStream) new URL(url)
                .getContent());
    } catch (Exception e) {
        e.printStackTrace();
    }
    return bitmap;
}

Arka plan görevinizin gerektirdiği izinleri AndroidManifest.xml dosyasına ekleyin ...

<manifest>
...
    <uses-permission android:name="android.permission.INTERNET" />

Etkinliğinizi AndroidManifest.xml dosyasına ekleyin ...

<manifest>
...
    <application>
        <activity
            android:name=".ThreadsActivity"
            android:label="@string/title_activity_threads"
            android:parentActivityName=".MainActivity">
            <meta-data
                android:name="android.support.PARENT_ACTIVITY"
                android:value="com.example.mysecondapp.MainActivity" />
        </activity>

Harika. Bu konuda bir blog yazmalısınız.
Akh

2
@AKh. Cevabımın Stackoverflow'da çok fazla yer kapladığını mı söylemek istiyorsun?
John Bentley

1
Bence sadece harika bir cevabınız var ve bir blog yazmalısınız! =) @JohnBentley
Sandy D.

@ SandyD.yesterday Olumlu yorum için teşekkürler. Umarım bunu amaçlamıştır.
John Bentley

Ben de bunun harika bir cevap olduğunu düşündüm ve ben de böyle yorumladım. Bunun gibi çok eksiksiz cevaplar harika !!
LeonardoSibela

3

Son zamanlarda, burada iyi bir çözüm buldum . RetainConfiguration üzerinden bir görev nesnesinin kaydedilmesine dayanır. Benim görüşüme göre, çözüm çok zarif ve benim için kullanmaya başladım. Sadece zaman uyumsuzluğunuzu temel görevden iç içe yerleştirmeniz gerekir ve hepsi bu.


Bu ilginç cevap için çok teşekkür ederim. İlgili soruda belirtilenlere ek olarak iyi bir çözümdür.
caw

5
Ne yazık ki, bu çözüm kullanımdan kaldırılmış yöntemler kullanmaktadır.
Damien

3

@Alex Lockwood yanıtı ve @William & @quickdraw mcgraw bu yazıdaki cevaplara dayanarak: Etkinlik / parça duraklatıldığında İşleyici mesajlarını nasıl ele alacağım, genel bir çözüm yazdım.

Bu şekilde döndürme işlenir ve zaman uyumsuz görev yürütme sırasında etkinlik arka plana giderse, etkinlik geri çağrıları alır (onPreExecute, onProgressUpdate, onPostExecute & onCancelled), geri alındığında IllegalStateException nasıl işlenmez (bkz. etkinlik / parça duraklatıldığında mesajlar ).

Aynı şeylere sahip olmak harika ama AsyncTask gibi genel argüman türleriyle (örneğin: AsyncTaskFragment <Params, Progress, Sonuç>), ancak hızlı bir şekilde yapmayı başaramadım ve şu anda hiç vaktim yok. Herkes iyileştirme yapmak istiyorsanız, lütfen çekinmeyin!

Kod:

import android.app.Activity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Message;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v7.app.AppCompatActivity;

public class AsyncTaskFragment extends Fragment {

    /* ------------------------------------------------------------------------------------------ */
    // region Classes & Interfaces

    public static abstract class Task extends AsyncTask<Object, Object, Object> {

        private AsyncTaskFragment _fragment;

        private void setFragment(AsyncTaskFragment fragment) {

            _fragment = fragment;
        }

        @Override
        protected final void onPreExecute() {

            // Save the state :
            _fragment.setRunning(true);

            // Send a message :
            sendMessage(ON_PRE_EXECUTE_MESSAGE, null);
        }

        @Override
        protected final void onPostExecute(Object result) {

            // Save the state :
            _fragment.setRunning(false);

            // Send a message :
            sendMessage(ON_POST_EXECUTE_MESSAGE, result);
        }

        @Override
        protected final void onProgressUpdate(Object... values) {

            // Send a message :
            sendMessage(ON_PROGRESS_UPDATE_MESSAGE, values);
        }

        @Override
        protected final void onCancelled() {

            // Save the state :
            _fragment.setRunning(false);

            // Send a message :
            sendMessage(ON_CANCELLED_MESSAGE, null);
        }

        private void sendMessage(int what, Object obj) {

            Message message = new Message();
            message.what = what;
            message.obj = obj;

            Bundle data = new Bundle(1);
            data.putString(EXTRA_FRAGMENT_TAG, _fragment.getTag());
            message.setData(data);

            _fragment.handler.sendMessage(message);
        }
    }

    public interface AsyncTaskFragmentListener {

        void onPreExecute(String fragmentTag);
        void onProgressUpdate(String fragmentTag, Object... progress);
        void onCancelled(String fragmentTag);
        void onPostExecute(String fragmentTag, Object result);
    }

    private static class AsyncTaskFragmentPauseHandler extends PauseHandler {

        @Override
        final protected void processMessage(Activity activity, Message message) {

            switch (message.what) {

                case ON_PRE_EXECUTE_MESSAGE : { ((AsyncTaskFragmentListener)activity).onPreExecute(message.getData().getString(EXTRA_FRAGMENT_TAG)); break; }
                case ON_POST_EXECUTE_MESSAGE : { ((AsyncTaskFragmentListener)activity).onPostExecute(message.getData().getString(EXTRA_FRAGMENT_TAG), message.obj); break; }
                case ON_PROGRESS_UPDATE_MESSAGE : { ((AsyncTaskFragmentListener)activity).onProgressUpdate(message.getData().getString(EXTRA_FRAGMENT_TAG), ((Object[])message.obj)); break; }
                case ON_CANCELLED_MESSAGE : { ((AsyncTaskFragmentListener)activity).onCancelled(message.getData().getString(EXTRA_FRAGMENT_TAG)); break; }
            }
        }
    }

    // endregion
    /* ------------------------------------------------------------------------------------------ */



    /* ------------------------------------------------------------------------------------------ */
    // region Attributes

    private Task _task;
    private AsyncTaskFragmentListener _listener;
    private boolean _running = false;

    private static final String EXTRA_FRAGMENT_TAG = "EXTRA_FRAGMENT_TAG";
    private static final int ON_PRE_EXECUTE_MESSAGE = 0;
    private static final int ON_POST_EXECUTE_MESSAGE = 1;
    private static final int ON_PROGRESS_UPDATE_MESSAGE = 2;
    private static final int ON_CANCELLED_MESSAGE = 3;

    private AsyncTaskFragmentPauseHandler handler = new AsyncTaskFragmentPauseHandler();

    // endregion
    /* ------------------------------------------------------------------------------------------ */



    /* ------------------------------------------------------------------------------------------ */
    // region Getters

    public AsyncTaskFragmentListener getListener() { return _listener; }
    public boolean isRunning() { return _running; }

    // endregion
    /* ------------------------------------------------------------------------------------------ */



    /* ------------------------------------------------------------------------------------------ */
    // region Setters

    public void setTask(Task task) {

        _task = task;
        _task.setFragment(this);
    }

    public void setListener(AsyncTaskFragmentListener listener) { _listener = listener; }
    private void setRunning(boolean running) { _running = running; }

    // endregion
    /* ------------------------------------------------------------------------------------------ */



    /* ------------------------------------------------------------------------------------------ */
    // region Fragment lifecycle

    @Override
    public void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);
        setRetainInstance(true);
    }

    @Override
    public void onResume() {

        super.onResume();
        handler.resume(getActivity());
    }

    @Override
    public void onPause() {

        super.onPause();
        handler.pause();
    }

    @Override
    public void onAttach(Activity activity) {

        super.onAttach(activity);
        _listener = (AsyncTaskFragmentListener) activity;
    }

    @Override
    public void onDetach() {

        super.onDetach();
        _listener = null;
    }

    // endregion
    /* ------------------------------------------------------------------------------------------ */



    /* ------------------------------------------------------------------------------------------ */
    // region Utils

    public void execute(Object... params) {

        _task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, params);
    }

    public void cancel(boolean mayInterruptIfRunning) {

        _task.cancel(mayInterruptIfRunning);
    }

    public static AsyncTaskFragment getRetainedOrNewFragment(AppCompatActivity activity, String fragmentTag) {

        FragmentManager fm = activity.getSupportFragmentManager();
        AsyncTaskFragment fragment = (AsyncTaskFragment) fm.findFragmentByTag(fragmentTag);

        if (fragment == null) {

            fragment = new AsyncTaskFragment();
            fragment.setListener( (AsyncTaskFragmentListener) activity);
            fm.beginTransaction().add(fragment, fragmentTag).commit();
        }

        return fragment;
    }

    // endregion
    /* ------------------------------------------------------------------------------------------ */
}

PauseHandler'a ihtiyacınız olacak:

import android.app.Activity;
import android.os.Handler;
import android.os.Message;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * Message Handler class that supports buffering up of messages when the activity is paused i.e. in the background.
 *
 * /programming/8040280/how-to-handle-handler-messages-when-activity-fragment-is-paused
 */
public abstract class PauseHandler extends Handler {

    /**
     * Message Queue Buffer
     */
    private final List<Message> messageQueueBuffer = Collections.synchronizedList(new ArrayList<Message>());

    /**
     * Flag indicating the pause state
     */
    private Activity activity;

    /**
     * Resume the handler.
     */
    public final synchronized void resume(Activity activity) {
        this.activity = activity;

        while (messageQueueBuffer.size() > 0) {
            final Message msg = messageQueueBuffer.get(0);
            messageQueueBuffer.remove(0);
            sendMessage(msg);
        }
    }

    /**
     * Pause the handler.
     */
    public final synchronized void pause() {
        activity = null;
    }

    /**
     * Store the message if we have been paused, otherwise handle it now.
     *
     * @param msg   Message to handle.
     */
    @Override
    public final synchronized void handleMessage(Message msg) {
        if (activity == null) {
            final Message msgCopy = new Message();
            msgCopy.copyFrom(msg);
            messageQueueBuffer.add(msgCopy);
        } else {
            processMessage(activity, msg);
        }
    }

    /**
     * Notification message to be processed. This will either be directly from
     * handleMessage or played back from a saved message when the activity was
     * paused.
     *
     * @param activity  Activity owning this Handler that isn't currently paused.
     * @param message   Message to be handled
     */
    protected abstract void processMessage(Activity activity, Message message);
}

Örnek kullanım:

public class TestActivity extends AppCompatActivity implements AsyncTaskFragmentListener {

    private final static String ASYNC_TASK_FRAGMENT_A = "ASYNC_TASK_FRAGMENT_A";
    private final static String ASYNC_TASK_FRAGMENT_B = "ASYNC_TASK_FRAGMENT_B";

    @Override
    public void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        Button testButton = (Button) findViewById(R.id.test_button);
        final AsyncTaskFragment fragment = AsyncTaskFragment.getRetainedOrNewFragment(TestActivity.this, ASYNC_TASK_FRAGMENT_A);

        testButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {

                if(!fragment.isRunning()) {

                    fragment.setTask(new Task() {

                        @Override
                        protected Object doInBackground(Object... objects) {

                            // Do your async stuff

                            return null;
                        }
                    });

                    fragment.execute();
                }
            }
        });
    }

    @Override
    public void onPreExecute(String fragmentTag) {}

    @Override
    public void onProgressUpdate(String fragmentTag, Float percent) {}

    @Override
    public void onCancelled(String fragmentTag) {}

    @Override
    public void onPostExecute(String fragmentTag, Object result) {

        switch (fragmentTag) {

            case ASYNC_TASK_FRAGMENT_A: {

                // Handle ASYNC_TASK_FRAGMENT_A
                break;
            }
            case ASYNC_TASK_FRAGMENT_B: {

                // Handle ASYNC_TASK_FRAGMENT_B
                break;
            }
        }
    }
}

3

Bunun için Yükleyiciler'i kullanabilirsiniz. Doc'u buradan kontrol edin


2
Yükleyiciler artık Android API 28'den itibaren kullanımdan kaldırılmıştır (bağlantının size söyleyeceği gibi).
Sumit

Yükleyiciler kullanımdan kaldırılmadı, sadece bunları nasıl değiştirdiğiniz değişti
EdgeDev

2

Parçalardan kaçmak isteyenler için, onRetainCustomNonConfigurationInstance () ve bazı kabloları kullanarak yön değiştirme üzerinde çalışan AsyncTask'ı koruyabilirsiniz .

(Bu yöntemin kullanımdan kaldırılmış onRetainNonConfigurationInstance () yöntemine alternatif olduğuna dikkat edin .)

Bu çözümden sıkça bahsedilmiyor gibi görünüyor. Açıklamak için basit bir çalışma örneği yazdım.

Şerefe!

public class MainActivity extends AppCompatActivity {

private TextView result;
private Button run;
private AsyncTaskHolder asyncTaskHolder;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    result = (TextView) findViewById(R.id.textView_result);
    run = (Button) findViewById(R.id.button_run);
    asyncTaskHolder = getAsyncTaskHolder();
    run.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            asyncTaskHolder.execute();
        }
    });
}

private AsyncTaskHolder getAsyncTaskHolder() {
    if (this.asyncTaskHolder != null) {
        return asyncTaskHolder;
    }
    //Not deprecated. Get the same instance back.
    Object instance = getLastCustomNonConfigurationInstance();

    if (instance == null) {
        instance = new AsyncTaskHolder();
    }
    if (!(instance instanceof ActivityDependant)) {
        Log.e("", instance.getClass().getName() + " must implement ActivityDependant");
    }
    return (AsyncTaskHolder) instance;
}

@Override
//Not deprecated. Save the object containing the running task.
public Object onRetainCustomNonConfigurationInstance() {
    return asyncTaskHolder;
}

@Override
protected void onStart() {
    super.onStart();
    if (asyncTaskHolder != null) {
        asyncTaskHolder.attach(this);
    }
}

@Override
protected void onStop() {
    super.onStop();
    if (asyncTaskHolder != null) {
        asyncTaskHolder.detach();
    }
}

void updateUI(String value) {
    this.result.setText(value);
}

interface ActivityDependant {

    void attach(Activity activity);

    void detach();
}

class AsyncTaskHolder implements ActivityDependant {

    private Activity parentActivity;
    private boolean isRunning;
    private boolean isUpdateOnAttach;

    @Override
    public synchronized void attach(Activity activity) {
        this.parentActivity = activity;
        if (isUpdateOnAttach) {
            ((MainActivity) parentActivity).updateUI("done");
            isUpdateOnAttach = false;
        }
    }

    @Override
    public synchronized void detach() {
        this.parentActivity = null;
    }

    public synchronized void execute() {
        if (isRunning) {
            Toast.makeText(parentActivity, "Already running", Toast.LENGTH_SHORT).show();
            return;
        }
        isRunning = true;
        new AsyncTask<Void, Integer, Void>() {

            @Override
            protected Void doInBackground(Void... params) {
                for (int i = 0; i < 100; i += 10) {
                    try {
                        Thread.sleep(500);
                        publishProgress(i);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                return null;
            }

            @Override
            protected void onProgressUpdate(Integer... values) {
                if (parentActivity != null) {
                    ((MainActivity) parentActivity).updateUI(String.valueOf(values[0]));
                }
            }

            @Override
            protected synchronized void onPostExecute(Void aVoid) {
                if (parentActivity != null) {
                    ((MainActivity) parentActivity).updateUI("done");
                } else {
                    isUpdateOnAttach = true;
                }
                isRunning = false;
            }
        }.execute();
    }
}

0

Ben uygulayan kütüphane görev yürütülürken aktivite duraklama ve dinlenme ile sorunları çözebilir.

Uygulamalı AsmykPleaseWaitTaskve AsmykBasicPleaseWaitActivity. Ekranı döndürecek ve uygulamalar arasında geçiş yapacak olsanız bile etkinlik ve arka plan göreviniz iyi çalışır


-9

HIZLI GEÇİCİ ÇÖZÜM (önerilmez)

Bir Etkinliğin kendisini yok etmesini ve yaratmasını önlemek, etkinliğinizi bildirim dosyasında bildirmektir: android: configChanges = "orientation | keyboardHidden | screenSize

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

Dokümanlarda belirtildiği gibi

Ekran yönü değişti - kullanıcı cihazı döndürdü.

Not: Uygulamanız API seviyesi 13 veya üzerini (minSdkVersion ve targetSdkVersion öznitelikleri tarafından bildirildiği gibi) hedefliyorsa, bir aygıt dikey ve yatay yönler arasında geçiş yaptığında da değiştiğinden, "screenSize" yapılandırmasını da bildirmeniz gerekir.


1
Bu en iyi şekilde önlenir. developer.android.com/guide/topics/resources/… "Not: Yapılandırma değişikliğini kendiniz halletmek, alternatif kaynakların kullanılmasını çok daha zor hale getirebilir, çünkü sistem bunları sizin için otomatik olarak uygulamaz. Bu teknik son olarak düşünülmelidir yapılandırma değişikliği nedeniyle yeniden başlatmalardan kaçınmanız gerektiğinde başvurunuz ve çoğu uygulama için önerilmez. "
David
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.