2017-05-04 29 views
6

son zamanlarda java/android'de bellek sızıntıları hakkında araştırma yapıyordum ve hemen hemen her yerde anonim sınıflar yerine zayıf referanslarla statik iç sınıfları kullanmam gerektiğini söylüyor.
bu yüzden benim android uygulamasında bunu yapmaya başladım ama çok çabuk bıktım çünkü bu bir çok kodlu kod ... kullanmayı tercih edeceğim alternatif bir çözümüm var, ama emin değilim Statik iç sınıflara, bellek sızıntılarını önlemek açısından geçerli bir alternatiftir. Daha önce de söylediğim gibi, bu çözümü başka bir yerde (statik iç sınıfları kullanmak için söylendi) önerdim, bu yüzden neden alternatifimin işe yaramayacağından emin değilim.statik iç sınıflara alternatif olabilir mi?

hasta kullanım benim app basit bir örnek:
i asenkron web isteklerini işleyen bir sınıf olarak adlandırılan WebClient'ı var ve bir kere ben arayana sunucudan yanıt döndürür iCallback denilen bir arayüz kabul eder ve benim aktivitesinde Bu geri bildirimi al, bir iletişim kutusunu kapatmam gerekiyor ve aktiviteyle ilgili bazı şeyleri gerçekleştirebiliyorum (tetikte onBackPressed() ve setResult()). işte
benim statik iç i yarattık sınıftır: ben bu statik iç sınıfta gereken her değişken için öylesine

private static class CallBack implements WebClient.ICallback 
{ 
    private WeakReference<ProgressDialog> mProgDiag; 
    private WeakReference<BaseActivity> mActivity; 

    public CallBack(BaseActivity activity, ProgressDialog progDiag) 
    { 
     this.mProgDiag = new WeakReference<>(progDiag); 
     this.mActivity = new WeakReference<>(activity); 
    } 

    @Override 
    public void onCallback(String data) 
    { 
     String responseAsString = Utils.extractStringFromResponse(...); 

     final BaseActivity parentActivity = mActivity.get(); 
     ProgressDialog dialog = mProgDiag.get(); 

     if(dialog != null) 
     { 
      dialog.dismiss(); 
     } 

     if (responseAsString == null) 
     { 
      if(parentActivity != null) 
      { 
       Utils.makeServerErrorDialog(parentActivity, 
              new iDialogButtonClickedListener() 
              { 
               @Override 
               public void onDialogButtonClicked() 
               { 
                parentActivity.onBackPressed(); 
               } 
              }); 
      } 

      return; 
     } 

     //everything is ok 
     if (responseAsString.equals("1")) 
     { 
      if(parentActivity != null) 
      { 
       Intent result = new Intent(); 
       result.putExtra(...); 

       parentActivity.setResult(Activity.RESULT_OK, result); 
      } 
     } 

     else 
     { 
      Utils.reportErrorToServer(...); 

      if(parentActivity != null) 
      { 
       parentActivity.setResult(Activity.RESULT_CANCELED); 
      } 
     } 

     if(parentActivity != null) 
     { 
      parentActivity.onBackPressed(); 
     } 
    } 
} 

i sonra her zaman sonra, yeni bir zayıf referans oluşturmak nesnenin kendisini almak ve gerek Ona erişmek istiyorum, null olup olmadığını kontrol etmem gerekiyor ... bana çok fazla kod gibi görünüyor. Bana bu kadar

public abstract class BaseActivity extends AppCompatActivity 
     implements WebClient.ICallback 
{ 
    private static final String TAG = "BaseActivity"; 

    WebClient.ICallback mCallBack; 
    ProgressDialog mProgDiag; 

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

     setContentView(...); 

     mCallBack = this; 

     //some code to invoke a server request on button click 
     //and passing mCallBack to the request 
    } 

    @Override 
    public void onCallback(String data) 
    { 
     String responseAsString = Utils.extractStringFromResponse(...); 

     mProgDiag.dismiss(); 

     if (responseAsString == null) 
     { 
      Utils.makeServerErrorDialog(this, 
             new iDialogButtonClickedListener() 
             { 
              @Override 
              public void onDialogButtonClicked() 
              { 
               onBackPressed(); 
              } 
             }); 

      return; 
     } 

     //everything is ok 
     if (responseAsString.equals("1")) 
     { 
      Intent result = new Intent(); 
      result.putExtra(...); 

      setResult(Activity.RESULT_OK, result); 
     } 

     else 
     { 
      Utils.reportErrorToServer(...); 

      setResult(Activity.RESULT_CANCELED); 
     } 

     onBackPressed(); 
    } 

    @Override 
    protected void onPause() 
    { 
     mCallBack = null; 

     super.onPause(); 
    } 

    @Override 
    protected void onResume() 
    { 
     super.onResume(); 

     mCallBack = this; 
    } 
} 

