華東 發表於 2019-11-29 17:07:00

Android开发 在Activity里释放资源的一种思维

<h1><span style="background-color: rgba(255, 255, 255, 1); color: rgba(0, 128, 128, 1)">前言</span></h1>
<p><span style="background-color: rgba(255, 255, 255, 1); color: rgba(0, 0, 0, 1)">  当前你已经入门Android开发,开始关注深入的问题,你就会碰到一个Android开发阶段经常碰到的问题,那就是<strong>内存泄漏</strong>. 其实大多数Android的内存泄漏都是因为activity里的资源释放不正确导致,activity与单例或者接口互相持有无法释放.这篇博客就来讲解如何在Android里最优的释放资源.</span></p>
<p><span style="background-color: rgba(255, 255, 255, 1); color: rgba(0, 0, 0, 1)">  <span style="color: rgba(255, 0, 0, 1)"><strong>请注意,此篇博客只是一个思维参考,请不要将全部activity的资源释放都套用这个方式,其实释放资源与初始化资源最好的方式是深刻理解Activity的生命周期,来决定何时初始化资源何时释放资源!</strong></span></span></p>
<h1><span style="background-color: rgba(255, 255, 255, 1); color: rgba(0, 128, 128, 1)">错误释放资源的一些例子</span></h1>
<p><span style="background-color: rgba(255, 255, 255, 1); color: rgba(0, 0, 0, 1)">  在看正面例子之前,我们看看反面例子,了解为什么经常莫名其妙的内存泄露</span></p>
<h2><span style="background-color: rgba(255, 255, 255, 1); color: rgba(0, 0, 0, 1)">在Activity的onDestroy()的生命周期里释放资源</span></h2>
<p><span style="background-color: rgba(255, 255, 255, 1); color: rgba(0, 0, 0, 1)">在下面的<span style="color: rgba(0, 0, 0, 1)">onDestroy</span>()方法里我们有一个叫<span style="color: rgba(0, 0, 0, 1)">mHttpList</span>的资源要释放,我们都知道activity的生命周期的最后是<span style="color: rgba(0, 0, 0, 1)"><span style="color: rgba(0, 0, 0, 1)"><span style="color: rgba(0, 0, 0, 1)">onDestroy</span></span></span>方法,那么为什么在<span style="color: rgba(0, 0, 0, 1)"><span style="color: rgba(0, 0, 0, 1)">onDestroy()</span></span>里释放资源会有问题呢?</span></p>
<p><strong><span style="background-color: rgba(255, 255, 255, 1); color: rgba(0, 0, 0, 1)">  </span></strong><span style="background-color: rgba(255, 255, 255, 1); color: rgba(0, 0, 0, 1)">问题出在</span><span style="background-color: rgba(255, 255, 255, 1); color: rgba(0, 0, 0, 1)"><span style="color: rgba(0, 0, 0, 1)">onDestroy()</span>生命周期<strong>并不是立即执行的</strong>.Activity退出前台后先是进入栈里的.是否执行<span style="color: rgba(0, 0, 0, 1)">onDestroy()</span>是交给系统决定的,一般情况下系统的确会及时的运行<span style="color: rgba(0, 0, 0, 1)">onDestroy()</span>方法销毁activity,但是在一些Activity跳转频繁的情况下可能系统并不会马上运行<span style="color: rgba(0, 0, 0, 1)">onDestroy()</span></span><span style="background-color: rgba(255, 255, 255, 1); color: rgba(0, 0, 0, 1)">方法.这个时候问题就出现了你认为应该结束的资源并没有马上结束可能导致一些回调报错或者内存泄露.</span></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">  @Override
    </span><span style="color: rgba(0, 0, 255, 1)">protected</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> onDestroy() {
      </span><span style="color: rgba(0, 0, 255, 1)">super</span><span style="color: rgba(0, 0, 0, 1)">.onDestroy();
      </span><span style="color: rgba(0, 0, 255, 1)">if</span> (mHttpList != <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">){
            mHttpList.release();
            mHttpList </span>= <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">;
      }
      
    }</span></pre>
