Android App 디 버 깅 메모리 유출 Cursor 편

최근 에 업무 중 에 메모리 유출 문 제 를 처 리 했 습 니 다.이 과정 에서 저 는 특히 기본 적 인 문 제 를 발 견 했 습 니 다.예 를 들 어 정적 변수,cursor 종료,스 레 드,타이머,반 등록,bitmap 등 을 무시 하고 메모리 유출 을 초래 하 는 것 을 발 견 했 습 니 다.물론 이런 문 제 는 이렇게 말 하면 비교적 추상 적 입 니 다.그 다음 에 저 는 문제 에 따라일부 인 스 턴 스 코드 를 붙 이 고 한 걸음 한 걸음 분석 하 며 구체 적 인 장면 에서 효과 적 인 방법 으로 유출 의 근본 원인 을 찾 아 해결 방안 을 제시한다.지금 은 cursor 가 닫 히 는 문제 부터 시작 합 니 다.누구나 cursor 가 닫 혀 야 한 다 는 것 을 알 고 있 지만 반대로 사람들 은 닫 는 것 을 잊 어 버 립 니 다.왜냐하면 진정한 응용 장면 은 이상 적 인 간단 함 이 아 닐 수 있 기 때 문 입 니 다.1.이상 적 인 cursor 종료
 
// Sample Code
Cursor cursor = db.query();
List<String> list = convertToList(cursor);
cursor.close();
이것 은 가장 간단 한 cursor 사용 장면 입 니 다.만약 에 이곳 의 cursor 가 닫 히 지 않 았 다 면 수많은 침 과 욕 을 불 러 일 으 킬 수 있 었 을 것 이 라 고 생각 합 니 다.그러나 실제 장면 은 그렇지 않 을 수 있 습 니 다.이곳 의 cursor 는 닫 히 지 않 을 수도 있 습 니 다.적어도 다음 과 같은 두 가지 가능성 이 있 습 니 다.2.Cursor 가 닫 히 지 않 았 을 가능성(1).cursor.close()이전에 이상 이 발생 했 습 니 다.(2).cursor 는 계속 사용 해 야 합 니 다.바로 닫 을 수 없습니다.뒤에서 닫 는 것 을 잊 었 습 니 다.3.Cursor.close()이전에 이상 이 발생 한 것 은 쉽게 이해 할 수 있 고 초보 자 들 이 처음에 겪 는 흔 한 문제 일 것 이다.예 를 들 어 다음 과 같다.
 
try {
Cursor c = queryCursor();
int a = c.getInt(1);
......
// , cursor.close()
......
c.close();
} catch (Exception e) {
}
정확 한 표기 법 은 다음 과 같다.
 
Cursor c;
try {
c = queryCursor();
int a = c.getInt(1);
......
// , cursor.close()
//c.close();
} catch (Exception e) {
} finally{
if (c != null) {
c.close();
}
} 
간단 하지만 항상 명심 해 야 한다.4.Cursor 는 계속 사용 해 야 합 니 다.바로 닫 을 수 없습니다.이런 경우 가 있 습 니까?어 떡 하지?정 답 은 있 습 니 다.Cursor Adapter 는 전형 적 인 예 입 니 다.Cursor Adapter 예 는 다음 과 같 습 니 다.
 
