热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Android优化一:Leakcanary检测内存泄漏汇总

Leakcanary检测内存泄漏汇总目录介绍:1.什么是内存泄漏2.内存泄漏造成什么影响3.内存泄漏检测的工具有哪些4.关于Leakcanary使用介绍5.Leakcana

Leakcanary检测内存泄漏汇总
目录介绍:
1.什么是内存泄漏
2.内存泄漏造成什么影响
3.内存泄漏检测的工具有哪些
4.关于Leakcanary使用介绍
5.Leakcanary捕捉常见的内存泄漏及解决办法

  • 5.1 错误使用单例造成的内存泄漏
  • 5.2 错误使用静态变量,导致引用后无法销毁【工具类使用不当导致内存泄漏】
  • 5.3 Handler造成的内存泄漏
  • 5.4 线程造成的内存泄漏
  • 5.5 由WebView引起的内存泄漏
  • 5.6 资源未关闭造成的内存泄漏
  • 5.7 未注销EventBus导致的内存泄漏
  • 5.8 静态集合使用不当导致的内存泄漏
  • 5.9 使用弱引用避免内存泄漏

6.其他

1.什么是内存泄漏?
一些对象有着有限的声明周期,当这些对象所要做的事情完成了,我们希望它们会被垃圾回收器回收掉。但是如果有一系列对这个对象的引用存在,那么在我们期待这个对象生命周期结束时被垃圾回收器回收的时候,它是不会被回收的。它还会占用内存,这就造成了内存泄露。持续累加,内存很快被耗尽。
比如:当Activity的onDestroy()方法被调用后,Activity以及它涉及到的View和相关的Bitmap都应该被回收掉。但是,如果有一个后台线程持有这个Activity的引用,那么该Activity所占用的内存就不能被回收,这最终将会导致内存耗尽引发OOM而让应用crash掉。

2.内存泄漏会造成什么影响?
它是造成应用程序OOM的主要原因之一。由于android系统为每个应用程序分配的内存有限,当一个应用中产生的内存泄漏比较多时,就难免会导致应用所需要的内存超过这个系统分配的内存限额,这就造成了内存溢出而导致应用Crash。

3.内存泄漏检测的工具有哪些
最常见的是:Leakcanary

4.关于Leakcanary使用介绍
leakCanary是Square开源框架,是一个Android和Java的内存泄露检测库,如果检测到某个 activity 有内存泄露,LeakCanary 就是自动地显示一个通知,所以可以把它理解为傻瓜式的内存泄露检测工具。通过它可以大幅度减少开发中遇到的oom问题,大大提高APP的质量。
关于如何配置,这个就不说呢,网上有步骤

5.Leakcanary捕捉常见的内存泄漏及解决办法
* 5.1 错误使用单例造成的内存泄漏
在平时开发中单例设计模式是我们经常使用的一种设计模式,而在开发中单例经常需要持有Context对象,如果持有的Context对象生命周期与单例生命周期更短时,或导致Context无法被释放回收,则有可能造成内存泄漏,错误写法如下:
* 问题引起内存泄漏代码

public class LoginManager {
private static LoginManager mInstance;
private Context mContext;

private LoginManager(Context context) {
this.mCOntext= context; //修改代码:**this.mCOntext= context.getApplicationContext();**
}

public static LoginManager getInstance(Context context) {
if (mInstance == null) {
synchronized (LoginManager.class) {
if (mInstance == null) {
mInstance = new LoginManager(context);
}
}
}
return mInstance;
}

public void dealData() {}
}
  • 在一个Activity中调用的,然后关闭该Activity则会出现内存泄漏。
LoginManager.getInstance(this).dealData();
  • 看看报错

  • 解决办法:
要保证Context和AppLication的生命周期一样,修改后代码如下:
this.mContext = context.getApplicationContext();
  • 原因分析
    创建单例对象,并且在创建的时候需要传入一个Context对象,而这时候如果我们使用Activity、Service等Context对象,由于单例对象的生命周期与进程的生命周期相同,会造成我们传入的Activity、Service对象无法被回收,这时候就需要我们传入Application对象,或者在方法中使用Application对象

  • 5.2 错误使用静态变量,导致引用后无法销毁
    在平时开发中,有时候我们创建了一个工具类。比如分享工具类,十分方便多处调用,因此使用静态方法是十分方便的。但是创建的对象,建议不要全局化,全局化的变量必须加上static。这样会引起内存泄漏!

  • 问题代码
  • 在Activity中引用后,关闭该Activity会导致内存泄漏

