source

Loader Manager에서의 initLoader와 restartLoader의 차이점

nicesource 2022. 11. 24. 20:41
반응형

Loader Manager에서의 initLoader와 restartLoader의 차이점

두 는 전혀 initLoaderrestartLoaderLoaderManager:

  • 둘 다 같은 서명이 있어요.
  • restartLoader또한 로더가 없는 경우 로더를 작성합니다("이 매니저에서 새 로더를 시작하거나 기존 로더를 재시작합니다.").

두방 사사 사이 떤? ?? ?? ??을 하는 건가요?restartLoader 부르다initLoaderrestartLoader할 필요 initLoader ?????? ? ? ? ? ?로 initLoader데이터를 새로 고치는 데 두 번? 중 하나를 언제, 왜 사용해야 하나요?

, 「이것」에 대해 .LoaderManager코드. LoaderManager에 대한 문서 자체는 명확하지 않지만(혹은 이 질문이 없을 수도 있습니다), 추상 LoaderManager의 하위 클래스인 LoaderManagerImpl에 대한 문서는 훨씬 더 유익합니다.

initLoader

로더를 사용하여 특정 ID를 초기화하기 위한 호출입니다.이 ID에 이미 Loader가 관련되어 있는 경우 변경되지 않고 이전 콜백은 새로 제공된 콜백으로 대체됩니다.현재 ID에 대한 Loader가 없는 경우 새 ID가 생성되어 시작됩니다.

이 기능은 일반적으로 컴포넌트가 의존하는 로더가 생성되도록 하기 위해 컴포넌트가 초기화될 때 사용해야 합니다.이렇게 하면 기존 Loader의 데이터가 이미 있는 경우 기존 Loader의 데이터를 재사용할 수 있으므로 구성 변경 후 액티비티를 다시 작성할 필요가 없습니다.

재기동 로더

특정 ID와 연관된 로더를 다시 만들기 위한 호출입니다.현재 이 ID와 연결된 로더가 있는 경우 해당 로더는 적절히 취소/중지/파괴됩니다.지정된 인수를 사용하여 새 로더가 생성되고 해당 데이터가 사용자에게 전달됩니다.

[...] 이 함수를 호출하면 이 ID와 관련된 이전 로더는 유효하지 않은 것으로 간주되며 로더로부터 더 이상의 데이터 업데이트를 받지 않습니다.

기본적으로 다음 두 가지 경우가 있습니다.

  1. ID를 가진 로더가 존재하지 않습니다. 두 방법 모두 새 로더를 생성하므로 차이가 없습니다.
  2. 아이디입니다.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;
}

재기동 로더

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(...)」, 「Info=createAndInstallLoader(...)」.가 이미 initLoader는 mCallbacks = ...인 동안 info.displaces(info))만 합니다(infodisplaces.=restartLoader는 오래된 로더를 비활성화한 후(새 로더가 작업을 완료하면 파기됩니다) 새 로더를 만듭니다.

해서 '언제 ', '언제 쓸지'가해졌습니다.initLoader 사용 restartLoader두 가지 방법을 사용하는 것이 왜 말이 되는지에 대한 질문입니다. initLoader초기화된 로더가 있는지 확인하는 데 사용됩니다.존재하지 않는 경우 새 항목이 생성되고, 이미 존재하는 경우 다시 사용됩니다.) 한 이 이 CursorLoader SQL을 합니다.restartLoader.

액티비티/프래그먼트 라이프 사이클은 어느 하나의 방법을 사용하는지 결정하는 것과는 관계가 없습니다(Simon이 제안한 것처럼 원샷 플래그를 사용하여 콜을 추적할 필요는 없습니다).이 결정은 새 로더의 "필요성"에 따라 이루어집니다.사용하는 것과 동일한 쿼리를 실행하는 경우initLoader는 ''를 사용합니다restartLoader.

우리는 항상 사용할 수 있다restartLoader 쿼리 때문에 화면 회전 후 또는 사용자가 동일한 액티비티로 돌아가면 해당 액티비티로 돌아갑니다.restartLoader로더를 불필요하게 재작성하고 기본 쿼리 결과(매우 비싼)를 무시합니다.

