防止网络请求(或其他回调)引用,从而造成内存泄漏
本文的解決方案用來解決類似如:Activity請求網絡,而回調傳的是自身,造成Activity執行finish()后并沒有被銷毀,而是被網絡請求持有.和其相類似的問題
正文
1.網絡請求使用Activity當做回調,如:
public class MainActivity extends BaseActivity implements ObserverCallBack {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);HttpMethod.login("", "", this);//回調傳入了Activity自身} }這時,如果用戶按下了返回鍵,而網速又比較慢,如果30秒才會連接超時,則此時Activity會被網絡請求給引用大約30秒然后才有可能被釋放,由此造成了內存泄漏
2.解決方案1,取消請求:
我使用的是鴻洋大神的OkHttpUtils,可以在發起一個網絡請求的時候設置一個tag,然后可以通過該tag來取消請求
OkHttpUtils.post().url(url).params(map).tag(activity)//設置tag.build().connTimeOut(20000).execute(new StringCallback() {}@Override protected void onDestroy() {super.onDestroy();OkHttpUtils.getInstance().cancelTag(this);//在Activity的銷毀回調中取消該Activity中的所有網絡請求 }但是這樣還有一起其他的問題,比如在特定頁面的onDestroy()中我想發一個記錄的網絡請求,但是我不需要知道結果,這樣操作就會造成這個請求發不出去.
可能你會說我另設置一個tag即可,其實這樣也可以,但是會提高復雜度,并容易出錯,下面第二種解決方案
3.解決方案2,解耦回調:
我寫了一個類來進行解耦回調,我會把用法寫在下面(可以copy該類并將所有ObserverCallBack替換為你的回調類)
import android.text.TextUtils;import java.lang.ref.WeakReference; import java.util.HashMap; import java.util.Iterator; import java.util.Map;/*** 創 建: lt * 作 用: 解耦網絡請求* 注意事項:*/public class CallBackTask<T> {private CallBackTask(boolean isM) {this.isM = isM;id = 0;map = new HashMap<>();}private boolean isM;//是否運行在多線程環境下(多線程會進行加鎖操作,比較耗時)private long id;//唯一的key,依次遞增/*** 存儲CallBack的map* key為對象的內存地址+id* 值為對象的弱引用*/private HashMap<String, WeakReference<T>> map;private final int ADD = 0,REMOVE_KEY = 1,REMOVE_OBJECT = 2,GET = 3,CLEAR = 4;private static CallBackTask<ObserverCallBack> task = new CallBackTask<>(false);/*** 獲取單實例,由于網絡請求在應用中基本一直會用到,所以直接使用餓漢單例* ps:如果有需求需要同時存其他種類的回調,可以再創建一個這樣的方法,new的泛型改一下就行*/public static CallBackTask<ObserverCallBack> getInstance() {return task;}/*** 將回調存儲在此,并返回key*/public String add(T value) {return isM ? doubleThread(ADD, value).toString() : singleThread(ADD, value).toString();}/*** 根據key移除引用*/public void remove(String key) {if (isM)doubleThread(REMOVE_KEY, key);elsesingleThread(REMOVE_KEY, key);}/*** 根據對象的地址移除對象*/public void remove(T value) {if (isM)doubleThread(REMOVE_OBJECT, value);elsesingleThread(REMOVE_OBJECT, value);}/*** 根據key取出回調,需要判斷返回值是否為null,若為null可能已經銷毀(該方法的實現在get后會自動remove,如果不想如此可以自行修改方法)*/public T get(String key) {Object t = isM ? doubleThread(GET, key) : singleThread(GET, key);return t == null ? null : (T) t;}/*** 清理所有是null值的對象*/public void cleanUpNull() {if (isM)doubleThread(CLEAR, null);elsesingleThread(CLEAR, null);}/*** 單線程方法,isM為false時執行,需要調用者保證調用的都是在同一個線程,否則可能拋出ConcurrentModificationException*/private Object singleThread(int state, Object obj) {switch (state) {case ADD: {if (obj == null)return "";String key = obj.toString() + id;id++;map.put(key, new WeakReference<>((T) obj));return key;}case REMOVE_KEY: {if (obj == null || map.size() == 0)return null;String stringKey = obj.toString();if (TextUtils.isEmpty(stringKey))return null;map.remove(stringKey);break;}case REMOVE_OBJECT: {if (obj == null || map.size() == 0)return null;Iterator<String> iterator = map.keySet().iterator();while (iterator.hasNext()) {String key = iterator.next();if (key.contains(obj.toString()))iterator.remove();}break;}case GET: {if (obj == null || map.size() == 0)return null;String stringKey = obj.toString();if (TextUtils.isEmpty(stringKey))return null;WeakReference<T> tWeakReference = map.get(stringKey);if (tWeakReference == null) {map.remove(stringKey);return null;}T callBack = tWeakReference.get();map.remove(stringKey);return callBack;}case CLEAR: {if (map.size() == 0)return null;Iterator<Map.Entry<String, WeakReference<T>>> iterator = map.entrySet().iterator();while (iterator.hasNext()) {Map.Entry<String, WeakReference<T>> entry = iterator.next();if (entry.getValue() == null)iterator.remove();else if (entry.getValue().get() == null)iterator.remove();}break;}}return null;}/*** 多線程方法,isM為true時執行,無需擔心拋異常,但是效率略低(相對于單線程方法)*/private synchronized Object doubleThread(int state, Object obj) {return singleThread(state, obj);} }在網絡請求時,在把Activity當做回調傳入的時候,可以先調用add()方法存進CallBackTask中,并獲得key,然后發送handler消息使該方法棧彈棧,使其不直接引用Activity,然后在需要回調的時候通過key去CallBackTask中取出回調,如果為null表示該回調已經被銷毀了,則不需要繼續往下走,偽代碼如下:
發送請求并傳入回調
public static void requestGetOrPost(final String url,final ObserverCallBack callBack) {Message msg = Message.obtain();msg.obj = new Object[]{url,CallBackTask.getInstance().add(callBack)//調用方法把回調存入CallBackTask中并獲取到String類型的key};handler.sendMessage(msg);//發送handler消息,則該方法棧彈棧,不在引用這個callBack}Handler獲取到消息在調用方法發送網絡請求
private static Handler handler = new Handler() {@Overridepublic void handleMessage(Message msg) {Object[] objects = (Object[]) msg.obj;start((String) objects[0],(String) objects[1]);}};網絡請求成功后找到通過key找到回調并調用回調方法
????????????????????????????@Overridepublic void onResponse(String response, int id) {//OkHttpUtils請求成功后的回調try {ObserverCallBack callBack = CallBackTask.getInstance().get(callBackId);//根據key找到回調if (callBack == null)//判斷回調如果是null則表示已經被銷毀,無需往下走return;callBack.handleResult(response);} catch (Exception e) {}}如果Activity銷毀時可以調用(可直接封裝進Base)
@Overrideprotected void onDestroy() {super.onDestroy();CallBackTask.getInstance().remove(this);//移除該回調的所有引用}這樣就解決了上面提到的內存泄漏問題,并且不用擔心在onDestroy()中執行網絡請求被取消了
原理解析
原理就比較簡單了,回調存儲時使用軟引用,然后使用HashMap來存儲key和軟引用,如果內存不足時,若只有該軟引用引用時則會被gc給回收掉,減少造成oom的可能
其次,工具類里有一個參數來指定多線程和單線程模式,這是因為里面涉及到了map的循環,眾所周知,在java數據結構循環的時候修改該數據結構會拋異常,所以設定了這個單線程和多線程的模式
?
如果幫到您的話請點一下贊
轉發請附上原文鏈接謝謝
總結
以上是生活随笔為你收集整理的防止网络请求(或其他回调)引用,从而造成内存泄漏的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Kotlin 中的 run、let、wi
- 下一篇: 安卓程序添加指纹解锁功能