分析Android应用中内存泄漏的技巧

一、什么是内存泄漏

内存泄漏(Memory Leak)是指一块已经被开辟的堆内存由于某种原因程序未释放或无法释放,使得这部分内存长时间不能被使用,最终导致程序运行速度减慢甚至崩溃。

Android应用中的内存泄漏是指在应用中申请的内存空间,没有被程序释放,一直存在于内存当中,最终导致应用卡顿、崩溃等问题。内存泄漏往往难以发现,但是一旦发现,一定要及时修复,否则成倍的内存泄漏最终会导致应用的崩溃。

二、如何发现内存泄漏

Android提供了一些工具,如内存分析器,帮助我们检测Android应用中的内存泄漏。

在Android Studio中,我们可以使用Android Profiler来检测内存泄漏。在Profier中,可以查看应用的内存使用情况、资源使用情况和网络数据传输等信息。其中,在内存选项卡中,可以分别查看Java Heap、Native Heap和GPU Memory的情况,以及Allocation Tracker和Heap Dump的信息。

//使用LeakCanary监测内存泄漏:
dependencies {
   debugImplementation 'com.squareup.leakcanary:leakcanary-android:1.6.3'
}
public class ExampleApplication extends Application {
  @Override public void onCreate() {
    super.onCreate();
    if (LeakCanary.isInAnalyzerProcess(this)) {
      // This process is dedicated to LeakCanary for heap analysis.
      // You should not init your app in this process.
      return;
    }
    LeakCanary.install(this);
    // Normal app init code...
  }
}

三、内存泄漏的常见场景

1. 对象持有

当一个Activity结束时,如果这个Activity中包含的控件仍被其他地方所持有,这个Activity所占用的内存就无法释放。比如当一个Activity持有了一些Bitmap,并将其传递给了其他对象使用,但是没有及时trim内存或者内存不足时,这些Bitmap就会导致内存泄漏。

public class ExampleActivity extends Activity {
  private ImageView mImageView;

  @Override public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_example);
    mImageView = findViewById(R.id.image);
    // 使用了R.drawable.large_image这张大图
    mImageView.setImageResource(R.drawable.large_image);
  }
}

在以上代码中,如果这张大图没有及时回收,这将会导致内存一直暴增,程序异常。

2. 静态变量持有对象

静态变量是不能被GC回收的,如果静态变量持有了Activity等Context对象,那么在Activity被回收之前,GC将不能回收Activity所占用的内存。

以下代码演示了静态变量引用了Activity,从而导致Activity不能正常实现回收,API 21及以上的系统机制,可将该Activity加入“历史记录列表”,便可被回收。

public class ExampleActivity extends Activity {
    private static Activity sCurrentActivity; 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_example);
        sCurrentActivity = this;     
    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
        sCurrentActivity = null;   
    }
}

3. Handler导致泄露

Handler类在Android开发中经常被用来做一些异步的操作,带有延时的Runnable容易导致内存被泄漏。

public class ExampleActivity extends Activity {
  private Handler mHandler = new Handler() {
    @Override
    public void handleMessage(Message msg) {
      // do some thing;
    }
  };
  
  private void postSomeThingDelay() {
    mHandler.postDelayed(new Runnable() {
      @Override
      public void run() {
      }
    }, 2000);
  }
}

在以上代码中,如果Activity被销毁,则handler将长期持有它,并阻止它被回收。

4. 监听器导致泄露

监听器是一个常用的功能,在Android中有诸如setOnClickListener、addTextChangedListener等监听器。如果一个对象把它自己传给第三方,并注册一个监听器,那么这个对象将不能被GC回收。

public class ExampleActivity extends Activity {
  private EditText editText;
 
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    editText = findViewById(R.id.edit_text);
    editText.addTextChangedListener(new TextWatcher() {
      @Override
      public void beforeTextChanged(CharSequence s, int start, int count, int after) {
      }
      @Override
      public void onTextChanged(CharSequence s, int start, int before, int count) {
      }
      @Override
      public void afterTextChanged(Editable s) {
      }
    });
  }
}

在以上代码中,如果EditText对象被销毁之前,内存泄漏将被固定。

四、解决内存泄漏

发现内存泄漏后,要尽快进行解决,以下是几点解决方案。

1. 及时释放对象持有的资源

在Activity的onStop()或onDestroy()中,需要释放那些在onCreate()中创建的资源。如Image、Bitmap等。

2. 定期调用System.gc()

System.gc()的作用是告诉GC执行送达,但并不强制GC立刻执行,GC并不是万能的,它需要系统去为其提供垃圾收集的条件。所以并不建议调用。

3. 使用WeakReference和软引用