DoShareUtil.showFullScreenShareView(PNewsContentActivity.this, title, title, shareurl, logo);
  • 查看报错
  • 解决办法
    静态方法中,创建对象或变量,不要全局化,全局化后的变量或者对象会导致内存泄漏;popMenuView和popMenu都不要全局化

  • 知识延伸

**非静态内部类,静态实例化**
public class MyActivity extends AppCompatActivity {
//静态成员变量
public static InnerClass innerClass = null;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_my);
innerClass = new InnerClass();
}

class InnerClass {
public void doSomeThing() {}
}
}

这里内部类InnerClass隐式的持有外部类MyActivity的引用,而在MyActivity的onCreate方法中调用了。
这样innerClass就会在MyActivity创建的时候是有了他的引用,而innerClass是静态类型的不会被垃圾回收,
MyActivity在执行onDestory方法的时候由于被innerClass持有了引用而无法被回收,所以这样MyActivity就总是被innerClass持有而无法回收造成内存泄露。
  • 5.3 Handler造成的内存泄漏【轮播图无限循环轮播,一定要关闭,否则内存泄漏】
    handler是工作线程与UI线程之间通讯的桥梁,只是现在大量开源框架对其进行了封装,我们这里模拟一种常见使用方式来模拟内存泄漏情形。
  • 问题代码
public class MainActivity extends AppCompatActivity {
private Handler mHandler = new Handler();
private TextView mTextView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTextView = (TextView) findViewById(R.id.text); //模拟内存泄露
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
mTextView.setText("yangchong");
}
}, 2000);
}
}
  • 造成内存泄漏原因分析
    上述代码通过内部类的方式创建mHandler对象,此时mHandler会隐式地持有一个外部类对象引用这里就是MainActivity,当执行postDelayed方法时,该方法会将你的Handler装入一个Message,并把这条Message推到MessageQueue中,MessageQueue是在一个Looper线程中不断轮询处理消息,那么当这个Activity退出时消息队列中还有未处理的消息或者正在处理消息,而消息队列中的Message持有mHandler实例的引用,mHandler又持有Activity的引用,所以导致该Activity的内存资源无法及时回收,引发内存泄漏。

  • 查看报错结果如下:

  • 解决办法
    要想避免Handler引起内存泄漏问题,需要我们在Activity关闭退出的时候的移除消息队列中所有消息和所有的Runnable。
    上述代码只需在onDestroy()函数中调用mHandler.removeCallbacksAndMessages(null);就行了。

@Override
protected void onDestroy() {
super.onDestroy();
if(handler!=null){
handler.removeCallbacksAndMessages(null);
handler = null;
}
}
  • 5.4 线程造成的内存泄漏
    早时期的时候处理耗时操作多数都是采用Thread+Handler的方式,后来逐步被AsyncTask取代,直到现在采用RxJava的方式来处理异步。这里以AsyncTask为例,可能大部分人都会这样处理一个耗时操作然后通知UI更新结果:
  • 问题代码
public class MainActivity extends AppCompatActivity {

private AsyncTask asyncTask;
private TextView mTextView;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTextView = (TextView) findViewById(R.id.text);
testAsyncTask();
finish();
}

private void testAsyncTask() {
asyncTask = new AsyncTask() {
@Override
protected Integer doInBackground(Void... params) {
int i = 0;
//模拟耗时操作
while (!isCancelled()) {
i++;
if (i > 1000000000) {
break;
}
Log.e("LeakCanary", "asyncTask---->" + i);
}
return i;
}

@Override
protected void onPostExecute(Integer integer) {
super.onPostExecute(integer);
mTextView.setText(String.valueOf(integer));
}
};
asyncTask.execute();
}
}
  • 造成内存泄漏原因分析
    在处理一个比较耗时的操作时,可能还没处理结束MainActivity就执行了退出操作,但是此时AsyncTask依然持有对MainActivity的引用就会导致MainActivity无法释放回收引发内存泄漏

  • 查看报错结果如下:

  • 解决办法
    在使用AsyncTask时,在Activity销毁时候也应该取消相应的任务AsyncTask.cancel()方法,避免任务在后台执行浪费资源,进而避免内存泄漏的发生

private void destroyAsyncTask() {
if (asyncTask != null && !asyncTask.isCancelled()) {
asyncTask.cancel(true);
}
asyncTask = null;
}

@Override
protected void onDestroy() {
super.onDestroy();
destroyAsyncTask();
}
  • 5.5 由WebView引起的内存泄漏
    WebView解析网页时会申请Native堆内存用于保存页面元素,当页面较复杂时会有很大的内存占用。如果页面包含图片,内存占用会更严重。并且打开新页面时,为了能快速回退,之前页面占用的内存也不会释放。有时浏览十几个网页,都会占用几百兆的内存。这样加载网页较多时,会导致系统不堪重负,最终强制关闭应用,也就是出现应用闪退或重启
  • 问题代码