mCursor = getContentResolver().query(CONTENT_URI, PROJECTION,
null, null, null);
mAdapter = new MyCursorAdapter(this, R.layout.list_item, mCursor);
setListAdapter(mAdapter);
// mCursor.close(),
// list
5.이러한 Cursor 는 언제 닫 아야 합 니까?이것 은 대답 을 잘 할 수도 있 고 대답 하기 어 려 울 수도 있 는 질문 입 니 다.바로 Cursor 가 더 이상 사용 하지 않 을 때 닫 는 것 입 니 다.예 를 들 어 위의 조 회 는 들 어가 거나 resume 에 들 어 갈 때마다 다시 조회 하여 실 행 됩 니 다.일반적으로 이런 수요 이기 도 합 니 다.제 가 화면 을 볼 수 없 을 때 계속 조회 결 과 를 표시 하 는 경 우 는 드 물 죠.만약 에 정말 있 으 면 토론 하지 않 고 최종 적 으로 끄 면 OK 입 니 다.이 럴 때 우 리 는 보통 onStop()방법 에서 cursor 를 끌 수 있다.4567913)저 는 Cursor Adapter 의 change Cursor()방법 소스 코드 를 전문 적 으로 동봉 하여 여러분 에 게 더욱 분명하게 보 여 드 리 겠 습 니 다.불안 하지 않도록 change Cursor(null)방법:
 
@Override
protected void onStop() {
super.onStop();
// mCursorAdapter cursor, cursor
mCursorAdapter.changeCursor(null);
}
6.실전 AsyncQuery Handler 에서 Cursor 의 닫 기 문제 AsyncQuery Handler 는 전형 적 인 분석 Cursor 의 예 입 니 다.피 를 볼 수 있 을 뿐만 아니 라 하 나 를 보면 열 을 수 있 을 뿐만 아니 라 매우 흔 합 니 다.나중에 피하 기 위해 서.AsyncQueryHandler 문서 참조 주소:http://developer.android.com/reference/android/content/AsyncQueryHandler.html 다음 코드 는 Android 2.3 시스템 에서 Mms 정보 홈 페이지 의 ConversationList 소스 코드 의 일부분 입 니 다.Cursor 가 정확하게 닫 혔 는 지 보 시 겠 습 니까?4567913)
 
/**
* 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;
}
문제 가 있다 고 생각 하 십 니까?주로 두 가지:(1).THREADLIST_QUERY_TOKEN 분기 의 Cursor 가 정확하게 닫 혔 나 요?(2). HAVE_LOCKED_MESSAGES_TOKEN 분기 의 Cursor 가 정확하게 닫 혔 나 요?앞의 조목조목 분석 에 따 르 면 답 은(1).THREAD 이다.LIST_QUERY_TOKEN 분기 의 Cursor 는 mListAdapter 에 전달 되 었 으 며,mListAdapter 는 onStop 에서 changeCursor(null)를 사용 합 니 다.사용자 가 현재 Activity 를 떠 날 때 이 Cursor 는 정확하게 방출 되 어 누설 되 지 않 습 니 다.(2). HAVE_LOCKED_MESSAGES_TOKEN 분기 의 Cursor(즉,매개 변수 cursor)는 하나의 판단 의 조건 으로 사용 되 고 사용 되 지 않 지만 꺼 지지 않 아 cursor 가 유출 되 었 습 니 다.StrictMode 감시 하에 이 곳 에 도착 하면 이 오류 가 발생 합 니 다.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...................................................................................실제로 인터넷 에 서 는 AsyncQuery Handler 를 많이 사용 합 니 다.예 를 들 어 이 실 수 를 저 질 렀 습 니 다.이 글 을 본 후에 다 시 는 AsyncQuery Handler 의 cursor 가 유출 되 는 것 을 두려워 하지 않 습 니 다.그리고 현재 사용 하고 있 는 배경 strictmode 의 cursor not close 이상 문 제 를 해결 할 수 있 을 지도 모른다 고 합 니 다.7.소결 은 아직 cursor 가 닫 히 지 않 은 경우 가 많다 고 생각 하지만 근본 적 인 문 제 는 바로 cursor 를 정확하게 닫 는 것 입 니 다.메모리 유출 cursor 편 은 제 업무 경험 상의 정리 입 니 다.잘 훑 어 본 후에 저 자신 이 모두 에 게 도움 이 되 고 복잡 한 문 제 를 본질 화하 고 단순화 시 킵 니 다!

좋은 웹페이지 즐겨찾기