内存泄漏以优化大全,年薪50W

  • Post author:
  • Post category:其他


}



private void start() {

    Message msg = Message.obtain();

    msg.what = 1;

    mHandler.sendMessage(msg);

}



private Handler mHandler = new Handler(Looper.myLooper()) {

    @Override

    public void handleMessage(@NonNull Message msg) {

        if (msg.what == 1) {

            //something

        }

    }

};

}




也许有人会说,mHandler 并未作为静态变量持有 Activity 引用,生命周期可能不会比 Activity 长,应该不一定会导致内存泄漏呢,显然不是这样的! 熟悉 `Handler` 消息机制的都知道,`mHandler` 会作为成员变量保存在发送的消息 `msg` 中,即 `msg` 持有 `mHandler` 的引用,而 `mHandle`r 是 `Activity` 的非静态内部类实例,即 `mHandler` 持有 `Activity` 的引用,那么我们就可以理解为 `msg` 间接持有 `Activity` 的引用。`msg` 被发送后先放到消息队列 `MessageQueue` 中,然后等待 `Looper` 的轮询处理( `MessageQueue` 和 `Looper` 都是与线程相关联的,`MessageQueue` 是 `Looper` 引用的成员变量,而 `Looper` 是保存在 `ThreadLocal` 中的)。那么当 `Activity` 退出后,`msg` 可能仍然存在于消息队列 `MessageQueue` 中未处理或者正在处理,那么这样就会导致 `Activity` 无法被回收,以至发生 `Activity` 的内存泄漏。 通常在 Android 开发中如果使用内部类,但又要避免内存泄漏,一般都会采用**静态内部类 + 弱引用**的方式:



public class MainActivity extends AppCompatActivity {

private Handler mHandler;



@Override

protected void onCreate(Bundle savedInstanceState) {

    super.onCreate(savedInstanceState);

    setContentView(R.layout.activity_main);

    mHandler = new MyHandler(this);

    start();

}



private void start() {

    Message msg = Message.obtain();

    msg.what = 1;

    mHandler.sendMessage(msg);

}



private static class MyHandler extends Handler {



    private WeakReference<MainActivity> activityWeakReference;



    public MyHandler(MainActivity activity) {

        activityWeakReference = new WeakReference<>(activity);

    }



    @Override

    public void handleMessage(@NonNull Message msg) {

        MainActivity activity = activityWeakReference.get();

        if (activity != null) {

            if (msg.what == 1) {

                //something

            }

        }



    }

}

}




mHandler 通过弱引用的方式持有 Activity,当 GC 执行垃圾回收时,遇到 Activity 就会被回收并释放所有占据的内存单元。这样就不会发生内存泄漏了。 上面的做法确实避免了 Activity 导致的内存泄漏,发送的 msg 不再已经没有持有 Activity 的引用了,但是 msg 还是有可能存在消息队列 MessageQueue 中,所有更好的是在 Activity 销毁时就将 mHandler 的回调和发送的消息给移除掉。



@Override

protected void onDestroy() {

    super.onDestroy();

    mHandler.removeCallbacksAndMessages(null);

}



### []( )Thread、AsyncTask



非静态内部类造成内存泄漏还有一种情况就是使用 Thread 或者 AsyncTask。 比如在 Activity 中直接 new 一个子线程 Thread:



public class ThreadActivity extends AppCompatActivity {

@Override

protected void onCreate(@Nullable Bundle savedInstanceState) {

    super.onCreate(savedInstanceState);

    setContentView(R.layout.activity_thread);



    new Thread(new Runnable() {

        @Override

        public void run() {

            //Something

            try {

                Thread.sleep(2000);

            } catch (InterruptedException e) {

                e.printStackTrace();

            }

        }

    }).start();

}

}




或者直接新建 AsyncTask 异步任务:



public class AsyncTaskActivity extends AppCompatActivity {

@Override

protected void onCreate(@Nullable Bundle savedInstanceState) {

    super.onCreate(savedInstanceState);

    setContentView(R.layout.activity_async);



    new AsyncTask<Void, Void, Void>() {



        @Override

        protected Void doInBackground(Void... voids) {

            //something

            try {

                Thread.sleep(2000);

            } catch (InterruptedException e) {

                e.printStackTrace();

            }

            return null;

        }

    }.execute();

}

}




很多初学者都会像上面这样新建线程和异步任务,殊不知这样的写法非常不友好,这样方式新建的子线程 Thread 和 AsyncTask 都是匿名内部类对象,默认就隐式的持有外部 Activity 的引用,导致 Activity 内存泄漏。要避免内存泄漏的话还是需要像上面 Handler 一样使用**静态内部类 + 弱引用**的方式。