public class MainActivity5 extends AppCompatActivity {
private WebView mWebView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_web);
mWebView = (WebView) findViewById(R.id.web);
mWebView.loadUrl("http://www.cnblogs.com/whoislcj/p/5720202.html");
}
}
  • 造成内存泄漏原因分析
    加载网页有缓存,当加载了许多网页,并且手机配置比较低时,造成的内存泄漏就对手机影响很大

  • 查看报错结果如下:

  • 解决办法

@Override
protected void onDestroy() {
if (mWebView != null) {
mWebView.loadDataWithBaseURL(null, "", "text/html", "utf-8", null);
mWebView.clearHistory();
ViewGroup parent = (ViewGroup) mWebView.getParent();
if(parent!=null){
parent.removeView(mWebView);
}
mWebView.destroy();
mWebView = null;
}
super.onDestroy();
}
  • 5.6 资源未关闭造成的内存泄漏
    对于使用了BraodcastReceiver,ContentObserver,File,Cursor,Stream,Bitmap等资源的使用,应该在Activity销毁时及时关闭或者注销,否则这些资源将不会被回收,造成内存泄漏。

  • 5.7 未注销EventBus导致的内存泄漏
    直接展示代码

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_common);
EventBus.getDefault().register(this);
}

@Subscribe
public void onEvent(MessageEvent msg) {

}

@Override
protected void onDestroy() {
super.onDestroy();
//未移除注册的EventBus
//EventBus.getDefault().unregister(this);
}
  • 5.8 静态集合使用不当导致的内存泄漏
    添加Activity到栈,或者移除Activity出栈。导致内存泄漏
public class ActivityCollector {
public static List activities = new ArrayList();
public static void addActivity(Activity activity) {
activities.add(activity);
}
public static void removeActivity(Activity activity) {
activities.remove(activity);
}
}

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_common);
ActivityCollector.addActivity(this);
}

@Override
protected void onDestroy() {
super.onDestroy();
//静态集合没有移除元素
//ActivityCollector.removeActivity(this);
}
  • 5.9 使用弱引用避免内存泄漏
    在 Activity 中避免使用非静态内部类,比如上面我们将 Handler 声明为静态的,则其存活期跟 Activity 的生命周期就无关了。同时通过弱引用的方式引入 Activity,避免直接将 Activity 作为 context 传进去
public class WeakReferenceActivity extends AppCompatActivity {

//将Handler声明为静态 没有了Activity的引用, 无法直接引用其变量或方法,
//使用弱引用WeakReference来解决这个问题
private static class DBHandler extends Handler {
//弱引用, 而不是使用外部类this或者传进来
private final WeakReference mActivity;
public DBHandler(WeakReferenceActivity activity) {
mActivity = new WeakReference<>(activity);
}
@Override
public void handleMessage(Message msg) {
WeakReferenceActivity activity = mActivity.get();
if (activity != null) {
Toast.makeText(activity, "what: " + msg.what, Toast.LENGTH_SHORT).show();
}
}
}

private final DBHandler mHandler = new DBHandler(this);

private static final Runnable sRunnable = new Runnable() {
@Override
public void run() {
Toast.makeText(App.getInstance(), "sRunnable run()", Toast.LENGTH_SHORT).show();
}
};

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_weak_reference);
mHandler.postDelayed(sRunnable, 1000 * 20);
}

public void onClick(View view) {
mHandler.sendEmptyMessage(new Random().nextInt(10));
}
}

后续:
平时喜欢写写文章,笔记。别人建议我把笔记,以前写的东西整理,然后写成博客,所以我会陆续整理文章,只发自己写的东西,敬请期待。如果有什么问题或者需要笔记,可以直接联系我,可以发送印象笔记文档给你!
知乎:https://www.zhihu.com/people/yang-chong-69-24/pins/posts
领英:https://www.linkedin.com/in/chong-yang-049216146/
简书:http://www.jianshu.com/u/b7b2c6ed9284
csdn:http://my.csdn.net/m0_37700275
网易博客:http://yangchong211.blog.163.com/
新浪博客:http://blog.sina.com.cn/786041010yc
github:https://github.com/yangchong211
喜马拉雅听书:http://www.ximalaya.com/zhubo/71989305/
脉脉:yc930211
开源中国:https://my.oschina.net/zbj1618/blog
邮箱:yangchong211@163.com