로드되는 데이터와 해당 데이터를 로드하기 위한 "쿼리"의 차이를 이해하는 것이 매우 중요합니다.예를 들어 CursorLoader를 사용하여 테이블에 주문을 문의한다고 가정합니다.이 및 합니다('CursorLoader' onContentChanged() UI를 .restartLoader(어느 쪽인가)하며, 이 를 사용합니다.restartLoader커서 로더로더로더로더로더로더로더로더로더로더로더로더로더.


두 방법 사이에 어떤 관계가 있습니까?

새 로더를 만들기 위한 코드를 공유하지만 로더가 이미 있는 경우 다른 작업을 수행합니다.

을 하는 건가요?restartLoader 부르다initLoader

아니, 그렇지 않아.

★★★★★★★★★★★★★★★★★★★★에 전화해도 될까요?restartLoader할 필요 initLoader

네.

합니까?initLoader이터를 로로 치번 번?

it it 화 it it it it it it it it 。initLoader이치노

중 하나를 언제, 왜 사용해야 하나요?


위에서 설명한 후에 그것이 명확해질 것이다.

설정 변경

Loader Manager는 구성 변경(방향 변경 포함) 후에도 상태를 유지하므로 더 이상 우리가 할 일이 없다고 생각할 수 있습니다.다시 생각해봐...

하지 않기 않으면 Loader Manager 등의 onLoadFinished()그런 것들이 당신의 앱을 망가뜨릴 가능성이 높습니다.

때문에 합니다.initLoader하다)restartLoader물론 가능합니다.)문서에는 다음과 같이 기술되어 있습니다.

되어 있는 은 " " " 입니다.onLoadFinished(Loader, D)함수의 ) 이 함수의 종료) [...]가 즉시 됩니다.

