防止网络请求(或其他回调)引用,从而造成内存泄漏
本文的解决方案用来解决类似如:Activity请求网络,而回调传的是自身,造成Activity执行finish()后并没有被销毁,而是被网络请求持有.和其相类似的问题
1.网络请求使用Activity当做回调,如:
public class MainActivity extends BaseActivity implements ObserverCallBack { @Override protected 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); else singleThread(REMOVE_KEY, key); } /** * 根据对象的地址移除对象 */ public void remove(T value) { if (isM) doubleThread(REMOVE_OBJECT, value); else singleThread(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); else singleThread(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(); } 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() { @Override public void handleMessage(Message msg) { Object[] objects = (Object[]) msg.obj; start( (String) objects[0], (String) objects[1] ); } };
网络请求成功后找到通过key找到回调并调用回调方法
@Override public 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)
@Override protected void onDestroy() { super.onDestroy(); CallBackTask.getInstance().remove(this);//移除该回调的所有引用 }
这样就解决了上面提到的内存泄漏问题,并且不用担心在onDestroy()中执行网络请求被取消了
一、推荐使用迅雷或快车等多线程下载软件下载本站资源。
二、未登录会员无法下载,登录后可获得更多便利功能,若未注册,请先注册。
三、如果服务器暂不能下载请稍后重试!总是不能下载,请点我报错 ,谢谢合作!
四、本站大部分资源是网上搜集或私下交流学习之用,任何涉及商业盈利目的均不得使用,否则产生的一切后果将由您自己承担!本站将不对任何资源负法律责任.如果您发现本站有部分资源侵害了您的权益,请速与我们联系,我们将尽快处理.
五、如有其他问题,请加网站设计交流群(点击这里查看交流群 )进行交流。
六、如需转载本站资源,请注明转载来自并附带链接
七、本站部分资源为加密压缩文件,统一解压密码为:www.aizhanzhe.com
- 1CSS控制文字在Div最底部显示
- 2Thinkphp5如何配置IP+端口访问项目模块
- 3elementUI el-dialog弹框居中
- 4教你如何搭建及优化站点
- 5国内互联网视频行业运营分析
- 6service mysql start出错,mysql不能启动,解决mysql: unrecognized service错误
- 7CSS实现悬浮顶部的Div工具栏
- 8记一次Thinkphp5.1框架mysql数据库崩溃(SQLSTATE [08004] Too many connections)
- 9连接SQL Server数据库提示:Login failed for user 'sa'错误的解决方案
- 10Thinkphp3.2在centos7上设置计划任务的方法