使用这两种引用类型可以避免长时间持有Activity对象,当对象没有其他引用时,会被GC自动回收。

public class ExampleActivity extends Activity {
  private WeakReference mContext;

  @Override public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    mContext = new WeakReference(this);
  }

  private void doSomething() {
    if (mContext.get() != null) {
      // do something
    }
  }
}

4. 使用HandlerThread

将线程和Handler分离使用,这样Handler执行结束时,Thread不会立刻结束。

public class ExampleActivity extends Activity {

  private HandlerThread mHandlerThread;

  @Override public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    mHandlerThread = new HandlerThread("MyHandlerThread");
    mHandlerThread.start();
    Handler mHandler = new Handler(mHandlerThread.getLooper()) {
      @Override public void handleMessage(Message msg) {
        // do some thing
      }
    };
  }

  @Override protected void onDestroy() {
    super.onDestroy();
    mHandlerThread.quit();
  }
}

5. 使用LeakCanary进行定位

LeakCanary是一个强大的Android库,可以在应用内部发现内存泄漏。其会在弱引用对象被回收时,记录下调用堆栈(如果发现调用堆栈在不断增大,那么很可能存在泄漏),当我们检测到一个对象已经被泄露时,可以通过查看堆转储文件,查找泄露原因。

总结

内存泄漏是Android开发中常见的问题,对应的解决方案也有很多,但是依然需要开发者掌握一些技巧才能更好地避免内存泄漏。多检查自己的代码,适时使用各种工具进行检测和解决。

原创文章,作者:小蓝,如若转载,请注明出处:https://www.506064.com/n/295430.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
小蓝小蓝
上一篇 2024-12-27 12:56
下一篇 2024-12-27 12:56

相关推荐

  • 使用vscode建立UML图的实践和技巧

    本文将重点介绍在使用vscode在软件开发中如何建立UML图,并且给出操作交互和技巧的指导。 一、概述 在软件开发中,UML图是必不可少的重要工具之一。它为软件架构和各种设计模式的…

    编程 2025-04-29
  • Python创建分配内存的方法

    在python中,我们常常需要创建并分配内存来存储数据。不同的类型和数据结构可能需要不同的方法来分配内存。本文将从多个方面介绍Python创建分配内存的方法,包括列表、元组、字典、…

    编程 2025-04-29
  • Python变量在内存中的存储

    该文章将从多个方面对Python变量在内存中的存储进行详细阐述,包括变量的声明和赋值、变量的引用和指向、内存地址的变化、内存管理机制等。 一、声明和赋值 在Python中,变量声明…

    编程 2025-04-29
  • 优秀周记1000字的撰写思路与技巧

    优秀周记是每个编程开发工程师记录自己工作生活的最佳方式之一。本篇文章将从周记的重要性、撰写思路、撰写技巧以及周记的示例代码等角度进行阐述。 一、周记的重要性 作为一名编程开发工程师…

    编程 2025-04-28
  • Python计算内存占用

    Python是一种高级的、解释性的、面向对象的、动态的程序语言,因其易于学习、易于阅读、可移植性好等优点,越来越受到开发者的青睐。当我们编写Python代码时,可能经常需要计算程序…

    编程 2025-04-28
  • 使用Go-Redis获取Redis集群内存使用率

    本文旨在介绍如何使用Go-Redis获取Redis集群的内存使用率。 一、Go-Redis简介 Go-Redis是一个用于连接Redis服务器的Golang客户端。它支持Redis…

    编程 2025-04-28
  • Android ViewPager和ScrollView滑动冲突问题

    Android开发中,ViewPager和ScrollView是两个常用的控件。但是当它们同时使用时,可能会发生滑动冲突的问题。本文将从多个方面介绍解决Android ViewPa…

    编程 2025-04-28
  • Android如何点击其他区域收起软键盘

    在Android应用中,当输入框获取焦点弹出软键盘后,我们希望能够点击其他区域使软键盘消失,以提升用户体验。本篇文章将说明如何实现这一功能。 一、获取焦点并显示软键盘 在Andro…

    编程 2025-04-28
  • 堆叠图配色技巧分享

    堆叠图是数据可视化中常用的一种表现形式,而配色则是影响堆叠图观感和传达信息的重要因素之一。本文将分享一些堆叠图配色的技巧,帮助你创造更好的数据可视化。 一、色彩搭配原则 色彩是我们…

    编程 2025-04-27
  • 使用uring_cmd提高开发效率的技巧

    对于编程开发工程师来说,提高效率一直是致力追求的目标。本文将深度解析如何使用uring_cmd,提升工作效率。 一、常用命令 uring_cmd是一个非常强大的命令行工具,但是大部…

    编程 2025-04-27

发表回复

登录后才能评论