Android-内存泄漏处理(1)

//移除定时任务

mHandler.removeCallbacks(mTask);

}

isRunning=!isRunning;

}

}

protected void onDestroy(){

super.onDestroy();

if(ck_remove.isChecked()){

//移除定时任务

mHandler.removeCallbacks(mTask);

}

}

//定义一个定时任务,用于定时发送广播

private Runnable mTask=new Runnable(){

@Override

public void run{

Intent intent=new Intent(TASK_EVENT);

//通过本地的广播管理器来发送广播

LocalBroadcastManager.getInstance(RemoveTaskActivity.this).sendBroadcast(intent);

//延迟2秒后再次启动定时任务

mHandler.postDelayed(this,2000);

}

};

public void onStart(){

super.onStart();

//创建一个定时任务的广播接收器

taskReceiver=new TaskReceiver();

//创建一个意图过滤器,只处理指定事件来源的广播

IntentFilter filter=new Intentfilter(TASK_EVENT);

//注册广播接收器,注册之后才能正常接受广播

LocalBroadcastManager.getInstance(this).registerReceiver(taskReceiver,filter);

}

public void onStop(){

//注销广播接收器,注销之后就不再接受广播

LocalBroadcastManager.getInstance(this).registerReceiver(taskReceiver,filter);

}

//声明一个定时任务广播事件的标识串

private String TASK_EVENT=“com.example.performance.task”;

//声明一个定时任务的广播接收器

private TaskReceiver taskReceiver;

//定义一个广播接收器,用于处理定时任务事件

private class TaskReceiver extends BroadcastReceiver{

//在收到定时任务的广播时触发

public void onReceive(Context contect,Intent intent){

if(intent !=null){

mDesc=String.format(“%s%s 打印了一行测试日志\n”,mDesc,DateUtil.getNowTime());

tv_remove.setText(mDesc);

}

}

}

进入测试页面,点击开始执行任务按钮,页面会每隔2秒打印一行日志。然后不停止也不移除定时任务,直接退出该页面,按道理原测试页面上的内存都应该回收,不过接着进入测试页面,还没点击开始执行任务按钮,页面已经在自己打印日志了,很明显上次退出页面时系统未能自动的回收内存。

内存泄漏的预防


App开发中的内存泄露还常见于以下5个场景:

(1)数据库查询操作后没有关闭游标Cursor。

(2)适配器Adapter刷新数据时没有重用convertView对象。

(3)Bitmap对象使用完毕没有调用recycle方法回收内存。

(4)Activity引用了耗时对象,造成页面关闭时无法释放被引用的对象。

(5)给系统服务注册了监听任务,却没有及时注销。

要想避免出现内存泄漏,最好的办法就是防患于未然。针对以上5个内存泄漏场景,相应的预防措施分别如下:

  1. 关闭游标–游标Cursor不只用于数据库SQLite查询记录,也可用于内容解析器ContentResolver查询内容数据,还可以用于下载管理器DownloadManager查询下载进度。若要预防游标产生的内存泄漏,则可在每次查询操作结束后调用Cursor对象的close方法关闭游标。

  2. 重用适配–App网列表视图ListView或网格视图GirdView中填充数据都是通过适配器BaseAdapter的getView方法展示列表元素。列表元素较多时,系统只会加载屏幕上可见元素,其他元素只有滑动到屏幕区域内才会及时加载并显示。当列表元素多次处于“展示—>隐藏—>展示—>隐藏·····”时,有必要重用每个元素的视图如果不重用,那么每次展示可视元素都得重新分配视图对象,这便产生了内存泄漏。下面是重要列表元素的代码示例:

ViewHolder holder;

if(convertView==null){

holder=new ViewHolder();

convertView=mInflater.inflate(R.layout.list_title,null);

holder.tv_seq=(TextView)convertViewById(R.id.tv_seq);

holder.iv_title=(TextView)convertViewById(R.id.iv_title);

convertView.setTag(holder);

}else{

holder=(ViewHolder)convertView.getTag();

}

  1. 回收图像–若想避免图像操作引起的内存泄漏,可在Bitmap对象使用完毕后调用recycle方法。

  2. 释放引用–下面是预防这种内存泄漏的3个方法:

(1)如果异步任务时由Handler对象的postDelayed方法发起的,那么可能对应的removeCallbacks方法回收,把消息对象从消息队列移除就行了。

