内存泄漏的示例1.由单例导致的内存泄漏
public class AppManager {
private static AppManager instance;
private Context context;
private AppManager(Context context) {
this.context = context;
}
public static AppManager getInstance(Context context) {
if (instance == null) {
instance = new AppManager(context);
}
return instance;
}
}
由于需要传递一个Context,因此该Context的生命周期的长度非常重要:
正确的方法:
public class AppManager {
private static AppManager instance;
private Context context;
private AppManager(Context context) {
this.context = context.getApplicationContext();
}
public static AppManager getInstance(Context context) {
if (instance == null) {
instance = new AppManager(context);
}
return instance;
}
}
这样,无论传入什么上下文,最终都将使用应用程序上下文,并且单例的生命周期与应用程序一样长,从而防止内存泄漏
2.由于创建非静态内部类的静态实例而导致的内存泄漏
有时为了避免重复创建相同的数据资源,我们可能会频繁地在Activity中开始,这种书写方式可能会出现:
public class MainActivity extends AppCompatActivity {
private static TestResource mResource = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if(mManager == null){
mResource = new TestResource();
}
//...
}
class TestResource {
//...
}
}
通过这种方式,在Activity内部创建了一个非静态内部类的单例。每次启动活动时,将使用单例数据。尽管避免了资源的重复创建,但是这种写入方式将导致内存泄漏。 ,因为默认情况下非静态内部类将保存外部类的引用,并且非静态内部类用于创建静态实例。实例的生命周期与应用程序的生命周期相同,这将导致静态实例始终保留对Activity的引用,从而导致Activity的内存资源无法正常恢复。正确的方法是:
将内部类设置为静态内部类,或提取内部类并将其封装为单例。如果需要使用上下文,请使用ApplicationContext
3.处理程序导致的内存泄漏
使用Handler引起的内存泄漏问题应该说是最常见的。通常,应在Handler的帮助下处理诸如处理网络任务或封装一些请求回调之类的api。使用Handler的代码编写不正确可能会导致内存泄漏,以下示例:
public class MainActivity extends AppCompatActivity {
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
//...
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
loadData();
}
private void loadData(){
//...request
Message message = Message.obtain();
mHandler.sendMessage(message);
}
}

这种创建Handler的方式将导致内存泄漏。由于mHandler是Handler的非静态匿名内部类的实例,因此它拥有对外部类Activity的引用。我们知道消息循环在Looper线程中不断轮询。处理消息,然后在Activity退出时,消息队列中有未处理的消息或正在处理消息,并且消息队列中的Message持有对mHandler实例的引用,而mHandler持有对Activity的引用,这导致不能及时恢复活动资源的内存,从而导致内存泄漏,因此另一种方法是:
public class MainActivity extends AppCompatActivity {
private MyHandler mHandler = new MyHandler(this);
private TextView mTextView ;
private static class MyHandler extends Handler {
private WeakReference reference;
public MyHandler(Context context) {
reference = new WeakReference<>(context);
}
@Override
public void handleMessage(Message msg) {
MainActivity activity = (MainActivity) reference.get();
if(activity != null){
activity.mTextView.setText("");
}
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTextView = (TextView)findViewById(R.id.textview);
loadData();
}
private void loadData() {
//...request
Message message = Message.obtain();
mHandler.sendMessage(message);
}
}
创建一个静态Handler内部类,然后使用对Handler所持有对象的弱引用,以便在回收期间还可以回收Handler所持有的对象。尽管避免了活动泄漏,但是在Looper线程的消息队列中仍然有可能要处理消息,因此我们应该在销毁或停止活动期间删除消息队列中的消息。更准确的方法如下:
public class MainActivity extends AppCompatActivity {
private MyHandler mHandler = new MyHandler(this);
private TextView mTextView ;
private static class MyHandler extends Handler {
private WeakReference reference;
public MyHandler(Context context) {
reference = new WeakReference<>(context);
}
@Override
public void handleMessage(Message msg) {
MainActivity activity = (MainActivity) reference.get();
if(activity != null){
activity.mTextView.setText("");
}
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTextView = (TextView)findViewById(R.id.textview);
loadData();
}
private void loadData() {
//...request
Message message = Message.obtain();
mHandler.sendMessage(message);
}
@Override
protected void onDestroy() {
super.onDestroy();
mHandler.removeCallbacksAndMessages(null);
}
}
使用mHandler.removeCallbacksAndMessages(null);删除消息队列中的所有消息和所有可运行对象。当然,您也可以使用mHandler.removeCallbacks();。或mHandler.removeMessages();删除指定的Runnable和Message。
4.线程引起的内存泄漏
线程引起的内存泄漏也很常见。以下两个示例可能是每个人都写的:
// ——————test1
new AsyncTask() {
@Override
protected Void doInBackground(Void... params) {
SystemClock.sleep(10000);
return null;
}
}.execute();
//——————test2
new Thread(new Runnable() {
@Override
public void run() {
SystemClock.sleep(10000);
}
}).start();
上述异步任务和Runnable都是匿名内部类,因此它们具有对当前Activity的隐式引用。如果在销毁活动之前任务尚未完成,
这将导致活动的内存资源无法回收,从而导致内存泄漏。正确的方法是使用静态内部类,如下所示:
static class MyAsyncTask extends AsyncTask {
private WeakReference weakReference;
public MyAsyncTask(Context context) {
weakReference = new WeakReference<>(context);
}
@Override
protected Void doInBackground(Void... params) {
SystemClock.sleep(10000);
return null;
}
@Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
MainActivity activity = (MainActivity) weakReference.get();
if (activity != null) {
//...
}
}
}
static class MyRunnable implements Runnable{
@Override
public void run() {
SystemClock.sleep(10000);
}
}
//——————
new Thread(new MyRunnable()).start();
new MyAsyncTask(this).execute();
通过这种方式,避免了Activity的内存资源泄漏。当然,在销毁Activity时,应取消相应的任务AsyncTask :: cancel(),以避免在后台执行任务时浪费资源。
5.由于资源未封闭导致内存泄漏
对于诸如BraodcastReceiver,ContentObserver,File,Cursor,Stream,Bitmap等资源的使用,您应该在Activity被销毁时及时关闭或注销,否则这些资源将不会被回收,从而导致内存泄漏
本文来自电脑杂谈,转载请注明本文网址:
http://www.pc-fly.com/a/shoujiruanjian/article-362135-1.html
某国际学校的老师出了一道开放性问题