Avtivitys, Threads & Memory Leaks

Ref: http://www.androiddesignpatterns.com/2013/04/activitys-threads-memory-leaks.html


       安卓开发中一个难点就是activity生命周期里的长时间运行任务可能造成的内存泄漏。看下面一段代码:

/**
 * Example illustrating how threads persist across configuration
 * changes (which cause the underlying Activity instance to be
 * destroyed). The Activity context also leaks because the thread
 * is instantiated as an anonymous class, which holds an implicit
 * reference to the outer Activity instance, therefore preventing
 * it from being garbage collected.
 */
public class MainActivity extends Activity {

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    exampleOne();
  }

  private void exampleOne() {
    new Thread() {
      @Override
      public void run() {
        while (true) {
          SystemClock.sleep(1000);
        }
      }
    }.start();
  }
}
        当配置发生变化时,造成整个ACTIVITY被摧毁重造,很容易以为安卓会进行清理然后重新声明新的内存给ACTIVITY和它的线程。但是不是这样的。两者都会造成泄漏,这回导致严重的性能问题。


怎么泄漏一个ACTIVITY

         第一个内存泄漏应该很明显,读过以前写的内部类匿名类泄漏的文章就知道,非静态的内部/匿名类会隐含引用外部ACTIVITY,这样这个ACTIVITY就不会被GC。而ACTIVITY对象又引用了整个VIEW结构和所有它的资源,所有一旦泄漏就会造成大量内存泄漏。

         

https://i-blog.csdnimg.cn/blog_migrate/55d78601f1b645454c8102a51cc66ea0.png

10次 orientation 变化后内存状况;


解决方案,用静态内部类线程:

/**
 * This example avoids leaking an Activity context by declaring the 
 * thread as a private static inner class, but the threads still 
 * continue to run even across configuration changes. The DVM has a
 * reference to all running threads and whether or not these threads
 * are garbage collected has nothing to do with the Activity lifecycle.
 * Active threads will continue to run until the kernel destroys your 
 * application's process.
 */
public class MainActivity extends Activity {

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    exampleTwo();
  }

  private void exampleTwo() {
    new MyThread().start();
  }

  private static class MyThread extends Thread {
    @Override
    public void run() {
      while (true) {
        SystemClock.sleep(1000);
      }
    }
  }
}
这样新线程不会持有外部ACTIVITY的引用了,activity就可以顺利被GC。


怎么泄漏一个线程

下边是线程泄漏的问题。所有激活状态的java线程都在Dalvik虚拟机里有强引用,所以不会被GC。因此使用后台线程时一定要注意实施销毁测策略。下边给出一种解决方案:

/**
 * Same as example two, except for this time we have implemented a
 * cancellation policy for our thread, ensuring that it is never 
 * leaked! onDestroy() is usually a good place to close your active 
 * threads before exiting the Activity.
 */
public class MainActivity extends Activity {
  private MyThread mThread;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    exampleThree();
  }

  private void exampleThree() {
    mThread = new MyThread();
    mThread.start();
  }

  /**
   * Static inner classes don't hold implicit references to their
   * enclosing class, so the Activity instance won't be leaked across
   * configuration changes.
   */
  private static class MyThread extends Thread {
    private boolean mRunning = false;

    @Override
    public void run() {
      mRunning = true;
      while (mRunning) {
        SystemClock.sleep(1000);
      }
    }

    public void close() {
      mRunning = false;
    }
  }

  @Override
  protected void onDestroy() {
    super.onDestroy();
    mThread.close();
  }
}
上边这段代码在onDestroy()方法里保证永远不会异化的发生线程泄漏。

总结给出的建议:

  • 静态内部类好过非静态
  • JAVA永远不会GC你的运行中的线程。给你的后台线程设计关闭策略。
  • 考虑是否用Thread。查询DB可以用LOADER,小任务用ASYNCTASK。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值