写点什么

内存泄漏以优化大全,2021 非科班生的 Android 面试之路

用户头像
Android架构
关注
发布于: 12 小时前

}


}


}


}


}


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 s


《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》
浏览器打开:qq.cn.hn/FTe 免费领取
复制代码


avedInstanceState) {


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,以避免发生内存泄漏。


[](


)集合中的对象未清理造成内存泄漏




这个比较好理解,如果一个对象放入到 ArrayList、HashMap 等集合中,这个集合就会持有该对象的引用,当我们不再需要这个对象时,也并没有将它从集合中移除,这样只要集合还在使用(而此对象已经无用了),这个对象就造成了内存泄漏。并且如果集合被静态引用的话,集合里面那些没有用的对象更会造成内存泄漏了,所以在使用集合时要及时将不用的对象从集合 remove,或者 clear 集合,以避免内存泄漏。


[](


)资源未关闭或释放导致内存泄漏




在使用 IO、File 流或者 Sqlite、Cursor 等资源时要及时关闭。这些资源在进行写操作时通过都使用了缓冲,如果及时不关闭,这些缓冲对象就会一直被占用而得不到释放,以至于发生内存泄漏。因此我们在不需要使用它们的时候就及时关闭,以便缓冲能及时得到释放,从而避免内存泄漏。


[](


)属性动画造成内存泄漏




动画同样是一个耗时任务,比如在 Activity 中启动了属性动画(ObjectAnimator),但是在销毁的时候没有调用 cancel 方法,虽然我们看不到动画了,但是这个动画依然会不断地播放下去,动画引用所在的控件,所在的控件引用 Activity ,这就造成 Activity 无法正常释放。因此同样要在 Activity 销毁的时候 cancel 掉属性动画,避免发生内存泄漏。


@Override


protected void onDestroy() {


super.onDestroy();


mAnimator.cancel();


}


[](


)WebView 造成内存泄漏




关于 WebView 的内存泄漏,因为 WebView 在加载网页后会长期占用内存而不能被释放,因此我们在 Activity 销毁后要调用它的 destroy() 方法来销毁它以释放内存。 另外在查阅 WebView 内存泄漏相关资料时看到这种情况: WebView 下面的 Callback 持有 Activity 引用,造成 WebView 内存无法释放,即使是调用了 WebView.destroy() 等方法都无法解决问题(Android 5.1 之后)。 最终的解决方案是:在销毁 WebView 之前需要先将 WebView 从父容器中移除,然后在销毁 WebView。 详细分析过程可以参考这篇文章:[WebView 内存泄漏解决方法](


)。


@Override


protected void onDestroy() {


super.onDestroy();


if (mWebView != null) {


((ViewGroup) mWebView.getParent()).removeView(mWebView); //先从父控件中移除 WebView.


mWebView.stopLoading();


mWebView.getSettings().setJavaScriptEnabled(false);


mWebView.clearHistory();


mWebView.removeAllViews();


mWebView.destroy();


mWebView = null;


}


}


[](


)总结




用户头像

Android架构

关注

还未添加个人签名 2021.10.31 加入

还未添加个人简介

评论

发布
暂无评论
内存泄漏以优化大全,2021非科班生的Android面试之路