Android内存泄漏的预防

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

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

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

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

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

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

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

1、关闭游标

    游标Cursor不止用于数据库SQLite查询记录,也可用于内容解析器ContentResolver查询内容数据,还可用于下载管理器DownloadManager查询下载进度。

    若要预防游标产生的内存泄漏,则可在每次查询操作结束后调用Cursor对象的close方法关闭游标。

2、重用适配

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

    重用适配可先判断convertView对象,如果该对象为空,就为其分配视图对象,并调用setTag方法保存视图持有者;如果该对象非空,就调用getTag方法获取视图持有者。

    每次给ListView与GridView构造适配器都要加入重用代码,已经成了开发者的一大负担。所以Android在5.0之后推出了循环视图RecyclerView,它的适配器自动实现视图持有者ViewHolder,无须开发者进行重用判断的处理,算是一件善事。

 3、回收图像

    Android虽然定义了Bitmap类,但是读取图像数据的底层操作并非由Java代码完成。查看SDK源码,在BitmapFactory类中一路跟踪到nativeDecodeStream函数,发现它其实是一个native方法,也就是该方法来自于JNI接口。既然Bitmap的图像数据实际来自于C/C++代码,那么确实得手工释放C/C++的内存资源。查看Bitmap类的源码,它的回收方法recycle用到的nativeRecycle函数其实也是一个native方法,同样来自于JNI接口。

   因此,若想避免图像操作引起的内存泄漏,可在Bitmap对象使用完毕后调用recycle方法。举一反三,只要一个资源实在JNI接口中分配的,一旦不再使用该资源,就得手工调用该资源对应的JNI回收接口。

 4、释放引用

    编写Handler的处理函数时,Android Studio提示This Handler class should be static or leaks might occur,意思是这个类应该是一个静态类,否则可能发生内存泄漏。因为Handler对象经常处理异步任务,每当它调用postDelayed方法执行一个任务时,依据延时间隔都得等待一段时间,倘若活动页面在此期间退出,就会导致异步任务持有的引用无法回收。由于Runnable通常持有Activity的引用,因此造成Activity资源都无法回收。

    上面的描述可能不好理解,确实也不容易解释清楚,还是直接跳过烦琐的概念,讲讲如何解决该情况的内存泄漏问题。下面是预防这种内存泄漏的3个方法:

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

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

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

    3)把Handler对象作为App的全局变量,即把Handler对象作为自定义Application类的成员变量。

    这样只要App在运行,该对象就一直存在。既然避免为Handler对象重复分配内存,也就间接避免了内存泄漏的可能。

5、注销监听

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


你可能感兴趣的:(Android,Studio)