Android 优化总结( 二 )


2.2 错误使用静态变量
使用静态方法是十分方便的 。但是创建的对象 , 建议不要全局化 , 全局化的变量必须加上static 。全局化后的变量或者对象会导致内存泄漏!原因分析这里内部类AClass隐式的持有外部类Activity的引用 , 而在Activity的onCreate方法中调用了 。这样AClass就会在Activity创建的时候是有了他的引用 , 而AClass是静态类型的不会被垃圾回收 , Activity在执行onDestory方法的时候由于被AClass持有了引用而无法被回收 , 所以这样Activity就总是被AClass持有而无法回收造成内存泄露 。
2.3 handler内存泄漏
造成内存泄漏原因分析通过内部类的方式创建mHandler对象,此时mHandler会隐式地持有一个外部类对象引用这里就是MainActivity , 当执行postDelayed方法时 , 该方法会将你的Handler装入一个Message , 并把这条Message推到MessageQueue中 , MessageQueue是在一个Looper线程中不断轮询处理消息 , 那么当这个Activity退出时消息队列中还有未处理的消息或者正在处理消息 , 而消息队列中的Message持有mHandler实例的引用 , mHandler又持有Activity的引用 , 所以导致该Activity的内存资源无法及时回收 , 引发内存泄漏 。解决Handler内存泄露主要2点有延时消息 , 要在Activity销毁的时候移除Messages监听匿名内部类导致的泄露改为匿名静态内部类 , 并且对上下文或者Activity使用弱引用 。
2.4 线程造成内存泄漏
早时期的时候处理耗时操作多数都是采用Thread+Handler的方式 , 后来逐步被AsyncTask取代 , 直到现在采用RxJAVA的方式来处理异步 。造成内存泄漏原因分析在处理一个比较耗时的操作时 , 可能还没处理结束MainActivity就执行了退出操作 , 但是此时AsyncTask依然持有对MainActivity的引用就会导致MainActivity无法释放回收引发内存泄漏 。解决办法在使用AsyncTask时 , 在Activity销毁时候也应该取消相应的任务AsyncTask.cancel()方法 , 避免任务在后台执行浪费资源 , 进而避免内存泄漏的发生 。
2.5 非静态内部类
非静态内部类创建静态实例造成的内存泄漏 。有的时候我们可能会在启动频繁的Activity中 , 为了避免重复创建相同的数据资源 , 可能会出现这种写法 。问题代码
private static TestResource mResource = null;@Overrideprotected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); if(mResource == null){ mResource = new TestResource(); }}class TestResource { //里面代码引用上下文 , Activity.this会导致内存泄漏}解决办法将该内部类设为静态内部类或将该内部类抽取出来封装成一个单例 , 如果需要使用Context , 请按照上面推荐的使用Application 的 Context 。分析问题这样就在Activity内部创建了一个非静态内部类的单例 , 每次启动Activity时都会使用该单例的数据 , 这样虽然避免了资源的重复创建 , 不过这种写法却会造成内存泄漏 , 因为非静态内部类默认会持有外部类的引用 , 而该非静态内部类又创建了一个静态的实例 , 该实例的生命周期和应用的一样长 , 这就导致了该静态实例一直会持有该Activity的引用 , 导致Activity的内存资源不能正常回收 。
2.6 未移除监听
问题代码
//add监听 , 放到集合里面 tv.getViewTreeObserver().addOnWindowFocusChangeListener(new ViewTreeObserver.OnWindowFocusChangeListener() { @Override public void onWindowFocusChanged(boolean b) { //监听view的加载 , view加载出来的时候 , 计算他的宽高等 。} });解决办法
//计算完后 , 一定要移除这个监听 tv.getViewTreeObserver().removeOnWindowFocusChangeListener(this); 注意事项:
tv.setOnClickListener();//监听执行完回收对象 , 不用考虑内存泄漏
tv.getViewTreeObserver().addOnWindowFocusChangeListene,add监听 , 放到集合里面 , 需要考虑内存泄漏
2.7 持有activity引用
2.8 资源未关闭
在使用IO、File流或者Sqlite、Cursor等资源时要及时关闭 。这些资源在进行读写操作时通常都使用了缓冲 , 如果及时不关闭 , 这些缓冲对象就会一直被占用而得不到释放 , 以致发生内存泄露 。因此我们在不需要使用它们的时候就及时关闭 , 以便缓冲能及时得到释放 , 从而避免内存泄露 。BroadcastReceiver , ContentObserver , FileObserver , Cursor , Callback等在 Activity onDestroy 或者某类生命周期结束之后一定要 unregister 或者 close 掉 , 否则这个 Activity 类会被 system 强引用 , 不会被内存回收 。值得注意的是 , 关闭的语句必须在finally中进行关闭 , 否则有可能因为异常未关闭资源 , 致使activity泄漏 。


推荐阅读