, 을 하면initLoader '어디로 가든지', '어디로 가든지' .onLoadFinished데이터가 이미 로드되어 있기 때문에(변경전의 경우) 즉시 콜합니다.단도직입적으로 들리지만 까다로울 수 있습니다(우리 모두 안드로이드를 좋아하지 않나요?

다음 두 가지 경우를 구분해야 합니다.

  1. 변경를 처리합니다.입니다.setRetainInstance(true) 프래그먼트.android:configChanges onCreate 콜을 호출하는 것을 .initLoader/restartLoader예: 른른 ( ( ( ( (예예예예예예:onActivityCreated(Bundle)로더를 초기화하려면 로더 ID를 저장해야 합니다(예: 목록). 변경 ID를 호출할 수 .initLoader(loaderid, ...).
  2. 구성 변경 자체를 처리하지 않습니다.이 경우 로더를 onCreate에서 초기화할 수 있지만 로더 ID를 수동으로 유지해야 합니다.initLoader/restartLoader를 사용합니다.ID Array List id id id 。
    outState.putIntegerArrayList(loaderIdsKey, loaderIdsArray)를 onCreate: onSaveInstanceState ID onCreate로 합니다.loaderIdsArray = savedInstanceState.getIntegerArrayList(loaderIdsKey)하기 전에 initLoader를 합니다.

" "initLoader되어 있는 후 를 Loader에 합니다.onLoadFinished 되어 있지 를 들어 최초로 했을 때 등), 로더에 대한 콜은 「」(「」/「」)됩니다.initLoader에게 LoaderManager를 합니다.onCreateLoader을 사용하다

" "restartLoader는 이미 하는 Loader그와 데이터)를에 "Loader"("Loader Manager")를하도록 지시합니다.onCreateLoader새 로더를 만들고 새 로드를 시작합니다.


이 문서에서는 이에 대해서도 매우 명확하게 설명하고 있습니다.

  • initLoader로더가 초기화되고 활성화되도록 합니다.로더가 아직 존재하지 않으면 로더가 생성되고 (활동/조각이 현재 시작된 경우) 로더가 시작됩니다.그렇지 않으면 마지막으로 생성된 로더가 재사용됩니다.

  • restartLoader는 이 매니저에서 새로운 Loader를 시작하거나 기존 Loader를 재시작하여 콜백을 등록하고 (액티비티/플래그먼트가 현재 시작된 경우) 로드를 시작합니다.동일한 ID의 로더가 이전에 시작된 경우 새 로더가 작업을 완료하면 해당 로더는 자동으로 삭제됩니다.콜백은 오래된 로더가 파기되기 전에 전달됩니다.

최근 여러 로더 매니저와 화면 방향 변경에 문제가 발생했습니다.많은 시행착오를 겪은 후 다음 패턴이 액티비티와 프래그먼트 모두에서 작동한다고 말하고 싶습니다.

onCreate: call initLoader(s)
          set a one-shot flag
onResume: call restartLoader (or later, as applicable) if the one-shot is not set.
          unset the one-shot in either case.

(즉, initLoader가 항상 한 번 실행되고 restartLoader가 두 번째에 실행되며 이후가 onResume를 통과하도록 플래그를 설정합니다).

또한 액티비티 내의 각 로더에 다른 ID를 할당하는 것도 잊지 마십시오(번호부여에 주의하지 않으면 액티비티 내의 fragment에 문제가 될 수 있습니다).


initLoader만 사용해보니 효과가 없는 것 같습니다.

initLoader on Create를 null args(docs는 이것이 OK라고 말합니다) 및 onResume ......docs의 restartLoader(유효한 args)가 잘못되어 initLoader가 null pointer 예외를 발생시킵니다.

restart Loader만... 잠시 작동하지만 5번째 또는 6번째 화면 방향 전환이 중단됩니다.

onResume에서 initLoader를 시도했다.다시 잠시 작동하다가 끊어졌다.(구체적으로 "시작되지 않은 경우 호출된 doRetain:")에러)

다음 시도: (로더 ID가 컨스트럭터에 전달된 커버 클래스에서 제외)

/**
 * start or restart the loader (why bother with 2 separate functions ?) (now I know why)
 * 
 * @param manager
 * @param args
 * @deprecated use {@link #restart(LoaderManager, Bundle)} in onResume (as appropriate) and {@link #initialise(LoaderManager, Bundle)} in onCreate 
 */
@Deprecated 
public void start(LoaderManager manager, Bundle args) {
    if (manager.getLoader(this.id) == null) {
        manager.initLoader(this.id, args, this);
    } else {
        manager.restartLoader(this.id, args, this);
    }
}

(스택 오버플로우 어딘가에서 발견)

이 역시 잠시 작동했지만 여전히 가끔 문제가 발생했습니다.


디버깅 중에 알게 된 바로는 사이클의 스핀에서 살아남으려면 initLoader(/s)가 라이프 사이클의 onCreate 부분에서 실행되어야 하는 저장/복원 인스턴스 상태와 관련이 있다고 생각합니다.(잘못했을 수도 있습니다)

다른 매니저 또는 태스크에서 결과가 반환될 때까지 시작할 수 없는 매니저(onCreate에서 초기화할 수 없는 매니저)의 경우 initLoader만 사용합니다.(정답은 아닐지 모르지만 효과가 있는 것 같습니다.이러한 세컨더리 로더는 즉시 인스턴스 스테이트의 일부가 아니기 때문에 initLoader를 사용하는 것이 실제로 올바른 경우가 있습니다.)

라이프 사이클


그림이나 문서를 보면 initLoader가 onRestart의 onCreate & restartLoader를 액티비티용으로 사용해야 한다고 생각했지만 fragments는 다른 패턴을 사용하고 있기 때문에 이것이 실제로 안정적인지 조사할 시간이 없었습니다.이러한 활동 패턴으로 성공했는지에 대해 다른 사람이 코멘트할 수 있습니까?

첫 번째 시작 시 init loader는 loadInBackground() 메서드를 사용합니다.두 번째 시작 시 loader는 생략됩니다.그래서, 제 의견으로는, 더 나은 해결책은 다음과 같습니다.

Loader<?> loa; 
try {
    loa = getLoaderManager().getLoader(0);
} catch (Exception e) {
    loa = null;
}
if (loa == null) {
    getLoaderManager().initLoader(0, null, this);
} else {
    loa.forceLoad();
}

///////////////////////////////////////////////////////////////////////////

protected SimpleCursorAdapter mAdapter;

private abstract class SimpleCursorAdapterLoader 
    extends AsyncTaskLoader <Cursor> {

    public SimpleCursorAdapterLoader(Context context) {
        super(context);
    }

    @Override
    protected void onStartLoading() {
        if (takeContentChanged() || mAdapter.isEmpty()) {
            forceLoad();
        }
    }

    @Override
    protected void onStopLoading() {
        cancelLoad();
    }

    @Override
    protected void onReset() {
        super.onReset();
        onStopLoading();
    }
}

저는 이 솔루션을 찾기 위해 많은 시간을 소비했습니다.재시작 로더(...)는 제 경우 제대로 작동하지 않았습니다.유일한 forceLoad()는 콜백 없이 이전 스레드 로드를 종료하고(모든 DB 트랜잭션을 정상적으로 종료합니다), 새로운 스레드를 다시 시작합니다.네, 시간이 좀 더 걸리긴 하지만 좀 더 안정적이에요.마지막으로 시작된 스레드만 콜백을 받습니다.따라서 db 트랜잭션을 중단하는 테스트를 수행할 경우(환영합니다), restartLoader(...), 그렇지 않으면 forceLoad()를 시도합니다.restartLoader(...)의 유일한 편리성은 새로운 초기 데이터, 즉 파라미터를 전송하는 것입니다.그리고 이 경우 적절한 Fragment의 onDetach() 메서드로 로더를 파괴하는 것을 잊지 마십시오.또한 액티비티가 있는 경우, Loader를 사용한 2개의 fragment가 각각 포함된 액티비티라고 하면 2개의 Loader Manager에만 도달하기 때문에 Activity는 로드 중에 가장 먼저 화면에 표시되는 Fragment와 Loader Manager를 공유합니다.LoaderManager.enableDebugLogging(true)을 사용하여 각각의 경우에 대한 자세한 내용을 확인하십시오.

initLoader로더가 이미 존재하는 경우 에서는 동일한 파라미터가 재사용됩니다.새 매개 변수를 사용하여 호출하더라도 이전 데이터가 이미 로드된 경우 즉시 반환됩니다.로더는 이상적으로 새로운 데이터의 액티비티를 자동으로 통지해야 합니다.회전했을 경우는, 「 」initLoader다시 호출되고 이전 데이터가 즉시 표시됩니다.

restartLoader강제로 새로고침하고 파라미터도 변경하는 경우입니다.사용하여 로더로 할 수 있는 은 로더뿐입니다.restartLoader될 수 (잘못된 자격 증명 등으로 인해 버튼을 여러 번 클릭할 수 있습니다).는 ""에 안 해""initLoader로그인 진행 중에 화면이 회전된 경우 액티비티의 저장된 인스턴스 상태를 복원할 때 사용합니다.

로더가 이미 존재하는 경우 restartLoader는 오래된 로더를 중지/취소/파괴하고 initLoader는 지정된 콜백으로 초기화합니다.이 경우 오래된 콜백이 어떤 역할을 하는지 알 수 없지만 그냥 버려질 것입니다.

http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.0.1_r1/android/app/LoaderManager.java을 통해 스캔을 해봤지만, 두 가지 방법이 다른 것 외에는 정확한 차이를 알 수 없습니다.따라서 처음에 initLoader를 사용하여 다음 시간 동안 재시작하도록 하겠습니다.다만, 각각이 정확히 무엇을 할지는 확실히 말할 수 없습니다.

언급URL : https://stackoverflow.com/questions/14445070/difference-between-initloader-and-restartloader-in-loadermanager

반응형