亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 系統(tǒng) > Android > 正文

Android App調(diào)試內(nèi)存泄露之Cursor篇

2020-04-11 12:42:13
字體:
供稿:網(wǎng)友
最近在工作中處理了一些內(nèi)存泄露的問題,在這個過程中我尤其發(fā)現(xiàn)了一些基本的問題反而忽略導(dǎo)致內(nèi)存泄露,比如靜態(tài)變量,cursor關(guān)閉,線程,定時器,反注冊,bitmap等等,我稍微統(tǒng)計并總結(jié)了一下,當(dāng)然了,這些問題這么說起來比較籠統(tǒng),接下來我會根據(jù)問題,把一些實例代碼貼出來,一步一步分析,在具體的場景下,用行之有效的方法,找出泄露的根本原因,并給出解決方案。
現(xiàn)在,就從cursor關(guān)閉的問題開始把,誰都知道cursor要關(guān)閉,但是往往相反,人們卻常常忘記關(guān)閉,因為真正的應(yīng)用場景可能并非理想化的簡單。
1.理想化的cursor關(guān)閉
復(fù)制代碼 代碼如下:

// Sample Code
Cursor cursor = db.query();
List<String> list = convertToList(cursor);
cursor.close();

這是最簡單的cursor使用場景,如果這里的cursor沒有關(guān)閉,我想可能會引起萬千口水,一片罵聲。
但是實際場景可能并非如此,這里的cursor可能不會關(guān)閉,至少有以下兩種可能。
2. Cursor未關(guān)閉的可能
(1). cursor.close()之前發(fā)生異常。
(2). cursor需要繼續(xù)使用,不能馬上關(guān)閉,后面忘記關(guān)閉了。

3. Cursor.close()之前發(fā)生異常
這個很容易理解,應(yīng)該也是初學(xué)者最開始碰到的常見問題,舉例如下:
復(fù)制代碼 代碼如下:

try {
Cursor c = queryCursor();
int a = c.getInt(1);
......
// 如果出錯,后面的cursor.close()將不會執(zhí)行
......
c.close();
} catch (Exception e) {
}

正確寫法應(yīng)該是:
復(fù)制代碼 代碼如下:

Cursor c;
try {
c = queryCursor();
int a = c.getInt(1);
......
// 如果出錯,后面的cursor.close()將不會執(zhí)行
//c.close();
} catch (Exception e) {
} finally{
if (c != null) {
c.close();
}
} 

很簡單,但是需要時刻謹記。
4. Cursor需要繼續(xù)使用,不能馬上關(guān)閉
有沒有這種情況?怎么辦?
答案是有,CursorAdapter就是一個典型的例子。
CursorAdapter示例如下:
復(fù)制代碼 代碼如下:

mCursor = getContentResolver().query(CONTENT_URI, PROJECTION,
null, null, null);
mAdapter = new MyCursorAdapter(this, R.layout.list_item, mCursor);
setListAdapter(mAdapter);
// 這里就不能關(guān)閉執(zhí)行mCursor.close(),
// 否則list中將會無數(shù)據(jù)

5. 這樣的Cursor應(yīng)該什么時候關(guān)閉呢?
這是個可以說好回答也可以說不好回答的問題,那就是在Cursor不再使用的時候關(guān)閉掉。
比如說,
上面的查詢,如果每次進入或者resume的時候會重新查詢執(zhí)行。
一般來說,也是這種需求,很少我看不到界面的時候還在不停地顯示查詢結(jié)果吧,如果真的有,不予討論,記得最終關(guān)掉就OK了。
這個時候,我們一般可以在onStop()方法里面把cursor關(guān)掉。
復(fù)制代碼 代碼如下:

@Override
protected void onStop() {
super.onStop();
// mCursorAdapter會釋放之前的cursor,相當(dāng)于關(guān)閉了cursor
mCursorAdapter.changeCursor(null);
}

我專門附上CursorAdapter的changeCursor()方法源碼,讓大家看的更清楚,免得不放心changeCursor(null)方法:
復(fù)制代碼 代碼如下:

/**
* Change the underlying cursor to a new cursor. If there is an existing cursor it will be
* closed.
*
* @param cursor The new cursor to be used
*/
public void changeCursor(Cursor cursor) {
Cursor old = swapCursor(cursor);
if (old != null) {
old.close();
}
}

