38

Android开发 如何最优的在 Activity 里释放资源

 4 years ago
source link: http://mp.weixin.qq.com/s?__biz=MzIxNzU1Nzk3OQ%3D%3D&%3Bmid=2247489757&%3Bidx=1&%3Bsn=c16fcc894cb2a8906f422e783c41fa2d
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

code小生 一个专注大前端领域的技术平台 公众号回复 Android 加入安卓技术群

作者:禄子_c79b

链接:https://www.jianshu.com/p/b1e7422053a5

声明:本文已获 禄子_c79b 授权发表,转发等请联系原作者授权

前言

当前你已经入门Android开发,开始关注深入的问题,你就会碰到一个Android开发阶段经常碰到的问题,那就是内存泄漏. 其实大多数Android的内存泄漏都是因为activity里的资源释放不正确导致,activity与单例或者接口互相持有无法释放.这篇博客就来讲解如何在Android里最优的释放资源.

错误释放资源的一些例子

在看正面例子之前,我们看看反面例子,了解为什么经常莫名其妙的内存泄露

  • 在Activity的onDestroy()的生命周期里释放资源

在下面的onDestroy()方法里我们有一个叫mHttpList的资源要释放,我们都知道activity的生命周期的最后是onDestroy方法,那么为什么在onDestroy()里释放资源会有问题呢?

问题出在onDestroy()生命周期并不是立即执行的.Activity退出前台后先是进入栈里的.是否执行onDestroy()是交给系统决定的,一般情况下系统的确会及时的运行onDestroy()方法销毁activity,但是在一些Activity跳转频繁的情况下可能系统并不会马上运行onDestroy()方法.这个时候问题就出现了你认为应该结束的资源并没有马上结束可能导致一些回调报错或者内存泄露.

@Override
protected void onDestroy() {
  super.onDestroy();
  if (mHttpList != null){
    mHttpList.release();
    mHttpList = null;
  }

}
  • 在Activity的finish()方法里释放资源

同上环境,那么finish方法里释放资源有那些问题呢?

重写的finish()是一个释放资源的好地方,在按返回键(或者你自己主动调用onBackPressed()方法)和主动调用finish()方法时,重写的finish()都是会运行的.但是在这个方法在有一种情况下是不运行的,就是在后台太久后的自动清理或者其他Activity的启动模式是android:launchMode="singleTask" 在其他activity的singleTask下会自动清理它栈前的所以Activity,在这种情况下如果你的activity要被清理掉finish()方法是不会运行的.这样你的资源就没有被释放了.

@Override
public void finish() {
  super.finish();
  if (mHttpList != null){
    mHttpList.release();
    mHttpList = null;
  }
}
  • 在Activity的onPause()和onStop()里释放资源

在onPause()和onStop()我们都知道activity后台的时候会调用这2个生命周期先onPause() 然后在 onStop(),如果是Dialog模式的Activity弹出只会进入onPause(). 他们的问题是什么呢?

问题是如果在操作太快的操作前后台,就会导致我们的资源需要频繁的在onRestart()或者onResume() 重新初始化或者注册.这是较好的一种释放资源的方式一般情况下是推荐这种的,但是快速频繁的操作初始化与释放是最容易出现内存泄漏的...特别是初始化如果是耗时的...

@Override
protected void onPause() {
  super.onPause();
  Log.e(TAG, "onPause: ");
}

@Override
protected void onStop() {
    super.onStop();
    Log.e(TAG, "onStop: ");
}

推荐在Activity里释放资源的方式

onPause()或者onStop()结合onDestroy(),调用isFinishing()在方法判断后释放资源,如下代码:

private boolean mIsRelease = false;

/**
 * 释放资源
 */
private void release(){
    if (mIsRelease){
        return;
    }
    if (isFinishing()) {
        if (mHttpList != null) {
            mHttpList.release();
            mHttpList = null;
        }
    }
    mIsRelease = true;
}

@Override
protected void onPause() {
    super.onPause();
    release();
}

@Override
protected void onStop() {
    super.onStop();
    //结合实际情况也可以在onStop方法里 释放资源

}

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

注意添加判空或者也可以在方法里套一个全局布尔值来判断是否释放过资源,防止重复释放资源... 这里为什么onDestroy()还要运行一次?下面会说明原因.

首先讲讲isFinishing()的作用就是判断这个activity是不是需要被销毁,还是只是进入后台.我验证过以下情况:

  1. 在主动调用finish()方法的情况下,isFinishing() 返回的是true

  2. 在主动调用onBackPressed()方法或者按返回键的情况下, isFinishing() 返回的是true

  3. 如果只是因为进入到其他Activity而退到后台, isFinishing() 返回的是false

但是,isFinishing()并不是完美的,还有一种情况可以跳过清理的,那就是SingleTask模式下这个Activity要被销毁,但是后台因为入栈(入深栈最少在第三层的那种情况)已经触发过onPause或者onStop,所以SingleTask的清理就跳过了isFinishing()判断,直接走到了onDestroy().

所以,我们需要在onDestroy()再次兜底,保证不会因为SingleTask的原因没有释放资源.

总结

以上这种释放资源的组合,目的是在Activity真的要被销毁的时候能尽快的释放资源,又可以防止Activity只是在后台时需要重复注册资源,最后依靠onDestroy()兜底保证资源不会因为SingleTask原因没有释放

在Fragment里的资源释放

它有也要一个类似的方法可以判断,是否是被移除Activity

final public boolean isRemoving() {
return mRemoving;
}

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK