十大免费网站推广,为什么做的网站预览出来什么都没有,网页制作培训班哪个好,wordpress搬家至本地及域名替换目录介绍#xff1a; 1.什么是内存泄漏2.内存泄漏造成什么影响3.内存泄漏检测的工具有哪些4.关于Leakcanary使用介绍5.Leakcanary捕捉常见的内存泄漏及解决办法 5.0.1 错误使用单例造成的内存泄漏5.0.2 错误使用静态变量#xff0c;导致引用后无法销毁5.0.3 [常见]Handler使用…目录介绍 1.什么是内存泄漏2.内存泄漏造成什么影响3.内存泄漏检测的工具有哪些4.关于Leakcanary使用介绍5.Leakcanary捕捉常见的内存泄漏及解决办法 5.0.1 错误使用单例造成的内存泄漏5.0.2 错误使用静态变量导致引用后无法销毁5.0.3 [常见]Handler使用不当造成的内存泄漏5.0.4 线程造成的内存泄漏[比较少见]5.0.5 非静态内部类创建静态实例造成的内存泄漏5.0.6 不需要用的监听未移除会发生内存泄露5.0.7 [常见]资源未关闭造成的内存泄漏5.0.8 未注销EventBus导致的内存泄漏5.0.9 [常见]持有activity引用未被释放导致内存泄漏5.1.0 静态集合使用不当导致的内存泄漏5.1.1 动画资源未释放导致内存泄漏5.1.2 系统bug之InputMethodManager导致内存泄漏6.其他建议 6.0.1 尽量避免使用 static 成员变量7.版本更新 v1.0.0 更新于2016年3月19日v1.1.0 更新于2017年7月8日v1.2.0 更新于2018年5月3日v1.3.0 更新于2018年9月18日1.什么是内存泄漏 一些对象有着有限的声明周期当这些对象所要做的事情完成了我们希望它们会被垃圾回收器回收掉。但是如果有一系列对这个对象的引用存在那么在我们期待这个对象生命周期结束时被垃圾回收器回收的时候它是不会被回收的。它还会占用内存这就造成了内存泄露。持续累加内存很快被耗尽。比如当Activity的onDestroy()方法被调用后Activity以及它涉及到的View和相关的Bitmap都应该被回收掉。但是如果有一个后台线程持有这个Activity的引用那么该Activity所占用的内存就不能被回收这最终将会导致内存耗尽引发OOM而让应用crash掉。2.内存泄漏会造成什么影响 它是造成应用程序OOM的主要原因之一。由于android系统为每个应用程序分配的内存有限当一个应用中产生的内存泄漏比较多时就难免会导致应用所需要的内存超过这个系统分配的内存限额这就3.内存泄漏检测的工具有哪些 最常见的是Leakcanary4.关于Leakcanary使用介绍 leakCanary是Square开源框架是一个Android和Java的内存泄露检测库如果检测到某个 activity 有内存泄露LeakCanary 就是自动地显示一个通知所以可以把它理解为傻瓜式的内存泄露检测工具。通过它可以大幅度减少开发中遇到的oom问题大大提高APP的质量。关于如何配置这个就不说呢网上有步骤5.Leakcanary捕捉常见的内存泄漏及解决办法 5.0.1 错误使用单例造成的内存泄漏 在平时开发中单例设计模式是我们经常使用的一种设计模式而在开发中单例经常需要持有Context对象如果持有的Context对象生命周期与单例生命周期更短时或导致Context无法被释放回收则有可能造成内存泄漏错误写法如下问题引起内存泄漏代码public class LoginManager {private static LoginManager mInstance;private Context mContext;private LoginManager(Context context) {this.mContext context; //修改代码this.mContext context.getApplicationContext();}public static LoginManager getInstance(Context context) {if (mInstance null) {synchronized (LoginManager.class) {if (mInstance null) {mInstance new LoginManager(context);}}}return mInstance;}public void dealData() {}
}
复制代码使用场景 在一个Activity中调用的然后关闭该Activity则会出现内存泄漏。LoginManager.getInstance(this).dealData();
复制代码看看报错截图 解决办法 要保证Context和AppLication的生命周期一样修改后代码如下this.mContext context.getApplicationContext();1、如果此时传入的是 Application 的 Context因为 Application 的生命周期就是整个应用的生命周期所以这将没有任何问题。2、如果此时传入的是 Activity 的 Context当这个 Context 所对应的 Activity 退出时由于该 Context 的引用被单例对象所持有其生命周期等于整个应用程序的生命周期所以当前 Activity 退出时它的内存并不会被回收这就造成泄漏了。5.0.2 错误使用静态变量导致引用后无法销毁 在平时开发中有时候我们创建了一个工具类。比如分享工具类十分方便多处调用因此使用静态方法是十分方便的。但是创建的对象建议不要全局化全局化的变量必须加上static。这样会引起内存泄漏问题代码 使用场景 在Activity中引用后关闭该Activity会导致内存泄漏DoShareUtil.showFullScreenShareView(PNewsContentActivity.this, title, title, shareurl, logo);
复制代码查看报错 解决办法 静态方法中创建对象或变量不要全局化全局化后的变量或者对象会导致内存泄漏popMenuView和popMenu都不要全局化知识延伸非静态内部类静态实例化
public class MyActivity extends AppCompatActivity {//静态成员变量public static InnerClass innerClass null;Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_my);innerClass new InnerClass();}class InnerClass {public void doSomeThing() {}}
}
这里内部类InnerClass隐式的持有外部类MyActivity的引用而在MyActivity的onCreate方法中调用了。
这样innerClass就会在MyActivity创建的时候是有了他的引用而innerClass是静态类型的不会被垃圾回收
MyActivity在执行onDestory方法的时候由于被innerClass持有了引用而无法被回收所以这样MyActivity就总是被innerClass持有而无法回收造成内存泄露。静态变量引用不当会导致内存泄漏
静态变量Activity和View会导致内存泄漏在下面这段代码中对Activity的Context和TextView设置为静态对象从而产生内存泄漏。
public class MainActivity extends AppCompatActivity {private static Context context;private static TextView textView;Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);context this;textView new TextView(this);}
}
复制代码5.0.3 Handler使用不当造成的内存泄漏 handler是工作线程与UI线程之间通讯的桥梁只是现在大量开源框架对其进行了封装我们这里模拟一种常见使用方式来模拟内存泄漏情形。问题代码public class MainActivity extends AppCompatActivity {private Handler mHandler new Handler();private TextView mTextView;Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);mTextView (TextView) findViewById(R.id.text); //模拟内存泄露mHandler.postDelayed(new Runnable() {Overridepublic void run() {mTextView.setText(yangchong);}}, 2000);}
}
复制代码造成内存泄漏原因分析 上述代码通过内部类的方式创建mHandler对象,此时mHandler会隐式地持有一个外部类对象引用这里就是MainActivity当执行postDelayed方法时该方法会将你的Handler装入一个Message并把这条Message推到MessageQueue中MessageQueue是在一个Looper线程中不断轮询处理消息那么当这个Activity退出时消息队列中还有未处理的消息或者正在处理消息而消息队列中的Message持有mHandler实例的引用mHandler又持有Activity的引用所以导致该Activity的内存资源无法及时回收引发内存泄漏。查看报错结果如下 解决方案 第一种解决办法 要想避免Handler引起内存泄漏问题需要我们在Activity关闭退出的时候的移除消息队列中所有消息和所有的Runnable。上述代码只需在onDestroy()函数中调用mHandler.removeCallbacksAndMessages(null);就行了。Override
protected void onDestroy() {super.onDestroy();if(handler!null){handler.removeCallbacksAndMessages(null);handler null;}
}
复制代码第二种解决方案 使用弱引用解决handler内存泄漏问题public class SampleActivity extends Activity {private static class MyHandler extends Handler {private final WeakReferenceSampleActivity mActivity;public MyHandler(SampleActivity activity) {mActivity new WeakReferenceSampleActivity(activity);}Overridepublic void handleMessage(Message msg) {SampleActivity activity mActivity.get();if (activity ! null) {// ...}}private final MyHandler mHandler new MyHandler(this);private static final Runnable sRunnable new Runnable() {Overridepublic void run() { /* ... */ }};Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);mHandler.postDelayed(sRunnable, 1000 * 60 * 10);finish();}
}即推荐使用静态内部类 WeakReference 这种方式。每次使用前注意判空。
复制代码5.0.4 线程造成的内存泄漏 早时期的时候处理耗时操作多数都是采用ThreadHandler的方式后来逐步被AsyncTask取代直到现在采用RxJava的方式来处理异步。这里以AsyncTask为例可能大部分人都会这样处理一个耗时操作然后通知UI更新结果问题代码public class MainActivity extends AppCompatActivity {private AsyncTaskVoid, Void, Integer asyncTask;private TextView mTextView;Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);mTextView (TextView) findViewById(R.id.text);testAsyncTask();finish();}private void testAsyncTask() {asyncTask new AsyncTaskVoid, Void, Integer() {Overrideprotected Integer doInBackground(Void... params) {int i 0;//模拟耗时操作while (!isCancelled()) {i;if (i 1000000000) {break;}Log.e(LeakCanary, asyncTask---- i);}return i;}Overrideprotected void onPostExecute(Integer integer) {super.onPostExecute(integer);mTextView.setText(String.valueOf(integer));}};asyncTask.execute();}
}
复制代码造成内存泄漏原因分析 在处理一个比较耗时的操作时可能还没处理结束MainActivity就执行了退出操作但是此时AsyncTask依然持有对MainActivity的引用就会导致MainActivity无法释放回收引发内存泄漏查看报错结果如下 解决办法 在使用AsyncTask时在Activity销毁时候也应该取消相应的任务AsyncTask.cancel()方法避免任务在后台执行浪费资源进而避免内存泄漏的发生private void destroyAsyncTask() {if (asyncTask ! null !asyncTask.isCancelled()) {asyncTask.cancel(true);}asyncTask null;
}Override
protected void onDestroy() {super.onDestroy();destroyAsyncTask();
}
复制代码5.0.5 非静态内部类创建静态实例造成的内存泄漏 有的时候我们可能会在启动频繁的Activity中为了避免重复创建相同的数据资源可能会出现这种写法问题代码private static TestResource mResource null;
Override
protected 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的内存资源不能正常回收。5.0.6 不需要用的监听未移除会发生内存泄露 问题代码//add监听放到集合里面
tv.getViewTreeObserver().addOnWindowFocusChangeListener(new ViewTreeObserver.OnWindowFocusChangeListener() {Overridepublic void onWindowFocusChanged(boolean b) {//监听view的加载view加载出来的时候计算他的宽高等。}
});
复制代码解决办法//计算完后一定要移除这个监听
tv.getViewTreeObserver().removeOnWindowFocusChangeListener(this);
复制代码注意事项tv.setOnClickListener();//监听执行完回收对象不用考虑内存泄漏
tv.getViewTreeObserver().addOnWindowFocusChangeListene,add监听放到集合里面需要考虑内存泄漏
复制代码5.0.7 [常见]资源未关闭造成的内存泄漏 BroadcastReceiverContentObserverFileObserverCursorCallback等在 Activity onDestroy 或者某类生命周期结束之后一定要 unregister 或者 close 掉否则这个 Activity 类会被 system 强引用不会被内存回收。值得注意的是关闭的语句必须在finally中进行关闭否则有可能因为异常未关闭资源致使activity泄漏。5.0.7.1 广播注册之后没有被销毁导致内存泄漏 - 比如我们在Activity中注册广播如果在Activity销毁后不取消注册那么这个广播会一直存在系统中同上面所说的非静态内部类一样持有Activity引用导致内存泄露。因此注册广播后在Activity销毁后一定要取消注册。
- 在注册观察则模式的时候如果不及时取消也会造成内存泄露。比如使用RetrofitRxJava注册网络请求的观察者回调同样作为匿名内部类持有外部引用所以需要记得在不用或者销毁的时候取消注册。public class MeAboutActivity extends BaseActivity {Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);this.registerReceiver(mReceiver, new IntentFilter());}private BroadcastReceiver mReceiver new BroadcastReceiver() {Overridepublic void onReceive(Context context, Intent intent) {// 接收到广播需要做的逻辑}};Overrideprotected void onDestroy() {super.onDestroy();this.unregisterReceiver(mReceiver);}
}复制代码 5.0.7.2 资源未关闭导致资源被占用而内存泄漏 在使用IO、File流或者Sqlite、Cursor等资源时要及时关闭。这些资源在进行读写操作时通常都使用了缓冲如果及时不关闭这些缓冲对象就会一直被占用而得不到释放以致发生内存泄露。因此我们在不需要使用它们的时候就及时关闭以便缓冲能及时得到释放从而避免内存泄露。5.0.8 未注销EventBus导致的内存泄漏 直接展示代码Override
protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_common);EventBus.getDefault().register(this);
}Subscribe
public void onEvent(MessageEvent msg) {}Override
protected void onDestroy() {super.onDestroy();//未移除注册的EventBus//EventBus.getDefault().unregister(this);
}
复制代码5.0.9 持有activity引用未被释放导致内存泄漏 5.0.9.1 第一种场景 先看看问题代码 这个是在开发中经常会犯的错误NastyManager.getInstance() 是一个单例当我们通过 addListener(this) 将 Activity 作为 Listener 和 NastyManager 绑定起来的时候由于单例和Activity生命周期不同因此销毁时就会导致内存泄漏。public class LeakActivity extends Activity {Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);NastyManager.getInstance().addListener(this);}
}
复制代码解决办法 在你的 Acitivity 被销毁的时候将他和 NastyManager 取消掉绑定就好public class LeakActivity extends Activity {Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);NastyManager.getInstance().addListener(this);}Overrideprotected void onDestroy() {super.onDestroy();NastyManager.getInstance().removeListener(this);}
}
复制代码5.0.9.2 第二种场景 先来看看造成内存泄漏的代码 通过查看Toast类的源码可以看到Toast类内部的mContext指向传入的Context。而ToastUtils中的toast变量是静态类型的其生命周期是与整个应用一样长的从而导致activity得不到释放。因此对Context的引用不能超过它本身的生命周期。/*** 吐司工具类 避免点击多次导致吐司多次最后导致Toast就长时间关闭不掉了* param context 注意这里如果传入context会报内存泄漏传递activity..getApplicationContext()* param content 吐司内容*/
private static Toast toast;
SuppressLint(ShowToast)
public static void showToast(Context context, String content) {if (toast null) {toast Toast.makeText(context , content, Toast.LENGTH_SHORT);} else {toast.setText(content);}toast.show();
}
复制代码解决办法 是改为使用 ApplicationContext即可因为ApplicationContext会随着应用的存在而存在而不依赖于Activity的生命周期5.1.0 静态集合使用不当导致的内存泄漏 有时候我们需要把一些对象加入到集合容器例如ArrayList中当不再需要当中某些对象时如果不把该对象的引用从集合中清理掉也会使得GC无法回收该对象。如果集合是static类型的话那内存泄漏情况就会更为严重。因此当不再需要某对象时需要主动将之从集合中移除。5.1.1 动画资源未释放导致内存泄漏 问题代码public class LeakActivity extends AppCompatActivity {private TextView textView;Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_leak);textView (TextView)findViewById(R.id.text_view);ObjectAnimator objectAnimator ObjectAnimator.ofFloat(textView,rotation,0,360);objectAnimator.setRepeatCount(ValueAnimator.INFINITE);objectAnimator.start();}
}
复制代码解决办法 在属性动画中有一类无限循环动画如果在Activity中播放这类动画并且在onDestroy中去停止动画那么这个动画将会一直播放下去这时候Activity会被View所持有从而导致Activity无法被释放。解决此类问题则是需要早Activity中onDestroy去去调用objectAnimator.cancel()来停止动画。Override
protected void onDestroy() {super.onDestroy();mAnimator.cancel();
}
复制代码5.1.2 系统bug之InputMethodManager导致内存泄漏 每次从MainActivity退出程序时总会报InputMethodManager内存泄漏原因系统中的InputMethodManager持有当前MainActivity的引用导致了MainActivity不能被系统回收从而导致了MainActivity的内存泄漏。查了很多资料发现这是 Android SDK中输入法的一个Bug在15API23中都存在目前Google还没有解决这个Bug。6.其他建议 6.0.1 尽量避免使用 static 成员变量 尽量避免使用 static 成员变量 如果成员变量被声明为 static那我们都知道其生命周期将与整个app进程生命周期一样。这会导致一系列问题如果你的app进程设计上是长驻内存的那即使app切到后台这部分内存也不会被释放。按照现在手机app内存管理机制占内存较大的后台进程将优先回收如果此app做过进程互保保活那会造成app在后台频繁重启。当手机安装了你参与开发的app以后一夜时间手机被消耗空了电量、流量你的app不得不被用户卸载或者静默。架构设计上要思考是否真的有必要这样做尽量避免。如果架构需要这么设计那么此对象的生命周期你有责任管理起来。关于其他内容介绍 01.关于博客汇总链接 1.技术博客汇总2.开源项目汇总3.生活博客汇总4.喜马拉雅音频汇总5.其他汇总02.关于我的博客 我的个人站点www.yczbj.orgwww.ycbjie.cngithubgithub.com/yangchong21…知乎www.zhihu.com/people/yang…简书www.jianshu.com/u/b7b2c6ed9…csdnmy.csdn.net/m0_37700275喜马拉雅听书www.ximalaya.com/zhubo/71989…开源中国my.oschina.net/zbj1618/blo…泡在网上的日子www.jcodecraeer.com/member/cont…邮箱yangchong211163.com阿里云博客yq.aliyun.com/users/artic… 239.headeruserinfo.3.dT4bcVsegmentfault头条segmentfault.com/u/xiangjian…