Uyarı: Bu AsyncTask sınıfı statik olmalı veya sızıntılar oluşabilir


270

Kodumu bildiren bir uyarı alıyorum:

Bu AsyncTask sınıfı statik olmalıdır veya sızıntılar meydana gelebilir (anonim android.os.AsyncTask)

Tam uyarı:

Bu AsyncTask sınıfı statik olmalıdır veya sızıntılar meydana gelebilir (anonim android.os.AsyncTask) Statik alan bağlam sızdırır. Statik olmayan iç sınıfların dış sınıflarına örtük bir referansları vardır. Bu dış sınıf örneğin bir Parça veya Etkinlik ise, bu referans uzun süre çalışan işleyicinin / yükleyicinin / görevin, çöp toplanmasını önleyen etkinliğe bir referans tutacağı anlamına gelir. Benzer şekilde, bu daha uzun çalışan örneklerin etkinliklerine ve parçalarına doğrudan alan başvuruları sızıntılara neden olabilir. ViewModel sınıfları hiçbir zaman Görünümleri veya uygulama dışı Bağlamları göstermemelidir.

Bu benim kodum:

 new AsyncTask<Void,Void,Void>(){

        @Override
        protected Void doInBackground(Void... params) {
            runOnUiThread(new Runnable() {

                @Override
                public void run() {
                    mAdapter.notifyDataSetChanged();
                }
            });

            return null;
        }
    }.execute();

Bunu nasıl düzeltirim?


2
Bu androiddesignpatterns.com/2013/01/… okuma neden statik olması gerektiğini bir ipucu vermelidir
Raghunandan

Şimdiye kadar, AsyncTask'ı gerektiğinde runOnUiThread (...) ile birlikte yeni Thread (...). Statr () ile değiştirebildim, bu yüzden artık bu uyarıyla uğraşmak zorunda değilim.
Hong

1
Kotlin'de bu sorunun çözümü nedir?
TapanHP

Lütfen hangi cevabın kabul edilmesi gerektiğini tekrar düşünün. Aşağıdaki cevaplara bakın.
Sepmega

Benim durumumda, bu aktiviteye doğrudan referansı olmayan bir Singleton'dan myActivity.getApplication()alıyorum (RoomDB sınıflarını ve diğer sınıfları başlatmak için Singleton için özel kurucuya çıktı alır ). ViewModels'lerim, DB'de bazı işlemleri gerçekleştirmek için Singleton örneğini özel başvuru olarak alır. Bu nedenle, ViewModels, Singleton paketinin yanı sıra android.app.Applicationbunlardan birini içeri aktarır android.app.Activity. "Singleton" un çalışmak için bu ViewModels'i içe aktarması gerekmediğinden, bellek sızıntıları olabilir mi?
SebasSBM

Yanıtlar:


65

Statik olmayan iç sınıflar içeren sınıfa bir referans tutar. Bir AsyncTaskiç sınıf olarak ilan ettiğinizde , sınıfın sınıfından daha uzun yaşayabilir Activity. Bunun nedeni, kapsayıcı sınıfa örtük başvurudır. Bu, etkinliğin çöp toplanmasını ve dolayısıyla bellek sızıntısını önleyecektir.

Sorununuzu çözmek için, anonim, yerel ve iç sınıf yerine statik iç içe sınıf veya üst düzey sınıf kullanın.


1
Çözüm, uyarının kendisinde yatar. Statik iç içe sınıf veya üst düzey sınıf kullanın.
Anand

3
@KeyurNimavat Bence faaliyetlerinize zayıf bir referansta bulunabilirsiniz
peterchaula

