}else{
btn_remove.setText(“开始定时任务”);
//移除定时任务
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个内存泄漏场景,相应的预防措施分别如下:
-
关闭游标–游标Cursor不只用于数据库SQLite查询记录,也可用于内容解析器ContentResolver查询内容数据,还可以用于下载管理器DownloadManager查询下载进度。若要预防游标产生的内存泄漏,则可在每次查询操作结束后调用Cursor对象的close方法关闭游标。
-
重用适配–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();
}
-
回收图像–若想避免图像操作引起的内存泄漏,可在Bitmap对象使用完毕后调用recycle方法。
-
释放引用–下面是预防这种内存泄漏的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对象重复分配内存,也就间接避免了内存泄露的可能。
- 注销监听–App的某些功能依赖于Android的系统服务,比如定位功能依赖于系统的定位管理器,定时功能依赖于系统的闹钟管理器。App若想接收系统服务的消息,要么注册监听器,在回调方法中处理信息;要么注册广播接收器,在接收广播时处理消息。既然有注册操作,就存在对应的注销操作,不过如果不注意,就会忘记在代码中做注销处理。所以在进行页面编码时,千万要记得再检查一遍,确保onDestroy方法中已经包含相关的注销代码。
不同的的系统服务拥有不同的注销方法常见的系统服务注销方法见表:
| 系统服务的管理器 | 注销操作说明 | 注销函数 |
| :-: | :-: | :-: |
| AlarmManager | 取消定时广播 | cancel |
| ConnectivityManager | 取消监听网络状态 | unregisterNetworkCallback |
| DownloadManager | 移除下载任务 | remove |
| LocationManager | 取消监听位置信息的变化 | removeUpdates |
| LocationManager | 取消监听定位状态的变化 | removeGpsStatusListener |
| NotificationManager | 取消通知 | cancel |
结尾
最后,针对上面谈的内容,给大家推荐一个Android资料,应该对大家有用。
首先是一个知识清单:(对于现在的Android及移动互联网来说,我们需要掌握的技术)
泛型原理丶反射原理丶Java虚拟机原理丶线程池原理丶
注解原理丶注解原理丶序列化
Activity知识体系(Activity的生命周期丶Activity的任务栈丶Activity的启动模式丶View源码丶Fragment内核相关丶service原理等)
代码框架结构优化(数据结构丶排序算法丶设计模式)
APP性能优化(用户体验优化丶适配丶代码调优)
热修复丶热升级丶Hook技术丶IOC架构设计
NDK(c编程丶C++丶JNI丶LINUX)
如何提高开发效率?
MVC丶MVP丶MVVM
微信小程序
Hybrid
Flutter
接下来是资料清单:(敲黑板!!!)
1.数据结构和算法
2.设计模式
3.全套体系化高级架构视频;七大主流技术模块,视频+源码+笔记
4.面试专题资料包(怎么能少了一份全面的面试题总结呢~)
不论遇到什么困难,都不应该成为我们放弃的理由!共勉~
如果你看到了这里,觉得文章写得不错就给个赞呗?如果你觉得那里值得改进的,请给我留言。一定会认真查询,修正不足。谢谢。
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门,即可获取!
4.面试专题资料包*(怎么能少了一份全面的面试题总结呢~)
[外链图片转存中…(img-odwh0T7k-1714700438402)]
不论遇到什么困难,都不应该成为我们放弃的理由!共勉~
如果你看到了这里,觉得文章写得不错就给个赞呗?如果你觉得那里值得改进的,请给我留言。一定会认真查询,修正不足。谢谢。
[外链图片转存中…(img-u3P1zk7Z-1714700438402)]
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门,即可获取!