(2)按Android官方的推荐做法,可把Handler类改为静态类,同时Handler内部使用WeakReference关键字持有目标的引用。

之所以使用静态类,是因为静态类不持有目标的引用,不会影响内存自动回收机制。但是不持有目标的引用,Handler内部就无法操作Activity上面的控件。为解决该问题,在构造Handler类时需要初始化目标的弱引用。不同于前面的强引用,弱引用相当于一个指针,指针指向的地址随时可以回收。这又出现新的问题,即弱引用指向的对象可能是空的,所以Handler内部在使用目标活动前要先判断弱引用对象是否为空。以下是代码示例:

private WeakHandler mHandler=new WeakHandler(this);

private static class WeakHandler extends Handler{

public static WeakReferencemActivity;

public WeakHandler(ReferWeakActivity activity){

mActivity=new WeakReference(activity);

}

public void handleMessage(Message msg){

ReferWeakActivity act=mActivity.get();

if(act !=null){

act.mDesc=String.format(“%s%s 打印了一行测试日志\n”,act.mDesc,DateUtil.getNowTime());

act.tv_weak.setText(act.mDesc);

}

}

}

(3)把Handler对象作为App的全局变量,即把Handler对象作为自定义Application类的成员变量。这样只要App在运行,该对象就一直存在。既然避免为Handler对象重复分配内存,也就间接避免了内存泄露的可能。

  1. 注销监听–App的某些功能依赖于Android的系统服务,比如定位功能依赖于系统的定位管理器,定时功能依赖于系统的闹钟管理器。App若想接收系统服务的消息,要么注册监听器,在回调方法中处理信息;要么注册广播接收器,在接收广播时处理消息。既然有注册操作,就存在对应的注销操作,不过如果不注意,就会忘记在代码中做注销处理。所以在进行页面编码时,千万要记得再检查一遍,确保onDestroy方法中已经包含相关的注销代码。

不同的的系统服务拥有不同的注销方法常见的系统服务注销方法见表:

| 系统服务的管理器 | 注销操作说明 | 注销函数 |

| :-: | :-: | :-: |

| AlarmManager | 取消定时广播 | cancel |

| ConnectivityManager | 取消监听网络状态 | unregisterNetworkCallback |

| DownloadManager | 移除下载任务 | remove |

| LocationManager | 取消监听位置信息的变化 | removeUpdates |

| LocationManager | 取消监听定位状态的变化 | removeGpsStatusListener |

| NotificationManager | 取消通知 | cancel |

| TelephonyManager | 取消监听电话状态 | 使用listen方法注册一个空事件PhoneStateListener.LISTEN_NONE |
| Vibrator | 取消震动 | cancel |
作者:暴何丽
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

img

img

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)

尾声

如果你想成为一个优秀的 Android 开发人员,请集中精力,对基础和重要的事情做深度研究。

对于很多初中级Android工程师而言,想要提升技能,往往是自己摸索成长,不成体系的学习效果低效漫长且无助。 整理的这些架构技术希望对Android开发的朋友们有所参考以及少走弯路,本文的重点是你有没有收获与成长,其余的都不重要,希望读者们能谨记这一点。

这里,笔者分享一份从架构哲学的层面来剖析的视频及资料给大家梳理了多年的架构经验,筹备近6个月最新录制的,相信这份视频能给你带来不一样的启发、收获。

Android进阶学习资料库

一共十个专题,包括了Android进阶所有学习资料,Android进阶视频,Flutter,java基础,kotlin,NDK模块,计算机网络,数据结构与算法,微信小程序,面试题解析,framework源码!

大厂面试真题

PS:之前因为秋招收集的二十套一二线互联网公司Android面试真题 (含BAT、小米、华为、美团、滴滴)和我自己整理Android复习笔记(包含Android基础知识点、Android扩展知识点、Android源码解析、设计模式汇总、Gradle知识点、常见算法题汇总。)

《2019-2021字节跳动Android面试历年真题解析》

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

Android基础知识点、Android扩展知识点、Android源码解析、设计模式汇总、Gradle知识点、常见算法题汇总。)

[外链图片转存中…(img-LwfHuf3C-1713777276964)]

《2019-2021字节跳动Android面试历年真题解析》

[外链图片转存中…(img-O0H3ROH6-1713777276965)]

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

  • 5
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值