daha temiz görünüyor:

ve burada önerdiğim alternatif i doğrudan faaliyet yöntemlerini çağırabilir, hiçbir oluşturma ve ben erişmesi gereken her değişken için zayıf başvuru alınırken örnekleri (örn onBackPressed() ve her yerde null için kontrol yok.
Şimdi null olup olmadığını kontrol etmek için tek yer, callBack yöntemini çağırmadan önce WebClient sınıfının içinde.

bu yüzden sorum, bellek sızıntılarını önleme açısından aynı sonuca ulaşıyor mu? Statik iç sınıflara "layık" bir alternatif mi?

+0

İyi bir soru, her değişkendeki WeakReference'a gerçekten ihtiyacım varsa ve her seferinde null değerini kontrol edip etmediğimi merak ediyorum – Denny

+0

Eh, çözümünüzde bazı "null" kontroller hala gereklidir. Ama bence asıl sorunun, ihtiyaç duyduğunuzda bu değeri kontrol etmenizdir. Sadece bir kez kontrol yapın (örneğinizde 'parentActivity' için). – AxelH

+0

"İç sınıf" ın Java tanımı durağan olmayan iç içe geçmiş bir sınıf olduğu için Java'da "statik iç sınıf" diye bir şey yoktur. –

cevap

3

Maalesef yaklaşımınız çalışmıyor. Bir iç sınıftan ziyade, etkinliğinizde WebClient.ICallback'u uygulayarak, sızıntıdan kurtulmuyorsunuz. Sızıntı gerçekleşir çünkü etkinlik ve diyalog için yapılan başvurular anonim bir sınıfta veya lambda veya statik olmayan bir iç sınıf örneğinde gizlidir; WebClient, bu eylemi sürdürürken bu referansı sakladığı zaman olur (yok edilmez, çünkü güçlü bir referans vardır).

Etkinlik duraklatıldığında null olarak belirlediğiniz özel mCallBack, hiçbir şey kazanmaz. Aynen, etkinlik örneğinizi WebClient'e kolayca geçirebilirsiniz. Artık, sizin kontrolünüz altında olmayan birileri (WebClient'in async işleyicileri) tarafından yönetilen etkinliğiniz için güçlü bir referans var. Eğer şanssızsanız, async işleyicisi bir yere sıkışır ve bu referansı asla bırakmaz.

Lütfen bu detaylı bilgiyi explanation numaralı telefondan okuyun.

Özel önlemler alınmazsa, WebView'un kendi başına cause a memory leak olduğunu unutmayın!

+0

mCallback neden bir şey kazanmıyor? Bu benim mantığım, lütfen niçin yanlış olduğumu açıklayın: REFERENCE mCallback'i WebClient'e iletirsem, o zaman etkinlik tarafından tutulan ile aynı referansı tutar. Bu nedenle, etkinlik duraklatıldığında, bu başvuru hem etkinlikte hem de WebClient'te boştur (her ikisi de bellekte aynı nesneye işaret ettiğinden) - bu nedenle, etkinlik duraklatıldığında WebClient artık etkinliğe bir başvuruda bulunmaz (null'a bir referans verir) ve etkinlik toplanan çöp olabilir. –

+0

Maalesef, Java referansları bu şekilde çalışmıyor. WebClient'e mCallBack başvurusu yaptığınızda, "myctivityInstance.mCallBack" için bir 'işaretçi' tutmaz. Daha ziyade, * duplicates * mCallBack. Yani, mCallBack öğesini ** null olarak ayarladığınızda, WebClient etkilenmez. Java referans sayımları ile çalışıyorsa, 'mCallBack =' ayarının refcount = 2 olduğunu ve WebClient'i başlatmanın 3'e yükseldiğini söyleyebiliriz. OnPause(), 'mCallBack = null' değerini ayarlarsa, refcount tekrar 2 olur. Java refcount kullanmaz, ancak bu durumda sonuç aynıdır. –

İlgili konular