[Android Memory] App调试内存泄露之Context篇(下)
转载地址:http://www.cnblogs.com/qianxudetianxia/p/3655475.html
5. AsyncTask对象
我N年前去盛大面过一次试,当时面试官极力推荐我使用AsyncTask等系统自带类去做事情,当然无可厚非。
但是AsyncTask确实需要额外注意一下。它的泄露原理和前面Handler,Thread泄露的原理差不多,它的生命周期和Activity不一定一致。
解决方案是:在activity退出的时候,终止AsyncTask中的后台任务。
但是,问题是如何终止?
AsyncTask提供了对应的API:public final boolean cancel (boolean mayInterruptIfRunning)。
它的说明有这么一句话:
// Attempts to cancel execution of this task. This attempt will fail if the task has already completed, already been cancelled, or could not be cancelled for some other reason.
// If successful, and this task has not started when cancel is called, this task should never run. If the task has already started, then the mayInterruptIfRunning parameter determines whether the thread executing this task should be interrupted in an attempt to stop the task.
cancel是不一定成功的,如果正在运行,它可能会中断后台任务。怎么感觉这话说的这么不靠谱呢?
是的,就是不靠谱。
那么,怎么才能靠谱点呢?我们看看官方的示例:
private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {
protected Long doInBackground(URL... urls) {
int count = urls.length;
long totalSize = 0;
for (int i = 0; i < count; i++) {
totalSize += Downloader.downloadFile(urls[i]);
publishProgress((int) ((i / (float) count) * 100));
// Escape early if cancel() is called
// 注意下面这行,如果检测到cancel,则及时退出
if (isCancelled()) break;
}
return totalSize;
}
protected void onProgressUpdate(Integer... progress) {
setProgressPercent(progress[0]);
}
protected void onPostExecute(Long result) {
showDialog("Downloaded " + result + " bytes");
}
}
官方的例子是很好的,在后台循环中时刻监听cancel状态,防止没有及时退出。
为了提醒大家,google特意在AsyncTask的说明中撂下了一大段英文:
// AsyncTask is designed to be a helper class around Thread and Handler and does not constitute a generic threading framework. AsyncTasks should ideally be used for short operations (a few seconds at the most.) If you need to keep threads running for long periods of time, it is highly recommended you use the various APIs provided by the java.util.concurrent pacakge such as Executor, ThreadPoolExecutor and FutureTask.
可怜我神州大陆幅员辽阔,地大物博,什么都不缺,就是缺对英语阅读的敏感。
AsyncTask适用于短耗时操作,最多几秒钟。如果你想长时间耗时操作,请使用其他java.util.concurrent包下的API,比如Executor, ThreadPoolExecutor 和 FutureTask.
学好英语,避免踩坑!
6. BroadcastReceiver对象
... has leaked IntentReceiver ... Are you missing a call to unregisterReceiver()?
这个直接说了,种种原因没有调用到unregister()方法。
解决方法很简单,就是确保调用到unregister()方法。
顺带说一下,我在工作中碰到一种相反的情况,receiver对象没有registerReceiver()成功(没有调用到),于是unregister的时候提示出错:
// java.lang.IllegalArgumentException: Receiver not registered ...
有两种解决方案:
方案一:在registerReceiver()后设置一个FLAG,根据FLAG判断是否unregister()。网上搜到的文章几乎都这么写,我以前碰到这种bug,也是一直都这么解。但是不可否认,这种代码看上去确实有点丑陋。
方案二:我后来无意中听到某大牛提醒,在Android源码中看到一种更通用的写法:
// just sample, 可以写入工具类
// 第一眼我看到这段代码,靠,太粗暴了,但是回头一想,要的就是这么简单粗暴,不要把一些简单的东西搞的那么复杂。
private void unregisterReceiverSafe(BroadcastReceiver receiver) {
try {
getContext().unregisterReceiver(receiver);
} catch (IllegalArgumentException e) {
// ignore
}
}
7. TimerTask对象
TimerTask对象在和Timer的schedule()方法配合使用的时候极容易造成内存泄露。
private void startTimer(){
if (mTimer == null) {
mTimer = new Timer();
}
if (mTimerTask == null) {
mTimerTask = new TimerTask() {
@Override
public void run() {
// todo
}
};
}
if(mTimer != null && mTimerTask != null )
mTimer.schedule(mTimerTask, 1000, 1000);
}
泄露的点是,忘记cancel掉Timer和TimerTask实例。cancel的时机同cursor篇说的,在合适的时候cancel。
private void cancelTimer(){
if (mTimer != null) {
mTimer.cancel();
mTimer = null;
}
if (mTimerTask != null) {
mTimerTask.cancel();
mTimerTask = null;
}
}
8. Observer对象。
Observer对象的泄露,也是一种常见、易发现、易解决的泄露类型。
先看一段正常的代码:
// 其实也非常简单,只不过ContentObserver是系统的例子,有必要单独拿出来提示一下大家,不可掉以轻心
private final ContentObserver mSettingsObserver = new ContentObserver(new Handler()) {
@Override
public void onChange(boolean selfChange, Uri uri) {
// todo
}
}; @Override
public void onStart() {
super.onStart(); // register the observer
getContentResolver().registerContentObserver(Settings.Global.getUriFor(
xxx), false, mSettingsObserver);
} @Override
public void onStop() {
super.onStop(); // unregister it when stoping
getContentResolver().unregisterContentObserver(mSettingsObserver); }
看完示例,我们来看看病例:
private final class SettingsObserver implements Observer {
public void update(Observable o, Object arg) {
// todo ...
}
}
mContentQueryMap = new ContentQueryMap(mCursor, Settings.System.XXX, true, null);
mContentQueryMap.addObserver(new SettingsObserver());
靠,谁这么偷懒,把SettingObserver搞个匿名对象传进去,这可如何是好?
所以,有些懒是不能偷的,有些语法糖是不能吃的。
解决方案就是, 在不需要或退出的时候delete这个Observer。
private Observer mSettingsObserver;
@Override
public void onResume() {
super.onResume();
if (mSettingsObserver == null) {
mSettingsObserver = new SettingsObserver();
}
mContentQueryMap.addObserver(mSettingsObserver);
} @Override
public void onStop() {
super.onStop();
if (mSettingsObserver != null) {
mContentQueryMap.deleteObserver(mSettingsObserver);
}
mContentQueryMap.close();
}
注意一点,不同的注册方法,不同的反注册方法。
// 只是参考,不必死板
/*
addCallback <==> removeCallback
registerReceiver <==> unregisterReceiver
addObserver <==> deleteObserver
registerContentObserver <==> unregisterContentObserver
... ...
*/
9. Dialog对象
android.view.WindowManager$BadTokenException: Unable to add window -- token android.os.BinderProxy@438afa60 is not valid; is your activity running?
一般发生于Handler的MESSAGE在排队,Activity已退出,然后Handler才开始处理Dialog相关事情。
关键点就是,怎么判断Activity是退出了,有人说,在onDestroy中设置一个FLAG。我很遗憾的告诉你,这个错误很有可能还会出来。
解决方案是:使用isFinishing()判断Activity是否退出。
Handler handler = new Handler() {
public void handleMessage(Message msg) {
switch (msg.what) {
case MESSAGE_1:
// isFinishing == true, 则不处理,尽快结束
if (!isFinishing()) {
// 不退出
// removeDialog()
// showDialog()
}
break;
default:
break;
}
super.handleMessage(msg);
}
};
早完早释放!
10. 其它对象
以Listener对象为主,"把自己搭进去了,切记一定要及时把自己放出来"。
11. 小结
结合本文Context篇和前面Cursor篇,我们枚举了大量的泄露实例,大部分根本原因都是相似的。
通过分析这些例子后,我们应该能理解APP层90%的内存泄露情况了。
至于怎么发现和定位内存泄露,这是另外一个有意思的话题,现在只能说,有方法有工具。
[Android Memory] App调试内存泄露之Context篇(下)的更多相关文章
- [Android Memory] App调试内存泄露之Context篇(上)
转载自:http://www.cnblogs.com/qianxudetianxia/p/3645106.html Context作为最基本的上下文,承载着Activity,Service等最基本组件 ...
- Android学习系列(37)--App调试内存泄露之Context篇(下)
接着<Android学习系列(36)--App调试内存泄露之Context篇(上)>继续分析. 5. AsyncTask对象 我N年前去盛大面过一次试,当时面试官极力推荐我使用AsyncT ...
- Android学习系列(36)--App调试内存泄露之Context篇(上)
Context作为最基本的上下文,承载着Activity,Service等最基本组件.当有对象引用到Activity,并不能被回收释放,必将造成大范围的对象无法被回收释放,进而造成内存泄漏. 下面针对 ...
- Instruments指南:如何调试内存泄露
Instruments指南:如何调试内存泄露 开篇 现在,你应该使用的ARC,而不是原来我们使用的MRC或者其他.但是我们在使用ARC的时候也会出现内存泄露的情况. 幸运的是,苹果为我们提供了Inst ...
- Android handler 可能会造成内存泄露
Android handler 可能会造成内存泄露 Android Studio 使用 Handler 时: private Handler handler = new Handler(){ @Ove ...
- Android使用Handler造成内存泄露的分析及解决方法
一.什么是内存泄露? Java使用有向图机制,通过GC自动检查内存中的对象(什么时候检查由虚拟机决定),如果GC发现一个或一组对象为不可到达状态,则将该对象从内存中回收.也就是说,一个对象不被任何引用 ...
- Android内存优化7 内存检测工具1 Memory Monitor检测内存泄露
上篇说了一些性能优化的理论部分,主要是回顾一下,有了理论,小平同志又讲了,实践是检验真理的唯一标准,对于内存泄露的问题,现在通过Android Studio自带工具Memory Monitor 检测出 ...
- Android性能优化第(二)篇---Memory Monitor检测内存泄露
上篇说了一些性能优化的理论部分,主要是回顾一下,有了理论,小平同志又讲了,实践是检验真理的唯一标准,对于内存泄露的问题,现在通过Android Studio自带工具Memory Monitor 检测出 ...
- Android Studio 使用Memory Monitor进行内存泄露分析
在使用Android Studio进行内存泄露分析之前,我们先回顾一下Java相关的内存管理机制,然后再讲述一下内存分析工具如何使用. 一.Java内存管理机制 1. Java内存分配策略 Java ...
随机推荐
- newinstance和new有什么区别
用newInstance与用new是区别的,区别在于创建对象的方式不一样,前者是使用类加载机制,那么为什么会有两种创建对象方式?这个就要从可伸缩.可扩展,可重用等软件思想上解释了.Java中工厂模式经 ...
- Effective Java之最佳建议
#Effective Java之最佳建议 此书中,目前给我帮助最大的两条建议是: - 检查参数的有效性 - 不要忽略异常 ###检查参数的有效性 对于这一条,使我感同身受的原因是:在Web项目中,前期 ...
- WinForm TextBox 焦点停留到文本最后
最近写个 WinForm 项目,TextBox 控件有内容的时候,获取焦点,光标总是在最前面,很不便于输入.那怎么样让光标停留到最后呢?如下代码可以实现: this.txtBox ...
- php获取当前方法名和类名
php提供的一些系统常量可以完成这些 php获取当前方法名(函数名) __FUNCTION__ php获取当前类名 __CLASS__ 或者 get_class($this); php获取本类所有的方 ...
- 网站的PV,UV,IP名词解释
PV:PV 是Page Views的缩写,即页面浏览量,用户每一次对网站中的每个网页访问均被记录一次.注意,访客每刷新一次页面,pv就增加一次. UV:UV是Unique Visitor的缩写,即独立 ...
- EDdb 是ED数据
eddb 是ED数据统计汇总软件的简称,用于统计汇总企事业单位的各类信息数据. 采用Excel界面,操作简单. 对各类信息数据,均可以自定义数据格式,通过internet联网,收集各类信息数据,并通 ...
- You should blog even if you have no readers
Spencer Fry wrote a great post on "Why entrepreneurs should write." I would further add th ...
- UVALive 6124 Hexagon Perplexagon 题解
http://vjudge.net/problem/viewProblem.action?id=37480 East Central Regional Contest Problem C: Hexag ...
- C# JavascriptSerializer与匿名对象打造Json的完美工具
一:背景 在web项目中经常需要生成json数据,返回给前端ajax. 无论是ashx,还是WebMethod,可以人工的用字符串去拼接,最终得到json数据. 有没有更好的方法呢?我个人推荐使用Ja ...
- Hello Kitty微信主题很可爱?小心财产安全!
个性化是产品服务的一个趋势.微信很火,可为什么微信主题只有一个呢?你让那些小女生情何以堪?这时HelloKitty微信主题.大嘴猴.哆啦A梦等一大批主题在网上出现了,有些打着免费的旗号却做着盗号的勾当 ...