如何使用AsyncTask防止内存泄漏(Handler同理)
2017-10-25 19:33
295 查看
Andorid AsyncTask防止内存泄漏
问题根源—-内部类
内部类在Java中是一个很常见的数据结构。它们很受欢迎,因为它们可以以这样的方式来定义:即只有外部类可以实例化它们。很多人可能没有意识到的是这样的类会持有外部类的隐式引用。隐式引用很容易出错,尤其是当两个类具有不同的生命周期。以下是常见的Android Activity写法。public class AsyncActivity extends Activity { TextView textView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_async); textView = (TextView) findViewById(R.id.textView); new BackgroundTask().execute(); } private class BackgroundTask extends AsyncTask<Void, Void, String>{ @Override protected String doInBackground(Void... params) { // Do background work. Code omitted. return "some string"; } @Override protected void onPostExecute(String result) { textView.setText(result); } } }
这种特殊的实现在执行上没有问题。问题是,它保留内存的时间肯定会超过必要的时间。由于BackgroundTask持有一个AsyncActivity隐式引用并运行在另一个没有取消策略的线程上,它将保留AsyncActivity在内存中的所有资源连接,直到后台线程终止运行。在HTTP请求的情况下,这可能需要很长的时间,尤其是在速度较慢的连接。
解决办法
我们第一要务是使用静态类的实现方式来消除指向Activity的引用,但这样我们也不能直接访问 textView了。因此我们还需要添加一个构造函数,把textView作为参数传递进来。最后,我们需要引入AsyncTask文档中所述的取消策略。考虑到所有这一切,让我们看看我们的代码最终呈现。public class AsyncActivity extends Activity { TextView textView; AsyncTask task; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_async); textView = (TextView) findViewById(R.id.textView); task = new BackgroundTask(textView).execute(); } @Override protected void onDestroy() { task.cancel(true); super.onDestroy(); } private static class BackgroundTask extends AsyncTask<Void, Void, String> { private final TextView resultTextView; public BackgroundTask(TextView resultTextView) { this.resultTextView = resultTextView; } @Override protected void onCancelled() { // Cancel task. Code omitted. } @Override protected String doInBackground(Void... params) { // Do background work. Code omitted. return "some string"; } @Override protected void onPostExecute(String result) { resultTextView.setText(result); } } }
但是。。。。。以上这种方法还存在问题。。。
resultTextView持有一个mContext引用,毫无疑问,它就是泄露的Activity的引用。那么如何解决这个问题?我们无法消除resultTextView绑定的context引用,因为我们需要在BackgroundTask中使用resultTextView的引用,以便更新用户界面。为了解决这个问题,一种简单的方法是使用WeakReference。我们持有的resultTextView引用是强引用,具有防止GC回收的能力。相反,WeakReference不保证其引用的实例存活。当一个实例最后一个强引用被删除,GC会把其资源回收,而不管这个实例是否有弱引用。下面是使用WeakReference的最终版本:
public class AsyncActivity extends Activity { TextView textView; AsyncTask task; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_async); textView = (TextView) findViewById(R.id.textView); task = new BackgroundTask(textView).execute(); } @Override protected void onDestroy() { task.cancel(true); super.onDestroy(); } private static class BackgroundTask extends AsyncTask<Void, Void, String> { private final WeakReference<TextView> textViewReference; public BackgroundTask(TextView resultTextView) { this.textViewReference = new WeakReference<>(resultTextView); } @Override protected void onCancelled() { // Cancel task. Code omitted. } @Override protected String doInBackground(Void... params) { // Do background work. Code omitted. return "some string"; } @Override protected void onPostExecute(String result) { TextView view = textViewReference.get(); if (view != null) { view.setText(result); } } } }
请注意,在onPostExecute我们要检查空值,判断实例是否被回收。
以上才算正在实现内存不出现泄漏。
相关文章推荐
- 如何避免使用Handler造成的内存泄漏
- 如何防止activity的双重引用和Handler等造成内存泄漏
- 使用AsyncTask防止Memory Leaks(内存泄漏)的正确姿势
- Android使用Handler防止内存泄漏
- Android使用Handler防止内存泄漏
- Handler的正确使用,防止内存泄漏
- 性能优化 -- 如何优雅的防止Handler引发的内存泄漏
- 如何更加有效防止用户使用外挂
- 新手--Discuz!使用技巧如何防止用户在论坛恶意灌水?
- 使用HttpHandler 实现无页面退出和防止盗链
- 如何防止他人使用旧id和旧口令访问Domino服务器
- 如何防止动态加载JavaScript引起的内存泄漏问题
- 如何防止使用盗版windows xp系统被黑屏,嘿嘿
- [IPhone]如何使用Leak检查内存泄漏
- 如何使用远程工具跟踪 Windows CE 应用程序中的内存泄漏(二)
- 在自定义HttpHandler中如何使用Session
- 如何使用jquery对特殊字符进行转义,防止js注入
- 如何防止别人使用、更改或删除电脑里的重要资料?
- S60 3rd下如何使用HookLogger查找内存泄漏错误?
- 如何使用远程工具跟踪 Windows CE 应用程序中的内存泄漏(一)