/**
* Swap in a new Cursor, returning the old Cursor. Unlike
* {@link #changeCursor(Cursor)}, the returned old Cursor is <em>not</em>
* closed.
*
* @param newCursor The new cursor to be used.
* @return Returns the previously set Cursor, or null if there wasa not one.
* If the given new Cursor is the same instance is the previously set
* Cursor, null is also returned.
*/
public Cursor swapCursor(Cursor newCursor) {
if (newCursor == mCursor) {
return null;
}
Cursor oldCursor = mCursor;
if (oldCursor != null) {
if (mChangeObserver != null) oldCursor.unregisterContentObserver(mChangeObserver);
if (mDataSetObserver != null) oldCursor.unregisterDataSetObserver(mDataSetObserver);
}
mCursor = newCursor;
if (newCursor != null) {
if (mChangeObserver != null) newCursor.registerContentObserver(mChangeObserver);
if (mDataSetObserver != null) newCursor.registerDataSetObserver(mDataSetObserver);
mRowIDColumn = newCursor.getColumnIndexOrThrow("_id");
mDataValid = true;
// notify the observers about the new cursor
notifyDataSetChanged();
} else {
mRowIDColumn = -1;
mDataValid = false;
// notify the observers about the lack of a data set
notifyDataSetInvalidated();
}
return oldCursor;
}

6.實戰(zhàn)AsyncQueryHandler中Cursor的關(guān)閉問題
AsyncQueryHandler是一個很經(jīng)典很典型的分析Cursor的例子,不僅一陣見血,能舉一反三,而且非常常見,為以后避免。
AsyncQueryHandler文檔參考地址:
http://developer.android.com/reference/android/content/AsyncQueryHandler.html
下面這段代碼是Android2.3系統(tǒng)中Mms信息主頁面ConversationList源碼的一部分,大家看看Cursor正確關(guān)閉了嗎?
復(fù)制代碼 代碼如下:

private final class ThreadListQueryHandler extends AsyncQueryHandler {
public ThreadListQueryHandler(ContentResolver contentResolver) {
super(contentResolver);
}

@Override
protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
switch (token) {
case THREAD_LIST_QUERY_TOKEN:
mListAdapter.changeCursor(cursor);
setTitle(mTitle);
... ...
break;

case HAVE_LOCKED_MESSAGES_TOKEN:
long threadId = (Long)cookie;
confirmDeleteThreadDialog(new DeleteThreadListener(threadId, mQueryHandler,
ConversationList.this), threadId == -1,
cursor != null && cursor.getCount() > 0,
ConversationList.this);
break;

default:
Log.e(TAG, "onQueryComplete called with unknown token " + token);
}
}
}

復(fù)制代碼 代碼如下:

@Override
protected void onStop() {
super.onStop();

mListAdapter.changeCursor(null);
}

大家覺得有問題嗎?
主要是兩點
(1). THREAD_LIST_QUERY_TOKEN分支的Cursor正確關(guān)閉了嗎?
(2). HAVE_LOCKED_MESSAGES_TOKEN分支的Cursor正確關(guān)閉了嗎?
根據(jù)前面的一條條分析,答案是:
(1). THREAD_LIST_QUERY_TOKEN分支的Cursor被傳遞到了mListAdapter了,而mListAdapter在onStop里面使用changeCursor(null),當(dāng)用戶離開當(dāng)前Activity,這個Cursor被正確釋放了,不會泄露。
(2). HAVE_LOCKED_MESSAGES_TOKEN分支的Cursor(就是參數(shù)cursor),只是作為一個判斷的一個條件,被使用后不再使用,但是也沒有關(guān)掉,所以cursor泄露,在StrictMode監(jiān)視下只要跑到這個地方都會拋出這個錯誤:

E/StrictMode(639): A resource was acquired at attached stack trace but never released. See java.io.Closeable for information on avoiding resource leaks.
E/StrictMode(639): java.lang.Throwable: Explicit termination method 'close' not called
E/StrictMode(639): at dalvik.system.CloseGuard.open(CloseGuard.java:184)
... ...

在Android.0 JellyBean中谷歌修正了這個泄露問題,相關(guān)代碼如下:
復(fù)制代碼 代碼如下:

