2016-03-28 41 views
1

使用LeakCanary後,我發現我的應用程序中有很多泄漏,其中大部分是由於Volley的匿名回調監聽器造成的。所以我寫了一個Util(下面)類,它使用靜態回調和WeakReference來保持對Context的引用和匿名回調。但是,當我第一次打開應用程序,即冷啓動時,在請求完成後不久,上下文就會被打開,但在熱啓動期間,所有的工作都正常。這也只發生在應用程序中的第一個活動。排隊靜態回調監聽器上下文GCed

任何處理內存泄漏的替代方法都適用於正常工作的volley,這也是值得歡迎的。

public abstract class VUtil { 

    public static final String TAG = VUtil.class.getSimpleName(); 

    public interface JsonCallback { 
     void onSuccess(JSONObject response); 
    } 

    public interface StringCallback { 
     void onSuccess(String response); 
    } 

    public interface ErrorCallback { 
     void onError(VolleyError error); 
    } 

    public static class JsonResponseListener implements Response.Listener<JSONObject> { 
     private final WeakReference<Context> mContextWeakReference; 
     private final WeakReference<JsonCallback> mCallbackWeakReference; 

     public JsonResponseListener(Context context, JsonCallback callback) { 
      mContextWeakReference = new WeakReference<>(context); 
      mCallbackWeakReference = new WeakReference<>(callback); 
     } 

     @Override 
     public void onResponse(JSONObject jsonObject) { 
      Context context = mContextWeakReference.get(); 
      JsonCallback callback = mCallbackWeakReference.get(); 
      if (context != null && callback != null) { 
       callback.onSuccess(jsonObject); 
      } else { 
       Log.d(TAG, "Context was GCed"); 
      } 
     } 
    } 

    public static class StringResponseListener implements Response.Listener<String> { 
     private final WeakReference<Context> mContextWeakReference; 
     private final WeakReference<StringCallback> mCallbackWeakReference; 

     public StringResponseListener(Context context, StringCallback callback) { 
      mContextWeakReference = new WeakReference<>(context); 
      mCallbackWeakReference = new WeakReference<>(callback); 
     } 

     @Override 
     public void onResponse(String response) { 
      Context context = mContextWeakReference.get(); 
      StringCallback callback = mCallbackWeakReference.get(); 
      if (context != null && callback != null) { 
       callback.onSuccess(response); 
      } else { 
       Log.d(TAG, "Context was GCed"); 
      } 
     } 
    } 

    public static class ErrorListener implements Response.ErrorListener { 

     private final WeakReference<Context> mContextWeakReference; 
     private final WeakReference<ErrorCallback> mCallbackWeakReference; 

     public ErrorListener(Context context, ErrorCallback callback) { 
      mContextWeakReference = new WeakReference<>(context); 
      mCallbackWeakReference = new WeakReference<>(callback); 
     } 

     @Override 
     public void onErrorResponse(VolleyError error) { 
      Context context = mContextWeakReference.get(); 
      ErrorCallback callback = mCallbackWeakReference.get(); 
      if (context != null && callback != null) { 
       callback.onError(error); 
      } else { 
       Log.d(TAG, "Context was GCed"); 
      } 
     } 
    } 
} 
+0

@ Sourabh如何在Stringrequest中傳遞該類對象,並從該響應中將該響應重定向到各自的活動響應 –

+0

將對象傳遞爲「MyClass.this」?我不明白你的意思是'將不同活動的響應重定向到相應的活動響應' – Sourabh

+0

我進行網絡呼叫,因此無論接收到什麼響應,我都必須將其重定向到特定活動。 –

回答

0

由於djodjo的答案,幫我達成解決方案

  • 始終使用addToRequestQueue(request, TAG)。這裏TAG位是我們將用來取消請求時,他們的活動/片段/視圖或任何東西是GCed

  • 我所做的是創建一個基本活動,並添加所有此請求取消代碼在該活動中。這裏是什麼樣子

    public abstract class BaseActivity extends AppCompatActivity { 
    
        public final String tag; 
    
        public BaseActivity() { 
         super(); 
         tag = getClass().getSimpleName(); 
        } 
    
        @Override 
        protected void onDestroy() { 
         App.getInstance().cancelRequests(tag); 
         super.onDestroy(); 
        } 
    
        protected <T> void addToRequestQueue(Request<T> request) { 
         App.getInstance().addToRequestQueue(request, tag); 
        } 
    
    } 
    

cancelRequests只是簡單的代碼

getRequestQueue().cancelAll(tag); 

從這個BaseActivity擴展您的活動,並使用addToRequestQueue提出要求,當您的活動被破壞,這將自動被取消。爲片段/對話框/其他任何事情做類似的事情。

如果您從其他任何地方發出的請求不符合生命週期,請確保它不與任何Context綁定,並且您會沒事的。

+0

@MidasLefko,我想在我的抽籤請求中添加cancelAll。我在哪裏添加「getRequestQueue()。cancelAll(tag);」,它是單身模式嗎?活動生命週期?我很困惑。非常感謝。 – Woppi

+0

將它添加到你的'Application'實例中,並且因爲它的Application可以使用靜態引用或'WeakReference' – Sourabh

1

GC取決於許多正在發生的事情。您的情況的一個可能原因是,當您在「冷啓動」後執行第一次請求時,您的應用程序必須啓動各種自定義對象,片段,活動,視圖緩存等,因此在增加堆並因此執行GC之前需要內存。

然而,我提出的解決方案是改變你的架構。

1)似乎你保持參考上下文,但它從來沒有使用過。只是放棄它

2)你有Volley回調委託給你的自定義回調,你仍然需要通過,你爲什麼不簡單地使用你傳遞給相應請求的一組回調。

3)你WeekRef自定義回調,但你離不開他們。周引用不是內存泄漏的最終解決方案。當你不需要它時,你必須找出ref爲什麼仍然存在。

所以如果你泄漏的問題是在JsonCallback,StringCallback和ErrorCallback實現只是試圖找出這個,而不是讓鏈更長,並在最後切割它。