Android 开发者必会的内存泄漏指南( 三 )


当然,解决方案也非常地简单,如下:
public class GoodActivity extends Activity {private AsyncTask mLongRunningTask;private TextView mMessageView;@Overrideprotected voidonCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.layout_good_activity);mMessageView = (TextView) findViewById(R.id.messageView);mLongRunningTask = new LongRunningTask(mMessageView).execute;}@Overrideprotected voidonDestroy {super.onDestroy;mLongRunningTask.cancel(true);}private static class LongRunningTask extends AsyncTask<Void, Void, String> {private final WeakReference<TextView> messageViewReference;publicLongRunningTask(TextView messageView) {this.messageViewReference = new WeakReference<>(messageView);}@Overrideprotected String doInBackground(Void... params) {String message = ;if (!isCancelled) {message = "I am finally done!";}return message;}@Overrideprotected voidonPostExecute(String result) {TextView view = messageViewReference.get;if (view != ) {view.setText(result);}}}}正如你看到的代码,首先我将非静态内部类改成了静态内部类,这样它就不会持有外部类的引用了 。当然,使用静态的内部类,非静态的变量就不能访问了 。所以我们需要将 TextView 通过构造方法把它传过去 。
在这里,我强烈推荐使用 WeakReference,它能更好的避免引起内存泄漏 。你应该去学习 Java 中关于不同引用类型的知识:
http://javarevisited.blogspot.in/2014/03/difference-between-weakreference-vs-softreference-phantom-strong-reference-java.html

  • 匿名内部类
【Android 开发者必会的内存泄漏指南】匿名内部类也是在开发过程中经常使用到的一个东西,它的定义和使用都非常的简洁 。但以我的经验来看,匿名内部类造成了大量的内存泄漏的问题 。
匿名内部类与非静态内部类相似,造成内部类的原因也和上面说的一样 。你有可能在好多地方都使用了匿名内部类,如果使用不当,会严重影响 App 的性能 。
public class MoviesActivity extends Activity {private TextView mNoOfMoviesThisWeek;@Overrideprotected voidonCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.layout_movies_activity);mNoOfMoviesThisWeek = (TextView) findViewById(R.id.no_of_movies_text_view);MoviesRepository repository = ((MoviesApp) getApplication).getRepository;repository.getMoviesThisWeek.enqueue(new Callback<List<Movie>> {@Overridepublic voidonResponse(Call<List<Movie>> call,Response<List<Movie>> response) {int numberOfMovies = response.body.size;mNoOfMoviesThisWeek.setText("No of movies this week: " + String.valueOf(numberOfMovies));}@Overridepublic voidonFailure(Call<List<Movie>> call, Throwable t) {// Oops.}});}}上面的例子中,我使用一个常用的网络库 Retrofit 发送了一个网络请求,然后在 TextView 中显示返回的结果 。很明显,那个 Callback 对象持有 Activity 的引用 。如果现在网络很慢,在网络响应回来之前,页面旋转或者关闭,就会导致 Activity 泄漏 。
我强烈建议,在需要的时候,尽量使用静态的内部类,而非匿名内部类 。当然,我的意思不是不在使用匿名内部类,如果你需要使用匿名内部类,你需要注意引起内存泄漏的问题,保证不会出现问题 。
  • Bitmaps
在应用中,你看到的所有图片都是 Bitmap 对象,包含了所有的像素数据 。现在这些 Bitmap 数据非常的大,一个处理不好,就会引起 OOM,造成 APP 崩溃 。在 APP 中使用的图片资源生成的 Bitmap 会由系统进行管理,但是如果你需要自己处理 Bitmap,要记住,使用完过后要调用 bitmap.recycle 来释放资源 。
在处理 Bitmap 时,需要将一张大的图缩放变小过后,在使用,多重用同一个图片数据 。google 官方有一个关于处理 Bitmap 内存的文档:
https://developer.android.com/training/displaying-bitmaps/manage-memory.html
  • Contexts
另一个是关于 Context 的滥用引起的内存泄漏 。Activity / Application / Service 都是继承自 Context 并实现它们自己的功能,但是你也需要搞清楚它们之间的区别,什么是 activity 级别的 Context,什么是 application 级别的 Context,根据项目需求的场景去选择使用哪一个 Context。错误地使用 Activity Context,导致引用不能被释放,就会引起内存泄漏 。
7.结语现在,你知道了什么是垃圾回收器,什么是内存泄漏,内存泄漏给你带来的影响 。你也知道如何检测和修复内存泄漏 。
从现在开始,构建高质量/高性能的应用 。处理内存泄漏不仅能让你的应用有更好的用户体验,也能让你成为更好的开发者 。


推荐阅读