2017-05-04 17 views
6

最近我一直在研究有關java/android中的內存泄漏,以及幾乎所有的地方都說它不是匿名類我應該使用帶有弱引用的靜態內部類。
所以,在我的Android應用程序我開始這樣做,但很快就厭倦了它,因爲它是很多樣板代碼...我認爲有一個替代解決方案,我寧願使用,但我不確定就防止內存泄漏而言,它是靜態內部類的有效替代方案。正如我之前所說,我沒有看到任何其他地方建議的解決方案(都說使用靜態內部類),所以這就是爲什麼我不知道我的替代方案會工作。可能的替代靜態內部類以防止android/java中的內存泄漏?

生病使用從我的應用程序一個簡單的例子:
我有一類稱爲Web客戶端負責處理異步Web請求,並接受名爲iCallback的接口返回從服務器到呼叫者的響應,並在我的活動,一旦我得到這個回調我需要關閉對話框,也許執行一些活動相關的事情(如觸發onBackPressed()和setResult())。
所以這裏是我的靜態內部類我創建:

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

所以對於每一個變量,我需要在這個靜態內部類我要創建一個新的弱引用,然後檢索對象本身,然後每次我想訪問它,我需要檢查它是否爲空...這對我來說看起來像很多代碼。

這裏是我建議的替代方法:

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; 
    } 
} 

對我來說這似乎更清潔:沒有創造和每一個變量i需要訪問弱引用的檢索的情況下,我可以直接調用活動的方法(例如onBackPressed ()),並且不檢查無處不在。
我現在必須檢查null的唯一地方是在調用callBack方法之前在WebClient類中。

所以我的問題是,這種方法在防止內存泄漏方面取得了相同的結果嗎?這是靜態內部類的「值得」替代嗎?

+0

好問題,一直想知道我是否真的需要每個變量的WeakReference並且每次檢查爲空 – Denny

+0

好吧,在你的解決方案中仍然需要一些'null'檢查。但我認爲你的主要問題是你在需要時檢查這些價值。只需做一次檢查(對於你的例子中的'parentActivity')。 – AxelH

+0

Java中沒有這樣的「靜態內部類」,因爲「內部類」的Java定義是非靜態的嵌套類。 –

回答

3

不幸的是,你的方法不起作用。通過在您的活動中實施WebClient.ICallback而不是內部類,您不會擺脫泄漏。泄漏並不是因爲活動和對話的引用隱含在匿名類,lambda或非靜態的內部類實例中;當WebClient在活動消失時保持此引用(不會被銷燬,因爲它有強烈的引用)。

特殊mCallBack您在活動暫停時設置爲null,不會獲得任何結果。同樣,您可以簡單地將您的活動實例傳遞給WebClient。現在有一個強有力的參考您的活動,這是由某人(WebClient的異步處理程序)管理,誰不在您的控制之下。如果你不走運,異步處理程序會卡住某處並永遠不會發布此引用。

請仔細閱讀詳細的explanation

請注意,WebView本身可以cause a memory leak,如果沒有采取特殊措施!

+0

爲什麼mCallback沒有獲得任何收益?這是我的邏輯,請解釋爲什麼我錯了:如果我將REFERENCE mCallback傳遞給WebClient,那麼它與該活動持有的引用相同。因此,當活動暫停時,該引用在WebClient中的活動AND上爲空(因爲它們都指向內存中的同一對象) - 因此,當活動暫停時,WebClient不再持有活動的引用(它擁有對null的引用),並且該活動可以被垃圾回收。 –

+0

不幸的是,Java引用不能以這種方式工作。當您將mCallBack引用傳遞給WebClient時,它不會保留對'myActivityInstance.mCallBack'的'指針'。相反,它保留一個單獨的引用,即*重複* mCallBack。因此,當您將mCallBack設置爲** null **時,WebClient不受影響。如果Java使用引用計數,我們可以說設置'mCallBack = this'設置refcount = 2,啓動WebClient將它增加到3.當onPause()設置'mCallBack = null'時,refcount又是2。 Java不使用refcount,但在這種情況下結果是相同的。 –