本文的解决方案用来解决类似如: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数据结构循环的时候修改该数据结构会抛异常,所以设定了这个单线程和多线程的模式

如果帮到您的话请点一下赞

转发请附上原文链接谢谢

防止网络请求(或其他回调)引用,从而造成内存泄漏相关推荐

  1. Android网络请求中的回调

    需求:高效地向服务器请求数据并解析. 涉及的主要工具类: HttpManager 大哥类.最重要的类,封装了GET和POST方法.所有的参数都要经过这个类发送给服务器. HttpRequestList ...

  2. 你不必使用弱引用以避免内存泄漏

    本文翻译自https://medium.com/google-developer-experts/weakreference-in-android-dd1e66b9be9d#.7gyh5qy6l 我的 ...

  3. Lua弱引用表处理普通的内存泄漏

    Lua中有垃圾回收机制(GC)的,支持GC的对象有五种string,table,function,full userdata,thread,理论上不会有内存泄漏,但是进行GC时,如果这个对象还有引用, ...

  4. android okgo 网络请求框架

    OkGo - OkHttpUtils-2.0.0升级后改名 OkGo,全新完美支持RxJava 项目地址:https://github.com/jeasonlzy,欢迎star,欢迎issue 该库是 ...

  5. android中网络请求中页面关闭了会怎么样

    这是我在面试的时候,别人问的,其实 ,在实际开发中,并没有怎么遇到: 如果activity中开启了一个网络请求,正在请求中的时候,activity关闭了,那网络请求会怎么样? 因为之前没有遇到过这个问 ...

  6. 模仿Retrofit封装一个使用更简单的网络请求框架

    本文已授权微信公众号:郭霖  在微信公众号平台原创首发.会用Retrofit了?你也能自己动手写一个! 前言 想封装一套网络请求,不想直接上来就用别人写好的,或者说对项目可以更好的掌控,所以自己模仿着 ...

  7. 解决网络请求的依赖关系

    怎么解决网络请求的依赖关系:当一个接口的请求需要依赖于另一个网络请求的结果 思路1:操作依赖:NSOperation 操作依赖和优先级(不适用,异步网络请求并不是立刻返回,无法保证回调时再开启下一个网 ...

  8. 如何使用ListView实现一个带有网络请求,解析,分页,缓存的公共的List页面来大大的提高工作效率

    在平常的开发中经常会有很多列表页面,每做一个列表页就需要创建这个布局文件那个Adapter适配器文件等等一大堆与之相关的附属的不必要的冗余文件.如果版本更新迭代比较频繁,如此以往,就会使项目工程变得无 ...

  9. Android中使用Volley开源库进行Http网络请求(GET方式)

    在之前的Http网络请求中,我们一般使用输入流以及缓冲区的方式进行访问,然后从服务器获取返回的数据.代码行数近20行,而且网络操作是放在Thread线程中进行的,对于Java或者Android的线程还 ...

最新文章

  1. Python 操作 MySQL 的5种方式
  2. 需要在函数中传递的变量
  3. bzoj 4012: [HNOI2015]开店 主席树
  4. linux网络编程之用一张图片说明函数inet_ntop、inet_pton、inet_addr、inet_ntoa 、inet_aton函数之间的关系
  5. MingW环境下的windows编程
  6. 动态规划01背包问题入门学习,详细笔记,推荐阅读
  7. iOS开发--开发者帐号
  8. 怎样成为一名优秀的运维工程师
  9. POJ题目分类(转)
  10. Android中CheckBox与CompoundButton源码解析
  11. zktime 协议_ZKTiMe5.0考勤管理系统使用介绍(1.3版).pdf
  12. 上班要了解的一些法律条例
  13. latex和word文档互相转换
  14. 这家公司,打造电竞三冠王SKT,无视法律,韩国总统还得给他赔笑脸
  15. 农历php,PHP阴历转农历的实现代码
  16. 程序员兼职可以从什么平台接私活?你值得知道!!!
  17. 如何删除鼠标右键的显示的快捷键或功能?
  18. SUSCTF 2022圆满落幕,SU战队夺冠
  19. 有xp系统的云服务器,哪个云服务器有xp系统
  20. LaTeX技巧心得28:如何在文中实现带圈的数字和圈中加号

热门文章

  1. 机器学习笔记:误差的来源(bias variance)
  2. 丛高教授《空间数据管理和挖掘及在智慧城市的应用》演讲笔记
  3. 以MATLAB的方式实现微积分问题的计算机求解问题及解决方案集锦(二)
  4. 从无到有算法养成篇-双向链表与双向循环链表
  5. 这个程序,有点意思!
  6. Python--切片学习记录
  7. 【LeetCode从零单排】No 3 Longest Substring Without Repeating Characters
  8. HDFS 原理、架构与特性介绍--转载
  9. mysql测试spring事务是否生效
  10. Lesson 16.1016.1116.1216.13 卷积层的参数量计算,1x1卷积核分组卷积与深度可分离卷积全连接层 nn.Sequential全局平均池化,NiN网络复现