几种导致内存泄漏的常见错误
Broadcast Receiver等未进行unregister
例如:当你在Activity中register broadcast receiver,假如你没有unregister the broadcast receiver,及时你关闭了Activity,仍然会持有Activity的引用
静态变量持有Activity、View或者大数据对象
例如:无论什么原因你生命了一个静态的activity或者view.如果activity或者view直接或者间接的被static引用,activity destroyed的时候不会被GC.
单例造成的内存泄漏
例如:你定义了一个静态类,需要传递一个context参数来实现一些功能。
public class SingletonSampleClass {
private Context context;
private static SingletonSampleClass instance;
private SingletonSampleClass(Context context) {
this.context = context;
}
public synchronized static SingletonSampleClass getInstance(Context context) {
if (instance == null) instance = new SingletonSampleClass(context);
return instance;
}
//some futures
public void onDestroy() {
if(context != null) {
context = null;
}
}
}
怎么解决?
- 使用ApplicationContext传入到单例中
- 如果非要使用Activity Context,在Activity destory的时候 设为null.
非静态内部类创建静态实例造成的内存泄漏
public class InnerClassReferenceLeakActivity extends AppCompatActivity {
/*
* Mistake Number 1:
* Never create a static variable of an inner class
* Fix I:
* private LeakyClass leakyClass;
*/
private static LeakyClass leakyClass;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_first);
new LeakyClass(this).redirectToSecondScreen();
/*
* Inner class is defined here
* */
leakyClass = new LeakyClass(this);
leakyClass.redirectToSecondScreen();
}
/*
* Mistake Number 2:
* 1. Never create a inner variable of an inner class
* 2. Never pass an instance of the activity to the inner class
*/
private class LeakyClass {
private Activity activity;
public LeakyClass(Activity activity) {
this.activity = activity;
}
public void redirectToSecondScreen() {
this.activity.startActivity(new Intent(activity, SecondActivity.class));
}
}
}
怎么解决?
- 永远不要用静态变量来定义非静态内部类。静态变量不会被GC,并且非静态内部类会持有外部类引用,导致外部类也不会被GC
- 应该吧内部类声明为静态内部类,这样内部类就不会持有外部类的引用。
- 在内部类中使用 WeakReference 来引用activity或者view.
public class InnerClassReferenceLeakActivity extends AppCompatActivity {
/*
* Mistake Number 1:
* Never create a static variable of an inner class
* Fix I:
*/
private LeakyClass leakyClass;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_first);
new LeakyClass(this).redirectToSecondScreen();
/*
* Inner class is defined here
* */
leakyClass = new LeakyClass(this);
leakyClass.redirectToSecondScreen();
}
/*
* How to fix the above class:
* Fix memory leaks:
* Option 1: The class should be set to static
* Explanation: Instances of anonymous classes do not hold an implicit reference to their outer class
* when they are "static".
*
* Option 2: Use a weakReference of the textview or any view/activity for that matter
* Explanation: Weak References: Garbage collector can collect an object if only weak references
* are pointing towards it.
* */
private static class LeakyClass {
private final WeakReference<Activity> messageViewReference;
public LeakyClass(Activity activity) {
this.activity = new WeakReference<>(activity);
}
public void redirectToSecondScreen() {
Activity activity = messageViewReference.get();
if(activity != null) {
activity.startActivity(new Intent(activity, SecondActivity.class));
}
}
}
}
AsyncTask或者线程造成的内存泄漏
例如:使用AsyncTask来获取一个String,并且OnPostExecute()
更新textView
public class AsyncTaskReferenceLeakActivity extends AppCompatActivity {
private TextView textView;
private BackgroundTask backgroundTask;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_first);
/*
* Executing AsyncTask here!
* */
backgroundTask = new BackgroundTask(textView);
backgroundTask.execute();
}
/*
* Couple of things we should NEVER do here:
* Mistake number 1. NEVER reference a class inside the activity. If we definitely need to, we should set the class as static as static inner classes don’t hold
* any implicit reference to its parent activity class
* Mistake number 2. We should always cancel the asyncTask when activity is destroyed. This is because the asyncTask will still be executing even if the activity
* is destroyed.
* Mistake number 3. Never use a direct reference of a view from acitivty inside an asynctask.
* */
private class BackgroundTask extends AsyncTask<Void, Void, String> {
@Override
protected String doInBackground(Void... voids) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "The task is completed!";
}
@Override
protected void onPostExecute(String s) {
super.onPostExecute(s);
textView.setText(s);
}
}
}
怎么解决?
- 在Activity destory的时候取消asyncTask任务。 如果不取消的话Activity destory后仍然会执行
- 在activity中永远不要用内部类,如果明确需要,应该用静态内部类,这样不会对外部类有隐式的引用
- 在内部类中使用 WeakReference 来引用activity或者view
public class AsyncTaskReferenceLeakActivity extends AppCompatActivity {
private TextView textView;
private BackgroundTask backgroundTask;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_first);
/*
* Executing AsyncTask here!
* */
backgroundTask = new BackgroundTask(textView);
backgroundTask.execute();
}
/*
* Fix number 1
* */
private static class BackgroundTask extends AsyncTask<Void, Void, String> {
private final WeakReference<TextView> messageViewReference;
private BackgroundTask(TextView textView) {
this.messageViewReference = new WeakReference<>(textView);
}
@Override
protected String doInBackground(Void... voids) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "The task is completed!";
}
@Override
protected void onPostExecute(String s) {
super.onPostExecute(s);
/*
* Fix number 3
* */
TextView textView = messageViewReference.get();
if(textView != null) {
textView.setText(s);
}
}
}
@Override
protected void onDestroy() {
super.onDestroy();
/*
* Fix number 2
* */
if(backgroundTask != null) {
backgroundTask.cancel(true);
}
}
}
Handler造成的内存泄漏
例如:使用Hanlder延迟5s跳转到新的activity
public class HandlersReferenceLeakActivity extends AppCompatActivity {
private TextView textView;
/*
* Mistake Number 1
* */
private Handler leakyHandler = new Handler();
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_first);
/*
* Mistake Number 2
* */
leakyHandler.postDelayed(new Runnable() {
@Override
public void run() {
textView.setText(getString(R.string.text_handler_1));
}
}, 5000);
}
怎么解决?
- 在activity中永远不要用内部类。使用hanlder发送 send message. message会持有handler对象.
Looper.loop()
时,会获取message持有的handler进行消息的处理 - 在内部类中使用 WeakReference 来引用activity或者view
public class HandlersReferenceLeakActivity extends AppCompatActivity {
private TextView textView;
/*
* Fix number I
* */
private final LeakyHandler leakyHandler = new LeakyHandler(this);
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_first);
leakyHandler.postDelayed(leakyRunnable, 5000);
}
/*
* Fix number II - define as static
* */
private static class LeakyHandler extends Handler {
/*
* Fix number III - Use WeakReferences
* */
private WeakReference<HandlersReferenceLeakActivity> weakReference;
public LeakyHandler(HandlersReferenceLeakActivity activity) {
weakReference = new WeakReference<>(activity);
}
@Override
public void handleMessage(Message msg) {
HandlersReferenceLeakActivity activity = weakReference.get();
if (activity != null) {
activity.textView.setText(activity.getString(R.string.text_handler_2));
}
}
}
private static final Runnable leakyRunnable = new Runnable() {
@Override
public void run() { /* ... */ }
}
Webview
待补充
资源型对象未关闭
例如:bitmap,stream等
对于资源性对象不再使用时,应该立即调用它的close()函数,将其关闭,然后在置为null。