private final class ThreadListQueryHandler extends ConversationQueryHandler {
public ThreadListQueryHandler(ContentResolver contentResolver) {
super(contentResolver);
}

@Override
protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
switch (token) {
case THREAD_LIST_QUERY_TOKEN:
mListAdapter.changeCursor(cursor);

... ...

break;

case UNREAD_THREADS_QUERY_TOKEN:
// 新增的UNREAD_THREADS_QUERY_TOKEN分子和HAVE_LOCKED_MESSAGES_TOKEN分支也是類似的情況,cursor在jellybean中被及時關(guān)閉了
int count = 0;
if (cursor != null) {
count = cursor.getCount();
cursor.close();
}
mUnreadConvCount.setText(count > 0 ? Integer.toString(count) : null);
break;

case HAVE_LOCKED_MESSAGES_TOKEN:
@SuppressWarnings("unchecked")
Collection<Long> threadIds = (Collection<Long>)cookie;
confirmDeleteThreadDialog(new DeleteThreadListener(threadIds, mQueryHandler,
ConversationList.this), threadIds,
cursor != null && cursor.getCount() > 0,
ConversationList.this);
// HAVE_LOCKED_MESSAGES_TOKEN分支中的cursor在jellybean中被及時關(guān)閉了
if (cursor != null) {
cursor.close();
}
break;

default:
Log.e(TAG, "onQueryComplete called with unknown token " + token);
}
}
}

復(fù)制代碼 代碼如下:

@Override
protected void onStop() {
super.onStop();
mListAdapter.changeCursor(null);
}

是不是小看了AsyncQueryHandler,谷歌在早期的版本里面都有一些這樣的代碼,更何況不注意的我們呢,實際上網(wǎng)上很多使用AsyncQueryHandler舉例中都犯了這個錯誤,看完這篇文章后,以后再也不怕AsyncQueryHandler的cursor泄露了,還說不定能解決很多你現(xiàn)在應(yīng)用的后臺strictmode的cursor not close異常問題。