[]( )未取消注册或回调导致内存泄漏

----------------------------------------------------------------------------



比如我们在 Activity 中注册广播,如果在 Activity 销毁后不取消注册,那么这个广播会一直存在系统中,同上面所说的非静态内部类一样持有 Activity 引用,导致内存泄漏。因此注册广播后在 Activity 销毁后一定要取消注册。



public class BroadcastActivity extends AppCompatActivity {

@Override

protected void onCreate(@Nullable Bundle savedInstanceState) {

    super.onCreate(savedInstanceState);

    setContentView(R.layout.activity_broadcast);

    registerReceiver(mReceiver, new IntentFilter());

}



private BroadcastReceiver mReceiver = new BroadcastReceiver() {

    @Override

    public void onReceive(Context context, Intent intent) {

        //接收到广播需要做的逻辑

    }

};



@Override

protected void onDestroy() {

    super.onDestroy();

    unregisterReceiver(mReceiver);

}

}




在注册观察者模式的时候,如果不及时取消也会造成内存泄漏。比如使用 Retrofit + RxJava 注册网络请求的观察者回调,同样作为匿名内部类持有外部引用,所以需要记得在不用或者销毁的时候取消注册。



[]( )Timer 和 TimerTask 导致内存泄漏

--------------------------------------------------------------------------------------



Timer 和 TimerTask 在 Android 中通常会被用来做一些计时或循环任务,比如实现无限轮播的 ViewPager:



public class TimerActivity extends AppCompatActivity {

private ViewPager mViewPager;

private PagerAdapter mAdapter;

private Timer mTimer;

private TimerTask mTimerTask;



@Override

protected void onCreate(@Nullable Bundle savedInstanceState) {

    super.onCreate(savedInstanceState);

    setContentView(R.layout.activity_timer);

    init();

    mTimer.schedule(mTimerTask, 3000, 3000);

}



private void init() {

    mViewPager = findViewById(R.id.view_pager);

    mAdapter = new ViewPagerAdapter();

    mViewPager.setAdapter(mAdapter);



    mTimer = new Timer();

    mTimerTask = new TimerTask() {

        @Override

        public void run() {

            runOnUiThread(new Runnable() {

                @Override

                public void run() {

                    loopViewpager();

                }

            });

        }

    };

}



private void loopViewpager() {

    if (mAdapter.getCount() > 0) {

        int curPos = mViewPager.getCurrentItem();

        curPos = (++curPos) % mAdapter.getCount();

        mViewPager.setCurrentItem(curPos);

    }

}



private void stopLoopViewPager() {

    if (mTimer != null) {

        mTimer.cancel();

        mTimer.purge();

        mTimer = null;

    }

    if (mTimerTask != null) {

        mTimerTask.cancel();

        mTimerTask = null;

    }

}



@Override

protected void onDestroy() {

    super.onDestroy();

    stopLoopViewPager();

}

}




当我们 Activity 销毁时,有可能 Timer 还在继续等待执行 TimerTask,它持有 Activity 的引用不能被回收,因此当我们 Activity 销毁的时候要立即 cancel 掉 Timer 和 TimerTask,以避免发生内存泄漏。


## 总结

最后小编想说:不论以后选择什么方向发展,目前重要的是把Android方面的技术学好,毕竟其实对于程序员来说,要学习的知识内容、技术有太多太多,要想不被环境淘汰就只有不断提升自己,**从来都是我们去适应环境,而不是环境来适应我们!**

> 这里附上我整理的几十套腾讯、字节跳动,京东,小米,头条、阿里、美团等公司19年的Android面试题。把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节。

由于篇幅有限,这里以图片的形式给大家展示一小部分。

![](https://img-blog.csdnimg.cn/img_convert/889ca467172eaf566f5a8cc26ba894e4.png)



**[CodeChina开源项目:《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》](https://codechina.csdn.net/m0_60958482/android_p7)**

> 网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。

择什么方向发展,目前重要的是把Android方面的技术学好,毕竟其实对于程序员来说,要学习的知识内容、技术有太多太多,要想不被环境淘汰就只有不断提升自己,**从来都是我们去适应环境,而不是环境来适应我们!**

> 这里附上我整理的几十套腾讯、字节跳动,京东,小米,头条、阿里、美团等公司19年的Android面试题。把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节。

由于篇幅有限,这里以图片的形式给大家展示一小部分。

[外链图片转存中...(img-cISdeZYQ-1631090798006)]



**[CodeChina开源项目:《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》](https://codechina.csdn.net/m0_60958482/android_p7)**

> 网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。

技术进阶之路很漫长,一起共勉吧~



版权声明:本文为m0_61408464原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。