</div>
<h2><span style="background-color: rgba(255, 255, 255, 1); color: rgba(0, 0, 0, 1)">在Activity的finish()方法里释放资源</span></h2>
<p><span style="background-color: rgba(255, 255, 255, 1); color: rgba(0, 0, 0, 1)">同上环境,那么finish方法里释放资源有那些问题呢?</span></p>
<p><span style="background-color: rgba(255, 255, 255, 1); color: rgba(0, 0, 0, 1)">  重写的<span style="color: rgba(0, 0, 0, 1)">finish()</span>是一个释放资源的好地方,在按返回键(或者你自己<span style="color: rgba(0, 0, 0, 1)">主动</span>调用onBackPressed()方法)和主动调用finish()方法时,<span style="color: rgba(0, 0, 0, 1)">重写的<span style="color: rgba(0, 0, 0, 1)">finish()都是会运行的.但是在这个方法在有一种情况下是不运行的,就是在后台太久后的自动清理或者其他Activity的启动模式是<span style="color: rgba(102, 14, 122, 1); font-weight: bold">android<span style="color: rgba(0, 0, 255, 1); font-weight: bold">:launchMode<span style="color: rgba(0, 128, 0, 1); font-weight: bold">="singleTask"</span></span></span>&nbsp; 在其他activity的singleTask下会自动清理它栈前的所以Activity,<strong>在这种情况下如果你的activity要被清理掉finish()方法是不会运行的.这样你的资源就没有被释放了.</strong></span></span></span></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">    @Override
    </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> finish() {
      </span><span style="color: rgba(0, 0, 255, 1)">super</span><span style="color: rgba(0, 0, 0, 1)">.finish();
      </span><span style="color: rgba(0, 0, 255, 1)">if</span> (mHttpList != <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">){
            mHttpList.release();
            mHttpList </span>= <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">;
      }
    }</span></pre>
</div>
<h2><span style="background-color: rgba(255, 255, 255, 1); color: rgba(0, 0, 0, 1)">在Activity的onPause()和onStop()里释放资源</span></h2>
<p><span style="background-color: rgba(255, 255, 255, 1); color: rgba(0, 0, 0, 1)">在<span style="color: rgba(0, 0, 0, 1)">onPause()和onStop()我们都知道activity后台的时候会调用这2个生命周期先<span style="color: rgba(0, 0, 0, 1)"><span style="color: rgba(0, 0, 0, 1)">onPause()</span></span> 然后在 <span style="color: rgba(0, 0, 0, 1)"><span style="color: rgba(0, 0, 0, 1)">onStop(),如果是Dialog模式的Activity弹出只会进入<span style="color: rgba(0, 0, 0, 1)"><span style="color: rgba(0, 0, 0, 1)">onPause(). 他们的问题是什么呢?</span></span></span></span></span></span></p>
<p><span style="background-color: rgba(255, 255, 255, 1); color: rgba(0, 0, 0, 1)"><span style="color: rgba(0, 0, 0, 1)"><span style="color: rgba(0, 0, 0, 1)"><span style="color: rgba(0, 0, 0, 1)"><span style="color: rgba(0, 0, 0, 1)"><span style="color: rgba(0, 0, 0, 1)">  问题是如果在操作太快的操作前后台,就会导致我们的资源需要频繁的在onRestart()或者onResume() 重新初始化或者注册.这是较好的一种释放资源的方式一般情况下是推荐这种的,<span style="color: rgba(0, 0, 0, 1)"><span style="color: rgba(0, 0, 0, 1)"><span style="color: rgba(0, 0, 0, 1)"><span style="color: rgba(0, 0, 0, 1)"><span style="color: rgba(0, 0, 0, 1)"><span style="color: rgba(0, 0, 0, 1)">但是快速频繁的操作初始化与释放是最容易出现内存泄漏的...特别是初始化如果是耗时的...</span></span></span></span></span></span></span></span></span></span></span></span></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">@Override
    </span><span style="color: rgba(0, 0, 255, 1)">protected</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> onPause() {
      </span><span style="color: rgba(0, 0, 255, 1)">super</span><span style="color: rgba(0, 0, 0, 1)">.onPause();
      Log.e(TAG, </span>"onPause: "<span style="color: rgba(0, 0, 0, 1)">);</span><span style="color: rgba(0, 0, 0, 1)">
    }

    @Override
    </span><span style="color: rgba(0, 0, 255, 1)">protected</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> onStop() {
      </span><span style="color: rgba(0, 0, 255, 1)">super</span><span style="color: rgba(0, 0, 0, 1)">.onStop();
      Log.e(TAG, </span>"onStop: "<span style="color: rgba(0, 0, 0, 1)">);</span><span style="color: rgba(0, 0, 0, 1)">
    }</span></pre>
