栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

内存泄漏以优化大全,kotlin语法教程

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

内存泄漏以优化大全,kotlin语法教程

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() {

@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 内存优化是一个比较重要的一个方面,很多时候程序中发生了内存泄漏我们不一定就能注意到,所有在编码的过程中养成良好的习惯。总结下来只要做到以下这几点就能避免大多数情况的内存泄漏:

构造单例的时候尽量别用 Activity 的引用

静态引用是注意引用对象的置空或者少用静态引用

使用静态内部类 + 弱引用代替非静态内部类

及时取消广播或者观察者注册

ngs().setJavascriptEnabled(false);

mWebView.clearHistory();

mWebView.removeAllViews();

mWebView.destroy();

mWebView = null;

}

}

总结


内存泄漏在 Android 内存优化是一个比较重要的一个方面,很多时候程序中发生了内存泄漏我们不一定就能注意到,所有在编码的过程中养成良好的习惯。总结下来只要做到以下这几点就能避免大多数情况的内存泄漏:

构造单例的时候尽量别用 Activity 的引用

静态引用是注意引用对象的置空或者少用静态引用

使用静态内部类 + 弱引用代替非静态内部类

及时取消广播或者观察者注册

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/720566.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号