推荐阅读
  • Android 自定义 RecycleView 左滑上下分层示例代码
    为了满足项目需求,需要在多个场景中实现左滑删除功能,并且后续可能在列表项中增加其他功能。虽然网络上有很多左滑删除的示例,但大多数封装不够完善。因此,我们尝试自己封装一个更加灵活和通用的解决方案。 ... [详细]
  • 如果应用程序经常播放密集、急促而又短暂的音效(如游戏音效)那么使用MediaPlayer显得有些不太适合了。因为MediaPlayer存在如下缺点:1)延时时间较长,且资源占用率高 ... [详细]
  • 本文介绍如何在 Android 中自定义加载对话框 CustomProgressDialog,包括自定义 View 类和 XML 布局文件的详细步骤。 ... [详细]
  • 实验九:使用SharedPreferences存储简单数据
    本实验旨在帮助学生理解和掌握使用SharedPreferences存储和读取简单数据的方法,包括程序参数和用户选项。 ... [详细]
  • 本文介绍了一种自定义的Android圆形进度条视图,支持在进度条上显示数字,并在圆心位置展示文字内容。通过自定义绘图和组件组合的方式实现,详细展示了自定义View的开发流程和关键技术点。示例代码和效果展示将在文章末尾提供。 ... [详细]
  • 【问题】在Android开发中,当为EditText添加TextWatcher并实现onTextChanged方法时,会遇到一个问题:即使只对EditText进行一次修改(例如使用删除键删除一个字符),该方法也会被频繁触发。这不仅影响性能,还可能导致逻辑错误。本文将探讨这一问题的原因,并提供有效的解决方案,包括使用Handler或计时器来限制方法的调用频率,以及通过自定义TextWatcher来优化事件处理,从而提高应用的稳定性和用户体验。 ... [详细]
  • 使用 ListView 浏览安卓系统中的回收站文件 ... [详细]
  • 在处理 XML 数据时,如果需要解析 `` 标签的内容,可以采用 Pull 解析方法。Pull 解析是一种高效的 XML 解析方式,适用于流式数据处理。具体实现中,可以通过 Java 的 `XmlPullParser` 或其他类似的库来逐步读取和解析 XML 文档中的 `` 元素。这样不仅能够提高解析效率,还能减少内存占用。本文将详细介绍如何使用 Pull 解析方法来提取 `` 标签的内容,并提供一个示例代码,帮助开发者快速解决问题。 ... [详细]
  • ButterKnife 是一款用于 Android 开发的注解库,主要用于简化视图和事件绑定。本文详细介绍了 ButterKnife 的基础用法,包括如何通过注解实现字段和方法的绑定,以及在实际项目中的应用示例。此外,文章还提到了截至 2016 年 4 月 29 日,ButterKnife 的最新版本为 8.0.1,为开发者提供了最新的功能和性能优化。 ... [详细]
  • 深入解析 Lifecycle 的实现原理
    本文将详细介绍 Android Jetpack 中 Lifecycle 组件的实现原理,帮助开发者更好地理解和使用 Lifecycle,避免常见的内存泄漏问题。 ... [详细]
  • 在Android开发中,当TextView的高度固定且内容超出时,可以通过设置其内置的滚动条属性来实现垂直滚动功能。具体来说,可以通过配置`android:scrollbars="vertical"`来启用垂直滚动,确保用户能够查看完整的内容。此外,为了优化用户体验,建议结合`setMovementMethod(ScrollerMovementMethod.getInstance())`方法,使滚动操作更加流畅和自然。 ... [详细]
  • 深入解析 Android 中 EditText 的 getLayoutParams 方法及其代码应用实例 ... [详细]
  • javax.mail.search.BodyTerm.matchPart()方法的使用及代码示例 ... [详细]
  • 在Cisco IOS XR系统中,存在提供服务的服务器和使用这些服务的客户端。本文深入探讨了进程与线程状态转换机制,分析了其在系统性能优化中的关键作用,并提出了改进措施,以提高系统的响应速度和资源利用率。通过详细研究状态转换的各个环节,本文为开发人员和系统管理员提供了实用的指导,旨在提升整体系统效率和稳定性。 ... [详细]
  • Squaretest:自动生成功能测试代码的高效插件
    本文将介绍一款名为Squaretest的高效插件,该工具能够自动生成功能测试代码。使用这款插件的主要原因是公司近期加强了代码质量的管控,对各项目进行了严格的单元测试评估。Squaretest不仅提高了测试代码的生成效率,还显著提升了代码的质量和可靠性。 ... [详细]
author-avatar
陈杰倩平贵奕白
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有