</div>
<h1><span style="background-color: rgba(255, 255, 255, 1); color: rgba(0, 128, 128, 1)">Activity里释放资源的方式一</span></h1>
<p><span style="color: rgba(0, 0, 0, 1)"><strong><span style="background-color: rgba(255, 255, 255, 1)">&nbsp;  onPause()或者onStop()<span style="color: rgba(0, 0, 0, 1)"><strong><span>结合<span style="color: rgba(0, 0, 0, 1)">onDestroy(),</span></span></strong></span>调用isFinishing()在方法判断后释放资源,如下代码:    </span></strong></span></p>
<div class="cnblogs_code">
<pre>    <span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">boolean</span> mIsRelease = <span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">;

    </span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)">
   * 释放资源
   </span><span style="color: rgba(0, 128, 0, 1)">*/</span>
    <span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> release(){
      </span><span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> (mIsRelease){
            </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)">;
      }
      </span><span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> (isFinishing()) {
            </span><span style="color: rgba(0, 0, 255, 1)">if</span> (mHttpList != <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">) {
                mHttpList.release();
                mHttpList </span>= <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">;
            }
      }
      mIsRelease </span>= <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">;
    }

    @Override
    </span><span style="color: rgba(0, 0, 255, 1)">protected</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> onPause() {
      </span><span style="color: rgba(0, 0, 255, 1)">super</span><span style="color: rgba(0, 0, 0, 1)">.onPause();
      <span style="color: rgba(0, 128, 0, 1)">//<span style="color: rgba(0, 128, 0, 1)">结合实际情况也可以在onPause方法里 释放资源,但是这里释放资源有一个问题你需要知道并且避免:<br>     //在onPause生命周期执行的瞬间,activity其实是还在前台的,所以有概率出现资源已经被释放,但是activity里的View是还有被点击的机会导致空指针报错(特别是在跑monkey的时候,容易出现这种报错)</span></span>
    }

    @Override
    </span><span style="color: rgba(0, 0, 255, 1)">protected</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> onStop() {
      </span><span style="color: rgba(0, 0, 255, 1)">super</span><span style="color: rgba(0, 0, 0, 1)">.onStop();</span><span style="color: rgba(0, 128, 0, 1)"><br>     <span style="color: rgba(0, 0, 0, 1)">release();</span><br></span>
<span style="color: rgba(0, 0, 0, 1)">
    }

    @Override
    </span><span style="color: rgba(0, 0, 255, 1)">protected</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> onDestroy() {
      </span><span style="color: rgba(0, 0, 255, 1)">super</span><span style="color: rgba(0, 0, 0, 1)">.onDestroy();
      release();
    }</span></pre>