42
Peki AsyncTask kullanmanın anlamı nedir? yeni Thread ve handler.post veya view.post (UI'yi güncellemek için) sonunda Thread'ın çalışma yöntemini çalıştırmak daha kolaysa. AsyncTask statik veya üst düzey sınıfsa, o zaman gerekli değişkenlere / yöntemlere erişmek zordur
user924

8
nasıl kullanılacağına dair herhangi bir kod yoktur. Oraya hiç statik koymayı denedim, ama daha fazla uyarı ve hata ortaya
çıkacak

19
@Anand stackoverflow.com/a/46166223/145119 adresindeki daha yararlı yanıtın en üstte olabilmesi için lütfen bu yanıtı silin .
Mithaldu

556

Statik iç AsyncTask sınıfı nasıl kullanılır

Sızıntıları önlemek için iç sınıfı statik hale getirebilirsiniz. Bununla birlikte sorun, artık Etkinliğin kullanıcı arabirimi görünümlerine veya üye değişkenlerine erişiminizin olmamasıdır. Bir referans gönderebilirsiniz, Contextancak daha sonra aynı bellek sızıntısı riski taşırsınız. (AsyncTask sınıfının güçlü bir referansı varsa, Android, Etkinliği kapattıktan sonra çöp toplayamaz.) Çözüm, Etkinliğe (veya Contextihtiyacınız olan her şeye) zayıf bir referans yapmaktır .

public class MyActivity extends AppCompatActivity {

    int mSomeMemberVariable = 123;

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

        // start the AsyncTask, passing the Activity context
        // in to a custom constructor 
        new MyTask(this).execute();
    }

    private static class MyTask extends AsyncTask<Void, Void, String> {

        private WeakReference<MyActivity> activityReference;

        // only retain a weak reference to the activity 
        MyTask(MyActivity context) {
            activityReference = new WeakReference<>(context);
        }

        @Override
        protected String doInBackground(Void... params) {

            // do some long running task...

            return "task finished";
        }

        @Override
        protected void onPostExecute(String result) {

            // get a reference to the activity if it is still there
            MyActivity activity = activityReference.get();
            if (activity == null || activity.isFinishing()) return;

            // modify the activity's UI
            TextView textView = activity.findViewById(R.id.textview);
            textView.setText(result);

            // access Activity member variables
            activity.mSomeMemberVariable = 321;
        }
    }
}

notlar

  • Bildiğim kadarıyla, bu tür bellek sızıntısı tehlikesi her zaman doğruydu, ancak yalnızca Android Studio 3.0'da uyarıyı görmeye başladım. AsyncTaskDışarıdaki ana öğreticilerin çoğu hala bununla ilgilenmiyor ( buraya , buraya , buraya ve buraya bakın ).
  • AsyncTaskÜst düzey bir sınıf olsaydınız da benzer bir prosedür izlerdiniz. Statik bir iç sınıf temelde Java'daki bir üst düzey sınıfla aynıdır.
  • Etkinliğin kendisine ihtiyacınız yoksa, ancak Bağlamın (örneğin, a görüntülemesini Toast) istiyorsanız, uygulama bağlamına bir referans iletebilirsiniz. Bu durumda AsyncTaskkurucu şöyle görünecektir:

    private WeakReference<Application> appReference;
    
    MyTask(Application context) {
        appReference = new WeakReference<>(context);
    }
  • Bu uyarıyı yok saymak ve yalnızca statik olmayan sınıfı kullanmak için bazı argümanlar var. Sonuçta, AsyncTask'ın çok kısa ömürlü olması (en uzun sürede birkaç saniye) olması amaçlanmıştır ve yine de bittiğinde Faaliyete olan referansını serbest bırakacaktır. Bkz bu ve bu .
  • Mükemmel makale: Bağlam Nasıl Sızılır: İşleyiciler ve İç Sınıflar

Kotlin

Kotlin'de sadece iç sınıf için inneranahtar kelime eklemeyin . Bu, varsayılan olarak statik hale getirir.

class MyActivity : AppCompatActivity() {

    internal var mSomeMemberVariable = 123

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // start the AsyncTask, passing the Activity context
        // in to a custom constructor
        MyTask(this).execute()
    }

    private class MyTask
    internal constructor(context: MyActivity) : AsyncTask<Void, Void, String>() {

        private val activityReference: WeakReference<MyActivity> = WeakReference(context)

        override fun doInBackground(vararg params: Void): String {

            // do some long running task...

            return "task finished"
        }

        override fun onPostExecute(result: String) {

            // get a reference to the activity if it is still there
            val activity = activityReference.get()
            if (activity == null || activity.isFinishing) return

            // modify the activity's UI
            val textView = activity.findViewById(R.id.textview)
            textView.setText(result)

            // access Activity member variables
            activity.mSomeMemberVariable = 321
        }
    }
}

1
@ManojFrekzz, Hayır, aslında sen yapabilirsiniz geçirildi Aktivite zayıf referans yararlanarak UI güncelleştirmek. Benim göz atın onPostExecuteyukarıdaki kodda yine yöntemini. Burada kullanıcı arayüzünü güncellediğimi görebilirsiniz TextView. Sadece kullanmak activity.findViewByIdEğer güncelleme gerekiyorsa onu UI öğesi bir başvuru alır.
17'de Suragch

7
+1. Bu şimdiye kadar gördüğüm en iyi ve en temiz çözüm! Yalnızca, onPostExecute yönteminde kullanıcı arayüzünü değiştirmek istiyorsanız, Etkinliğin yok edilip edilmediğini de kontrol etmelisiniz: activity.isFinishing ()
zapotec

