防止网络请求(或其他回调)引用,从而造成内存泄漏
本文的解决方案用来解决类似如: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数据结构循环的时候修改该数据结构会抛异常,所以设定了这个单线程和多线程的模式
如果帮到您的话请点一下赞
转发请附上原文链接谢谢
防止网络请求(或其他回调)引用,从而造成内存泄漏相关推荐
- Android网络请求中的回调
需求:高效地向服务器请求数据并解析. 涉及的主要工具类: HttpManager 大哥类.最重要的类,封装了GET和POST方法.所有的参数都要经过这个类发送给服务器. HttpRequestList ...
- 你不必使用弱引用以避免内存泄漏
本文翻译自https://medium.com/google-developer-experts/weakreference-in-android-dd1e66b9be9d#.7gyh5qy6l 我的 ...
- Lua弱引用表处理普通的内存泄漏
Lua中有垃圾回收机制(GC)的,支持GC的对象有五种string,table,function,full userdata,thread,理论上不会有内存泄漏,但是进行GC时,如果这个对象还有引用, ...
- android okgo 网络请求框架
OkGo - OkHttpUtils-2.0.0升级后改名 OkGo,全新完美支持RxJava 项目地址:https://github.com/jeasonlzy,欢迎star,欢迎issue 该库是 ...
- android中网络请求中页面关闭了会怎么样
这是我在面试的时候,别人问的,其实 ,在实际开发中,并没有怎么遇到: 如果activity中开启了一个网络请求,正在请求中的时候,activity关闭了,那网络请求会怎么样? 因为之前没有遇到过这个问 ...
- 模仿Retrofit封装一个使用更简单的网络请求框架
本文已授权微信公众号:郭霖 在微信公众号平台原创首发.会用Retrofit了?你也能自己动手写一个! 前言 想封装一套网络请求,不想直接上来就用别人写好的,或者说对项目可以更好的掌控,所以自己模仿着 ...
- 解决网络请求的依赖关系
怎么解决网络请求的依赖关系:当一个接口的请求需要依赖于另一个网络请求的结果 思路1:操作依赖:NSOperation 操作依赖和优先级(不适用,异步网络请求并不是立刻返回,无法保证回调时再开启下一个网 ...
- 如何使用ListView实现一个带有网络请求,解析,分页,缓存的公共的List页面来大大的提高工作效率
在平常的开发中经常会有很多列表页面,每做一个列表页就需要创建这个布局文件那个Adapter适配器文件等等一大堆与之相关的附属的不必要的冗余文件.如果版本更新迭代比较频繁,如此以往,就会使项目工程变得无 ...
- Android中使用Volley开源库进行Http网络请求(GET方式)
在之前的Http网络请求中,我们一般使用输入流以及缓冲区的方式进行访问,然后从服务器获取返回的数据.代码行数近20行,而且网络操作是放在Thread线程中进行的,对于Java或者Android的线程还 ...
最新文章
- Python 操作 MySQL 的5种方式
- 需要在函数中传递的变量
- bzoj 4012: [HNOI2015]开店 主席树
- linux网络编程之用一张图片说明函数inet_ntop、inet_pton、inet_addr、inet_ntoa 、inet_aton函数之间的关系
- MingW环境下的windows编程
- 动态规划01背包问题入门学习,详细笔记,推荐阅读
- iOS开发--开发者帐号
- 怎样成为一名优秀的运维工程师
- POJ题目分类(转)
- Android中CheckBox与CompoundButton源码解析
- zktime 协议_ZKTiMe5.0考勤管理系统使用介绍(1.3版).pdf
- 上班要了解的一些法律条例
- latex和word文档互相转换
- 这家公司,打造电竞三冠王SKT,无视法律,韩国总统还得给他赔笑脸
- 农历php,PHP阴历转农历的实现代码
- 程序员兼职可以从什么平台接私活?你值得知道!!!
- 如何删除鼠标右键的显示的快捷键或功能?
- SUSCTF 2022圆满落幕,SU战队夺冠
- 有xp系统的云服务器,哪个云服务器有xp系统
- LaTeX技巧心得28:如何在文中实现带圈的数字和圈中加号
热门文章
- 机器学习笔记:误差的来源(bias variance)
- 丛高教授《空间数据管理和挖掘及在智慧城市的应用》演讲笔记
- 以MATLAB的方式实现微积分问题的计算机求解问题及解决方案集锦(二)
- 从无到有算法养成篇-双向链表与双向循环链表
- 这个程序,有点意思!
- Python--切片学习记录
- 【LeetCode从零单排】No 3 Longest Substring Without Repeating Characters
- HDFS 原理、架构与特性介绍--转载
- mysql测试spring事务是否生效
- Lesson 16.1016.1116.1216.13 卷积层的参数量计算,1x1卷积核分组卷积与深度可分离卷积全连接层 nn.Sequential全局平均池化,NiN网络复现