</div>
<p>  注意添加判空或者也可以在方法里套一个全局布尔值来判断是否释放过资源,防止重复释放资源...&nbsp; 这里为什么<span style="color: rgba(0, 0, 0, 1)">onDestroy()</span>还要运行一次?下面会说明原因.</p>
<p>  首先讲讲<span style="color: rgba(0, 0, 0, 1)"><strong><span>isFinishing()的作用就是判断这个activity是不是需要被销毁,还是只是进入后台.我验证过以下情况:</span></strong></span></p>
<p><span style="color: rgba(0, 0, 0, 1)">1.在主动调用finish()方法的情况下,<span style="color: rgba(0, 0, 0, 1)">isFinishing()</span> 返回的是true</span></p>
<p><span style="color: rgba(0, 0, 0, 1)">2.在主动调用onBackPressed()方法或者按返回键的情况下, <span style="color: rgba(0, 0, 0, 1)"><span style="color: rgba(0, 0, 0, 1)">isFinishing() 返回的是true</span></span></span></p>
<p><span style="color: rgba(0, 0, 0, 1)">3.如果只是因为进入到其他Activity而退到后台, <span style="color: rgba(0, 0, 0, 1)"><span style="color: rgba(0, 0, 0, 1)"><span style="color: rgba(0, 0, 0, 1)">isFinishing() 返回的是false</span></span></span></span></p>
<p><span style="color: rgba(255, 0, 0, 1)"><strong><strong><strong>注意点:</strong></strong></strong></span></p>
<p><span style="color: rgba(0, 0, 0, 1)"><strong><span><span style="color: rgba(0, 0, 0, 1)"><strong><span style="color: rgba(0, 0, 0, 1)"><strong><span style="color: rgba(0, 0, 0, 1)">1.完全依靠<span style="color: rgba(0, 0, 0, 1)"><strong>isFinishing()来决定释放资源并</strong></span>不完美,还有一种情况可以跳过清理的,那就是SingleTask模式下这个Activity要被销毁,但是后台因为入栈(入深栈最少在第三层的那种情况)已经触发过<span style="color: rgba(0, 0, 0, 1)">onPause</span>或者<span style="color: rgba(0, 0, 0, 1)">onStop,所以<span style="color: rgba(0, 0, 0, 1)"><strong><span style="color: rgba(0, 0, 0, 1)"><strong><span style="color: rgba(0, 0, 0, 1)"><strong><span style="color: rgba(0, 0, 0, 1)">SingleTask</span></strong></span></strong></span></strong></span>的清理就跳过了<span style="color: rgba(0, 0, 0, 1)"><strong><span style="color: rgba(0, 0, 0, 1)"><strong><span style="color: rgba(0, 0, 0, 1)"><strong><span style="color: rgba(0, 0, 0, 1)"><span style="color: rgba(0, 0, 0, 1)"><strong>isFinishing()</strong></span></span></strong></span></strong></span></strong></span>判断,直接走到了<span style="color: rgba(0, 0, 0, 1)">onDestroy</span>().所以,我们需要在<span style="color: rgba(0, 0, 0, 1)">onDestroy()</span>再次兜底,保证不会因为SingleTask的原因没有释放资源.</span></span></strong></span></strong></span></span></strong></span></p>
<p><span style="color: rgba(0, 0, 0, 1)"><strong><span><span style="color: rgba(0, 0, 0, 1)"><strong><span style="color: rgba(0, 0, 0, 1)"><strong><span style="color: rgba(0, 0, 0, 1)"><span style="color: rgba(0, 0, 0, 1)">2.在<span style="color: rgba(0, 0, 0, 1)">onPause里释放资源需要慎重,在onPause生命周期执行的瞬间,activity其实是还在前台的,所以有概率出现资源已经被释放,但是activity里的View是还有被点击的机会导致空指针报错(特别是在跑monkey的时候,容易出现这种报错)。</span></span></span></strong></span></strong></span></span></strong></span></p>
<p>最后总结,以上这种释放资源的组合,目的是在Activity真的要被销毁的时候能尽快的释放资源,又可以防止Activity只是在后台时需要重复注册资源,最后依靠<span style="color: rgba(0, 0, 0, 1)"><span style="color: rgba(0, 0, 0, 1)"><span style="color: rgba(0, 0, 0, 1)"><span style="color: rgba(0, 0, 0, 1)"><span style="color: rgba(0, 0, 0, 1)"><span style="color: rgba(0, 0, 0, 1)">onDestroy()</span></span></span></span></span></span>兜底保证资源不会因为<span style="color: rgba(0, 0, 0, 1)"><span style="color: rgba(0, 0, 0, 1)"><span style="color: rgba(0, 0, 0, 1)"><span style="color: rgba(0, 0, 0, 1)"><span style="color: rgba(0, 0, 0, 1)">SingleTask</span></span></span></span></span>原因没有释放</p>
<h1>&nbsp;</h1>
<p>&nbsp;</p>
<p>End</p>

</div>
<div id="MySignature" role="contentinfo">
    <div style="text-align: center">
    <p style="color:orange;font-size:16px;" >本文来自博客园,作者:观心静 ,转载请注明原文链接:https://www.cnblogs.com/guanxinjing/p/11959000.html </p>
    <div style="color:orange;font-size:16px;">本文版权归作者和博客园共有,欢迎转载,但必须给出原文链接,并保留此段声明,否则保留追究法律责任的权利。 </div>
</div><br><br>
来源:https://www.cnblogs.com/guanxinjing/p/11959000.html
頁: [1]
查看完整版本: Android开发 在Activity里释放资源的一种思维