1
Not! Bu yanıtı kullanırken, doInBackground işlemi bellek yoğun olduğundan, çöp toplamayı tetiklediğinden, zayıf referansı topladığından ve zaman uyumsuzluğu öldürdüğünden Null Pointer Exceptions içinde çalışmaya devam ettim. Arka plan işlemlerinizin bellek açısından daha yoğun olduğunu biliyorsanız, zayıf bir yerine SoftReference kullanmak isteyebilirsiniz.
PGMacDesign

2
@Sunny, Etkinlik yerine Fragman'a bir referans verin. Dışarı alacağını activity.isFinishing()çek ve muhtemelen bir ile değiştirin fragment.isRemoving()çek. Yine de son zamanlarda parçalarla çok çalışmadım.
Suragch

1
@bashan, (1) Dış sınıf bir Etkinlik değilse, kurucunuzda dış sınıfınıza AsyncTaskbir başvuru iletirsiniz. Ve doInBackground()ile dış sınıfa bir referans alabilirsiniz MyOuterClass ref = classReference.get(). Kontrol edin null. (2) onPostExecute()Kullanıcı arayüzünü yalnızca arka plan görevinin sonuçlarıyla güncellersiniz. Kullanıcı arayüzünü güncellemeniz her zamanki gibi. Kontrol activity.isFinishing(), sadece etkinliğin bitirmeye başlamadığından emin olmak içindir, bu durumda kullanıcı arayüzünü güncellemek anlamsız olacaktır.
Suragch

23

Bu AsyncTasksınıf statik olmalı veya sızıntılar oluşabilir, çünkü

  • Zaman Activityyok edilir, AsyncTask(her ikisi de staticya da non-static) hala çalışır
  • İç sınıf non-static( AsyncTask) sınıfı ise, dış sınıfa ( Activity) başvuruda bulunacaktır .
  • Bir nesnenin üzerinde herhangi bir referans noktası yoksa, Garbage Collectedonu serbest bırakır. Bir nesne kullanılmamış ve varsa Garbage Collected edemez bırakın => sızıntı bellek

=> Eğer AsyncTaskbir non-static, Activity=> sızıntı yok edilir olayı bırakmayacaktır

AsyncTask'ı sızıntı olmadan statik sınıf olarak yaptıktan sonra güncelleme kullanıcı arayüzü için çözüm

1) WeakReference@Suragch cevap gibi kullanın
2) ( Gönderen Activity)AsyncTask

public class NoLeakAsyncTaskActivity extends AppCompatActivity {
    private ExampleAsyncTask asyncTask;

    @Override 
    protected void onCreate(Bundle savedInstanceState) {
        ...

        // START AsyncTask
        asyncTask = new ExampleAsyncTask();
        asyncTask.setListener(new ExampleAsyncTask.ExampleAsyncTaskListener() {
            @Override
            public void onExampleAsyncTaskFinished(Integer value) {
                // update UI in Activity here
            }
        });
        asyncTask.execute();
    }

    @Override
    protected void onDestroy() {
        asyncTask.setListener(null); // PREVENT LEAK AFTER ACTIVITY DESTROYED
        super.onDestroy();
    }

    static class ExampleAsyncTask extends AsyncTask<Void, Void, Integer> {
        private ExampleAsyncTaskListener listener;

        @Override
        protected Integer doInBackground(Void... voids) {
            ...
            return null;
        }

        @Override
        protected void onPostExecute(Integer value) {
            super.onPostExecute(value);
            if (listener != null) {
                listener.onExampleAsyncTaskFinished(value);
            }
        }

        public void setListener(ExampleAsyncTaskListener listener) {
            this.listener = listener;
        }

        public interface ExampleAsyncTaskListener {
            void onExampleAsyncTaskFinished(Integer value);
        }
    }
}


5
@Suragch bağlantınız, onDestroy'un çağrılması garanti edilmese de, sistemin işlemi öldürdüğü tek durumun, bu nedenle tüm kaynakların serbest bırakıldığını belirtir. Yani, burada tasarruf etmeyin, ancak burada bir kaynak sürümü yapabilirsiniz.
Angelo Fuchs

2
Statik olmayan AsyncTask kullanım durumunda, neden AsyncTask örnek değişkenini buna benzer NULL olarak ayarlayamıyoruz. AsyncTask çalışıyor olsa GC ücretsiz Aktivite söyleyemez mi?
Hanif

@ AsyncTask örnek değişkenini NUL olarak ayarlamak yardımcı olmayacaktır, çünkü görev dinleyicide hala ref'ye sahiptir.
Susanta
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.