本文共 13541 字,大约阅读时间需要 45 分钟。
5.Leakcanary捕捉常见的内存泄漏及解决办法
6.其他建议
7.版本更新
问题引起内存泄漏代码
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() {}}
使用场景
LoginManager.getInstance(this).dealData();
看看报错截图
解决办法:
问题代码
使用场景
DoShareUtil.showFullScreenShareView(PNewsContentActivity.this, title, title, shareurl, logo);
查看报错
解决办法
知识延伸
非静态内部类,静态实例化public class MyActivity extends AppCompatActivity { //静态成员变量 public static InnerClass innerClass = null; @Override protected 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; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); context = this; textView = new TextView(this); }}
问题代码
public class MainActivity extends AppCompatActivity { private Handler mHandler = new Handler(); private TextView mTextView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mTextView = (TextView) findViewById(R.id.text); //模拟内存泄露 mHandler.postDelayed(new Runnable() { @Override public void run() { mTextView.setText("yangchong"); } }, 2000); }}
造成内存泄漏原因分析
查看报错结果如下:
解决方案
第一种解决办法
@Overrideprotected void onDestroy() { super.onDestroy(); if(handler!=null){ handler.removeCallbacksAndMessages(null); handler = null; }}
第二种解决方案
public class SampleActivity extends Activity { private static class MyHandler extends Handler { private final WeakReferencemActivity; public MyHandler(SampleActivity activity) { mActivity = new WeakReference (activity); } @Override public 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() { @Override public void run() { /* ... */ } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mHandler.postDelayed(sRunnable, 1000 * 60 * 10); finish(); }}即推荐使用静态内部类 + WeakReference 这种方式。每次使用前注意判空。
问题代码
public class MainActivity extends AppCompatActivity { private AsyncTaskasyncTask; private TextView mTextView; @Override protected 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 AsyncTask () { @Override protected Integer doInBackground(Void... params) { int i = 0; //模拟耗时操作 while (!isCancelled()) { i++; if (i > 1000000000) { break; } Log.e("LeakCanary", "asyncTask---->" + i); } return i; } @Override protected void onPostExecute(Integer integer) { super.onPostExecute(integer); mTextView.setText(String.valueOf(integer)); } }; asyncTask.execute(); }}
造成内存泄漏原因分析
查看报错结果如下:
解决办法
private void destroyAsyncTask() { if (asyncTask != null && !asyncTask.isCancelled()) { asyncTask.cancel(true); } asyncTask = null;}@Overrideprotected void onDestroy() { super.onDestroy(); destroyAsyncTask();}
问题代码
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会导致内存泄漏}
解决办法
分析问题
问题代码
//add监听,放到集合里面tv.getViewTreeObserver().addOnWindowFocusChangeListener(new ViewTreeObserver.OnWindowFocusChangeListener() {@Overridepublic void onWindowFocusChanged(boolean b) {//监听view的加载,view加载出来的时候,计算他的宽高等。}});
解决办法
//计算完后,一定要移除这个监听tv.getViewTreeObserver().removeOnWindowFocusChangeListener(this);
注意事项:
tv.setOnClickListener();//监听执行完回收对象,不用考虑内存泄漏tv.getViewTreeObserver().addOnWindowFocusChangeListene,add监听,放到集合里面,需要考虑内存泄漏
- 比如我们在Activity中注册广播,如果在Activity销毁后不取消注册,那么这个广播会一直存在系统中,同上面所说的非静态内部类一样持有Activity引用,导致内存泄露。因此注册广播后在Activity销毁后一定要取消注册。- 在注册观察则模式的时候,如果不及时取消也会造成内存泄露。比如使用Retrofit+RxJava注册网络请求的观察者回调,同样作为匿名内部类持有外部引用,所以需要记得在不用或者销毁的时候取消注册。
public class MeAboutActivity extends BaseActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); this.registerReceiver(mReceiver, new IntentFilter()); } private BroadcastReceiver mReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { // 接收到广播需要做的逻辑 } }; @Override protected void onDestroy() { super.onDestroy(); this.unregisterReceiver(mReceiver); }}
直接展示代码
@Overrideprotected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_common); EventBus.getDefault().register(this);}@Subscribepublic void onEvent(MessageEvent msg) {}@Overrideprotected void onDestroy() { super.onDestroy(); //未移除注册的EventBus //EventBus.getDefault().unregister(this);}
先看看问题代码
public class LeakActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); NastyManager.getInstance().addListener(this); }}
解决办法:
public class LeakActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); NastyManager.getInstance().addListener(this); } @Override protected void onDestroy() { super.onDestroy(); NastyManager.getInstance().removeListener(this); }}
先来看看造成内存泄漏的代码
/** * 吐司工具类 避免点击多次导致吐司多次,最后导致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();}
解决办法
问题代码
public class LeakActivity extends AppCompatActivity { private TextView textView; @Override protected 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(); }}
解决办法
@Overrideprotected void onDestroy() { super.onDestroy(); mAnimator.cancel();}
尽量避免使用 static 成员变量
转载地址:http://bqupo.baihongyu.com/