7.小結(jié)
雖然我覺得還有很多cursor未關(guān)閉的情況沒有說到,但是根本問題都是及時正確的關(guān)閉cursor。
內(nèi)存泄露cursor篇是我工作經(jīng)驗上的一個總結(jié),專門捋清楚后對我自己對大家覺得都很有幫助,讓復(fù)雜的問題本質(zhì)化,簡單化!
發(fā)表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發(fā)表
无人区在线高清完整免费版 一区二| 久久手机免费视频| 欧美性猛交xxxx乱大交退制版| 国产在线第一页| 色先锋av资源中文字幕| 欧美日韩一区视频| 无人视频在线观看免费| 97超碰资源| www.av91| 成人在线免费看黄| 黄色毛片免费| 欧美日韩日日摸| 欧美精品在线观看91| 男人和女人啪啪网站| 中文字幕国产一区二区| 91成人抖音| √资源天堂中文在线| 成人一区二区视频| 国产在线观看中文字幕| 超碰国产精品久久国产精品99| 中文字幕在线观看网址| 日韩精品诱惑一区?区三区| 麻豆电影传媒二区| 天天躁日日躁狠狠躁喷水| 亚洲最新av网址| 国产一区二区在线免费| 99视频超级精品| 污视频网站免费在线观看| 精品蜜桃传媒| 99re热这里只有精品免费视频| 欧美一区二区三区激情视频| 亚洲天堂av在线免费| 91香蕉在线观看| 久无码久无码av无码| 日本一本视频| 女人被狂躁到高潮视频免费网站| 992tv在线影院| 99久久精品国产一区二区三区| 国产乱在线观看视频| 玖玖玖电影综合影院| 中文字幕第六页| 91亚洲精品久久久蜜桃网站| 精品一区在线视频| 曰本三级日本三级日本三级| 97在线观看视频国产| 亚洲性视频网址| 欧美日韩三级视频| 青青操免费在线视频| 日本中文字幕在线看| 国产又黄又粗又猛又爽| 综合自拍亚洲综合图不卡区| 日韩在线观看免费高清| www.xxxx国产| 91精品在线免费视频| 免费看黄色91| 国产成人免费视频精品含羞草妖精| 精品久久久久一区二区三区| 久久久精品国产免大香伊| 天天操天天擦| 日韩欧美成人午夜| 综合天堂久久久久久久| 精品视频久久久久久| 国产精选第一页| 精品视频在线一区二区在线| 性生交大片免费看l| 超碰在线人人爱| 亚洲欧美日韩国产成人| 久久久久久青草| aaa亚洲精品一二三区| 亚洲午夜久久久久中文字幕久| 国产精品天天看| 国产亚洲精品久久久久久| 免费污网站在线观看| 国产成人福利片| 欧美本精品男人aⅴ天堂| 1024成人| 欧美性少妇18aaaa视频| 久久精品国产99久久6| 欧美精品视频www在线观看| 婷婷亚洲五月色综合| 国产精品久久久久一区二区三区厕所| 爽爽影院免费观看视频| 黄色在线成人| 国产精品五月天| 亚洲成人999| 久久66热偷产精品| 91视频免费网站| 欧美人伦禁忌dvd放荡欲情| 国产福利一区二区三区视频| 伊人精品一区| 欧美国产日本视频| 国产性生活一级片| 狠狠狠色丁香婷婷综合久久五月| 中文字幕在线观看网站| 亚洲天堂av高清| 亚洲图色中文字幕| 97精品视频| 欧美videossex极品| 日韩av不卡一区| 久久久青草青青国产亚洲免观| 欧美在线色图| 欧美挠脚心视频网站| 女生裸体无遮挡天堂网站免费| 一级做a爱片久久毛片| 国产精品无码免费专区午夜| 欧美在线小视频| 可以看美女隐私的网站| 成人免费在线播放视频| 婷婷成人在线| 精品久久久久久亚洲精品| 亚洲成人网上| 亚洲国产精品yw在线观看| 91精品国产综合久久精品图片| 亚洲图片欧洲图片日韩av| 免费影院在线观看一区| 国产中文av在线| 久久97人妻无码一区二区三区| 91在线免费观看网站| 自拍偷拍在线视频| 欧美电影精品一区二区| 黄色影院在线观看| porn亚洲| 四虎www4hu永久免费| 亚洲欧洲成人在线| 24小时免费看片在线观看| 五月婷婷丁香色| 亚洲一区二区三区无吗| 日本电影一区二区在线观看| 麻豆av免费在线| 欧洲在线免费视频| 精品久久久久久久久久久久久久久| 欧美日韩一区二区区| 香蕉视频在线播放| 九色国产蝌蚪视频| 波多野结衣高清在线| 羞羞的视频在线| 激情六月婷婷久久| 中文字幕视频一区二区| 91在线资源| 一区二区三区日韩欧美精品| 卡通欧美亚洲| 美女激情福利视频在线观看| 国产综合中文字幕| xvideos亚洲人网站| 亚洲色大成网站www久久九九| 亚洲视频在线观看一区| 亚洲精品影视在线| 国产一区免费在线| 国产91精品久久久久久久网曝门| 丝袜美腿美女被狂躁在线观看| 欧美综合在线播放| 国产丝袜一区二区三区| 无码人妻熟妇av又粗又大| 九九热精品免费视频| 51成人做爰www免费看网站| 性生交大片免费看女人按摩| 天堂久久久久va久久久久| 日韩脚交footjobhd| 国产日韩视频| 成人在线视频区| 欧美日韩一区二区在线免费观看| 九九热这里有精品视频| 亚洲欧美综合网| 天堂中文字幕在线| 欧美一区二区三区四区在线观看| 国产成人精品一区二区无码呦| 日韩欧美中文字幕制服| 精品第一国产综合精品aⅴ| 国产精品亚洲片在线播放| 成年人网站av| 日本人添下边视频免费| 亚洲精品乱码久久久久久9色| 一区二区三区免费视频播放器| 亚洲日本韩国在线| 国产盗摄一区二区| 成年人免费看的视频| 国产一级av毛片| 中文字幕日韩视频| 国产在视频线精品视频www666| 一区二区三区波多野结衣在线观看| 久久久久久69| 国产精品免费看一区二区三区| 久久久精品人妻一区二区三区四| 天天操天天干视频| 亚洲乱码国产乱码精品精| 神马午夜一区二区| 欧美一区国产一区| 中文字幕av手机在线| 丰满少妇在线观看资源站| 18以下岁禁止1000部免费| 亚洲欧洲自拍偷拍| 伊人成人222| 成av人片在线观看www| 日日日日人人人夜夜夜2017| 麻豆九一精品爱看视频在线观看免费| 欧美午夜免费| 国产成人一区二区在线| 欧美日韩国产大片| 男人的天堂免费在线视频| 成人av资源网址| 蜜桃视频在线网站| 亚洲精品成人区在线观看| mm1313亚洲国产精品无码试看| 精品在线播放视频| 一区不卡在线观看| 国模大尺度视频一区二区| 欧美日韩视频在线一区二区| 欧美性黄网官网| 精品在线手机视频| 国产高清精品二区| 亚洲高清在线免费观看| 日韩三级在线观看| 欧美一区二区三区免费看| 久草手机在线视频| 国精产品一区二区| 久蕉依人在线视频| 欧美日韩精品区| 久久婷婷人人澡人人喊人人爽| 欧美主播一区二区三区美女| 色欲av无码一区二区三区| 黄色网址在线免费看| 国产精品美女无圣光视频| 久久视频中文字幕| av第一福利在线导航| 国产美女明星三级做爰| 欧美白人最猛性xxxxx| 一区二区乱码| 亚洲va国产天堂va久久en| 亚洲.欧美.日本.国产综合在线| 青娱乐精品在线| 久久综合久久久| 中国特级黄色大片| 欧美群妇大交群中文字幕| 日日干日日操| 国产精品一国产精品最新章节| 国产亚洲无码精品| www成人免费观看| 性欧美暴力猛交另类hd| 久久在精品线影院精品国产| 麻豆av资源| 国产精品永久入口久久久| 国产乱码精品一区二三区蜜臂| 久久精品黄色片| 亚洲成人影院在线观看| 亚洲欧美综合自拍| 亚洲成人va| 亚洲国产一区二区视频| 免费看美剧网站| www.av麻豆| 一区在线不卡| 在线观看一区二区精品视频| 99视频在线观看一区三区| 免费视频观看成人| 黑人一级大毛片| 亚洲不卡系列| 黄色在线免费观看网站| 欧美伦理影院| 日韩精品福利网站| 超碰在线免费看| 亚洲国产日韩欧美在线99| 日韩av影片在线观看| 超级碰碰久久| 亚洲愉拍自拍另类高清精品| 欧美黑人欧美精品刺激| 浮生影视网在线观看免费| 天天成人综合网| 国产成人精品一区二区三区网站观看| 日本一区二区三区在线观看视频| 亚洲欧美自偷自拍另类| 日韩区国产区| 亚洲级视频在线观看免费1级| 免费av成人在线| 精品伊人久久久久7777人| 色欧美88888久久久久久影院| 亚洲国产精品成人久久综合一区| 亚洲一区不卡在线| 天天射天天操天天干| www.555国产精品免费| 天堂俺去俺来也www久久婷婷| 在线播放91灌醉迷j高跟美女| 伊人久久大香线| 欧美黄色性视频| 91av中文字幕| 美女视频黄免费的久久| 国内精品久久久久久久久电影网| 国产精品一区二区在线免费观看| 日韩精品卡一| 国产一区二区三区视频免费观看| 久久综合九色综合网站| 亚洲成人你懂的| 婷婷综合伊人| 在线视频91p| 国产精品久久精品牛牛影视| 亚洲电影天堂av| 2020中文字字幕在线不卡| 性欧美暴力猛交69hd| 久久久精品99| 亚洲小说区图片区情欲小说| 国内精品伊人久久久久影院对白| 亚洲二区在线视频| 青青热久免费精品视频在线18| 国产高清精品一区| 日本精品久久久久影院| 丰满女人性猛交| 99亚洲视频| 国产欧美一区二区三区网站| 天天伊人狠狠| 一道本无吗dⅴd在线播放一区| 香蕉视频国产精品| 亚洲精品91在线| 国产伦理精品| 在线播放毛片| 国产综合精品| 国产97在线|亚洲| 成人毛片免费看| 日本电影中文字幕| 久久精品主播| 国产av不卡一区二区| 在哪里可以看毛片| 伊人手机在线视频| 久久99久久久久久久噜噜| 中文字幕免费不卡在线| japansex久久高清精品| 337p日本欧洲亚洲大胆精品| 欧美日韩性生活| 日韩一级在线播放| 亚州av电影免费在线观看| 超清av在线|