이 질문에 대답하려면 LoaderManager
코드 를 파헤쳐 야 합니다. LoaderManager 자체에 대한 문서가 명확하지 않거나 (이 질문이 없을 수도 있음) 추상 LoaderManager의 하위 클래스 인 LoaderManagerImpl에 대한 문서가 훨씬 더 밝아졌습니다.
initLoader
로더로 특정 ID를 초기화하려면 호출하십시오. 이 ID에 이미 연결된 로더가있는 경우 변경되지 않은 상태로 유지되며 이전 콜백은 새로 제공된 콜백으로 바뀝니다. 현재 ID에 대한 로더가없는 경우 새 로더가 작성되어 시작됩니다.
이 기능은 일반적으로 구성 요소를 초기화 할 때이를 사용하여 해당 구성 요소에 의존하는 로더가 작성되도록해야합니다. 이를 통해 기존 로더 데이터가 이미있는 경우이를 재사용 할 수 있습니다. 예를 들어 구성 변경 후 활동을 다시 작성할 때 로더를 다시 작성할 필요가 없습니다.
restartLoader
특정 ID와 연관된 로더를 다시 작성하기 위해 호출하십시오. 현재이 ID와 연관된 로더가 있으면 적절하게 취소 / 중지 / 파기됩니다. 주어진 인수를 가진 새로운 로더가 생성되고 사용 가능한 데이터가 전달됩니다.
[...]이 함수를 호출하면이 ID와 관련된 이전 로더는 유효하지 않은 것으로 간주되며 더 이상 데이터 업데이트를받지 않습니다.
기본적으로 두 가지 경우가 있습니다.
- ID가있는 로더가 존재하지 않습니다. 두 방법 모두 새 로더를 생성하므로 차이가 없습니다.
- ID가있는 로더가 이미 존재합니다 :
initLoader
매개 변수로 전달 된 콜백 만 교체하지만 로더를 취소하거나 중지하지는 않습니다. 의 경우 CursorLoader
커서가 열려 있고 활성 상태를 유지함을 의미합니다 ( initLoader
통화 이전의 경우 ). 반면에, restartLoader는 로더를 취소, 중지 및 파괴하고 (커서와 같은 기본 데이터 소스를 닫음) 새 로더를 생성합니다 (로더가 다음과 같은 경우 새 커서를 생성하고 쿼리를 다시 실행 함) CursorLoader).
다음은 두 방법 모두에 대한 간단한 코드입니다.
initLoader
LoaderInfo info = mLoaders.get(id);
if (info == null) {
// Loader doesn't already exist -> create new one
info = createAndInstallLoader(id, args, LoaderManager.LoaderCallbacks<Object>)callback);
} else {
// Loader exists -> only replace callbacks
info.mCallbacks = (LoaderManager.LoaderCallbacks<Object>)callback;
}
restartLoader
LoaderInfo info = mLoaders.get(id);
if (info != null) {
LoaderInfo inactive = mInactiveLoaders.get(id);
if (inactive != null) {
// does a lot of stuff to deal with already inactive loaders
} else {
// Keep track of the previous instance of this loader so we can destroy
// it when the new one completes.
info.mLoader.abandon();
mInactiveLoaders.put(id, info);
}
}
info = createAndInstallLoader(id, args, (LoaderManager.LoaderCallbacks<Object>)callback);
로더가 존재하지 않는 경우 (info == null) 두 메소드 모두 새 로더를 작성합니다 (info = createAndInstallLoader (...)). 로더가 이미 존재 initLoader
하는 restartLoader
경우 기존 로더 를 비활성화하는 동안 콜백 (info.mCallbacks = ...) 만 교체하고 ( 새 로더가 작업을 완료하면 소멸됨) 새 로더를 작성합니다.
따라서 언제 사용 initLoader
하고 언제 사용 restartLoader
해야하는지, 그리고 두 가지 방법을 갖는 것이 합리적이라고 분명해졌습니다 .
initLoader
초기화 된 로더가 있는지 확인하는 데 사용됩니다. 존재하지 않는 경우 새로운 것이 생성되고 이미 존재하는 경우 재사용됩니다. 실행할 쿼리가 변경 되었기 때문에 (기본 데이터가 아니라 CursorLoader의 SQL 문과 같은 실제 쿼리) 새 로더가 필요한 경우가 아니면 항상이 메소드를 사용합니다.이 경우을 호출 restartLoader
합니다.
활동 / 조각 라이프 사이클은 하나를 사용하기로 결정 또는 다른 방법을 함께 할 수 없다 (그리고 사이먼이 제안 원샷 플래그를 사용하여 전화를 추적 할 필요가 없습니다)! 이 결정은 전적으로 새로운 로더에 대한 "필요"에 기초하여 이루어집니다. 우리가 사용하는 동일한 쿼리를 실행하려면 initLoader
다른 쿼리를 실행하려면 사용 restartLoader
합니다.
우리는 항상 사용할 수 restartLoader
있지만 비효율적입니다. 화면 회전 후 또는 사용자가 앱에서 다른 곳으로 이동 한 후 나중에 동일한 활동으로 돌아 가면 일반적으로 동일한 쿼리 결과를 표시하려고하므로 restartLoader
불필요하게 로더를 다시 만들고 기본 (잠재적으로 비싼) 쿼리 결과를 무시합니다.
로드 된 데이터와 해당 데이터를로드하는 "쿼리"의 차이점을 이해하는 것이 매우 중요합니다. 주문을 위해 테이블을 쿼리하는 CursorLoader를 사용한다고 가정 해 봅시다. 새 주문이 해당 테이블에 추가되면 CursorLoader는 onContentChanged ()를 사용하여 UI에 새 주문을 업데이트하고 표시하도록 지시합니다 ( restartLoader
이 경우에는 사용할 필요 없음 ). 미결 주문 만 표시하려면 새 쿼리가 필요하며 새 쿼리를 restartLoader
반영하는 새 CursorLoader를 반환하는 데 사용 합니다.
두 방법 사이에 어떤 관계가 있습니까?
코드를 공유하여 새 로더를 작성하지만 로더가 이미 존재하면 다른 작업을 수행합니다.
전화는 restartLoader
항상 전화 initLoader
합니까?
아닙니다.
전화 restartLoader
하지 않고 전화 를 걸 수 initLoader
있습니까?
예.
initLoader
데이터를 새로 고치기 위해 두 번 전화하는 것이 안전 합니까?
initLoader
두 번 호출해도 안전 하지만 데이터가 새로 고쳐지지 않습니다.
때 나는 두 가지 중 하나를 사용한다 왜 ?
위의 설명 후에 (희망적으로) 분명해야합니다.
구성 변경
LoaderManager는 구성 변경 (방향 변경 포함)에서 상태를 유지하므로 우리가 할 일이 없다고 생각할 것입니다. 다시 생각 해봐...
우선, LoaderManager는 콜백을 유지하지 않으므로 아무 것도 수행하지 않으면 콜백 메소드 등의 호출을 수신하지 않으므로 onLoadFinished()
앱이 손상 될 가능성이 큽니다.
따라서 initLoader
콜백 메소드를 복원하기 위해 최소한 호출해야합니다 ( restartLoader
물론 가능합니다). 설명서 에는 다음 이 명시되어 있습니다.
호출 시점에 호출자가 시작 상태에 있고 요청 된 로더가 이미 존재하고 데이터를 생성 한 경우 콜백 onLoadFinished(Loader, D)
이 즉시이 함수 내에서 호출 됩니다 ([...]).
즉 initLoader
, 방향 변경 후 onLoadFinished
전화를 걸면 데이터가 이미로드되어 있기 때문에 즉시 전화를받을 수 있습니다 (변경 전의 경우라고 가정). 그 소리는 곧 들리지만 까다로울 수 있습니다 (우리 모두 Android를 좋아하지는 않습니다 ...).
우리는 두 가지 경우를 구별해야합니다.
- 구성 변경 자체를 처리합니다. setRetainInstance (true)를 사용하는 프래그먼트 또는
android:configChanges
매니페스트에서 관련 태그가 있는 활동의 경우입니다 . 이러한 구성 요소는 화면 회전과 같은 onCreate 호출을받지 않으므로 initLoader/restartLoader
다른 콜백 메서드 (예 : in
onActivityCreated(Bundle)
) 를 호출해야합니다
. 로더를 초기화하려면 로더 ID를 저장해야합니다 (예 : 목록). 구성 요소가 구성 변경 사항에 걸쳐 유지되므로 기존 로더 ID를 반복하고 호출 할 수 initLoader(loaderid,
...)
있습니다.
- 구성 변경 자체를 처리하지 않습니다.이 경우 onCreate에서 로더를 초기화 할 수 있지만 로더 ID를 수동으로 유지해야하거나 필요한 initLoader / restartLoader 호출을 수행 할 수 없습니다. id가 ArrayList에 저장되어 있으면 initLoader 호출을 수행하기 전에
outState.putIntegerArrayList(loaderIdsKey, loaderIdsArray)
onSaveInstanceState에서 수행하고 onCreate :에서 ID를 복원합니다
loaderIdsArray =
savedInstanceState.getIntegerArrayList(loaderIdsKey)
.
initLoader
회전 후 사용하고 (모든 콜백이 완료되고 로더가 유휴 상태 인 경우)onLoadFinished
콜백을받지 않지만 사용restartLoader
하면 사용 합니까?