문제

로더에 특별한 문제가 있습니다.현재 이것이 내 코드의 버그인지 아니면 로더를 잘못 이해하고 있는지 확실하지 않습니다.

문제는 대화에서 발생합니다(Whatsapp과 비슷한 것을 상상해 보세요).내가 사용하는 로더는 다음을 기반으로 구현됩니다. AsyncTaskLoader 예.지원 라이브러리를 사용하고 있습니다.

  • OnCreate에서는 캐시된 메시지를 검색하기 위해 로더를 시작합니다.
  • CachedMessageLoader가 완료되면 RefreshLoader를 시작하여 최신 메시지를 (온라인으로) 검색합니다.
  • 고유한 ID로 각 로더 유형(예: 오프라인:1 온라인:2)

이것은 다음 예외를 제외하고 매우 잘 작동합니다.

문제

다른 조각을 열고(트랜잭션을 백스택에 추가한 다음) Back-Key를 사용하여 대화Fragment로 돌아가면, onLoadFinished 다음과 같이 다시 호출됩니다. 둘 다 이전의 결과입니다.이 호출은 프래그먼트가 로더를 다시 시작할 기회를 갖기 전에 발생합니다.

이전에 얻은 "오래된" 결과를 전달하면 메시지가 중복됩니다.

질문

  • 왜 그 결과가 다시 전달되나요?
  • 이 로더를 잘못 사용하고 있습니까?
  • 결과가 한 번만 전달되도록 결과를 "무효화"할 수 있습니까? 아니면 중복된 결과를 직접 제거해야 합니까?

호출의 스택 추적

MyFragment.onLoadFinished(Loader, Result) line: 369 
MyFragment.onLoadFinished(Loader, Object) line: 1   
LoaderManagerImpl$LoaderInfo.callOnLoadFinished(Loader, Object) line: 427   
LoaderManagerImpl$LoaderInfo.reportStart() line: 307    
LoaderManagerImpl.doReportStart() line: 768 
MyFragment(Fragment).performStart() line: 1511  
FragmentManagerImpl.moveToState(Fragment, int, int, int, boolean) line: 957 
FragmentManagerImpl.moveToState(int, int, int, boolean) line: 1104  
BackStackRecord.popFromBackStack(boolean) line: 764 
...

업데이트 1여기에 언급된 로더는 대화 조각에 의해 시작됩니다.

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setHasOptionsMenu(true);
    Bundle args = getArguments();
    m_profileId = args.getString(ArgumentConstants.ARG_USERID);
    m_adapter = new MessageAdapter(this);

    if (savedInstanceState != null) {
        restoreInstanceState(savedInstanceState);
    }
    if (m_adapter.isEmpty()) {
        Bundle bundle = new Bundle();
        bundle.putString(ArgumentConstants.ARG_USERID, m_profileId);
        getLoaderManager().restartLoader(R.id.loader_message_initial, bundle, this);
    } else {
        // Omitted: Some arguments passed in Bundle
        Bundle b = new Bundle(). 
        getLoaderManager().restartLoader(R.id.loader_message_refresh, b, this);
    }
}

@Override
public void onResume() {
    super.onResume();
    // Omitted: setting up UI state / initiating other loaders that work fine
}

@Override
public AbstractMessageLoader onCreateLoader(final int type, final Bundle bundle) {
    final SherlockFragmentActivity context = getSherlockActivity();
    context.setProgressBarIndeterminateVisibility(true);
    switch (type) {
        case R.id.loader_message_empty:
            return new EmptyOnlineLoader(context, bundle);
        case R.id.loader_message_initial:
            return new InitialDBMessageLoader(context, bundle);
        case R.id.loader_message_moreoldDB:
            return new OlderMessageDBLoader(context, bundle);
        case R.id.loader_message_moreoldOnline:
            return new OlderMessageOnlineLoader(context, bundle);
        case R.id.loader_message_send:
            sendPreActions();
            return new SendMessageLoader(context, bundle);
        case R.id.loader_message_refresh:
            return new RefreshMessageLoader(context, bundle);
        default:
            throw new UnsupportedOperationException("Unknown loader");
    }
}

@Override
public void onLoadFinished(Loader<Holder<MessageResult>> loader, Holder<MessageResult> holder) {
    if (getSherlockActivity() != null) {
        getSherlockActivity().setProgressBarIndeterminateVisibility(false);
    }
    // Omitted: Error handling of result (can contain exception)
    List<PrivateMessage> unreadMessages = res.getUnreadMessages();
    switch (type) {
        case R.id.loader_message_moreoldDB: {
            // Omitted error handling (no data)
            if (unreadMessages.isEmpty()) {
                m_hasNoMoreCached = true;
                // Launch an online loader
                Bundle b = new Bundle();
                // Arguments omitted
                getLoaderManager().restartLoader(R.id.loader_message_moreoldOnline, b, ConversationFragment.this);
            }
            // Omitted: Inserting results into adapter
        }
        case R.id.loader_message_empty: { // Online load when nothing in DB
            // Omitted: error/result handling handling
            break;
        }
        case R.id.loader_message_initial: { // Latest from DB, when opening
            // Omitted: Error/result handling

            // If we found nothing, request online
            if (unreadMessages.isEmpty()) {
                 Bundle b = new Bundle();
                 // Omitted: arguments
                 getLoaderManager().restartLoader(R.id.loader_message_empty, b, this);
             } else {
                // Just get new stuff
                Bundle b = new Bundle();
               // Omitted: Arguments
               getLoaderManager().restartLoader(R.id.loader_message_refresh, b, this);
            }
            break;
        }
        // Omitted: Loaders that do not start other loaders, but only add returned data to the adapter
        default:
            throw new IllegalArgumentException("Unknown loader type " + type);
    }
    // Omitted: Refreshing UI elements
}

@Override
public void onLoaderReset(Loader<Holder<MessageResult>> arg0) { }

업데이트 2내 MainActivity(궁극적으로 모든 조각을 호스팅함)는 SherlockFragmentActivity를 하위 클래스로 지정하고 기본적으로 다음과 같은 조각을 시작합니다.

    Fragment f = new ConversationFragment(); // Setup omitted
    f.setRetainInstance(false);
    // Omitted: Code related to navigation drawer
    FragmentManager fragmentManager = getSupportFragmentManager();
    fragmentManager.beginTransaction().replace(R.id.fragment_container_frame, f).commit();

대화 조각은 다음과 같이 "디스플레이 프로필" 조각을 시작합니다.

DisplayProfileFragment f = new DisplayProfileFragment();
// Arguments omitted
FragmentManager manager = getSherlockActivity().getSupportFragmentManager();
FragmentTransaction transaction = manager.beginTransaction();
transaction.replace(R.id.fragment_container_frame, f).addToBackStack(null).commit();
도움이 되었습니까?

해결책

다음과 같은 다른 유사한 질문이 있습니다. 기계적 인조 인간:LoaderCallbacks.OnLoadFinished가 두 번 호출되었습니다. 그러나 로더 관리자 후크의 동작은 그대로입니다.첫 번째 결과 세트를 얻은 후 로더를 삭제할 수 있습니다.

public abstract void destroyLoader (int id)

또는 onLoaderReset을 처리하고 UI 데이터를 로더 데이터에 더 밀접하게 연결할 수 있습니다.

public abstract void onLoaderReset (Loader<D> loader)

이전에 생성 된 로더가 재설정 될 때 호출되어 데이터를 사용할 수 없습니다.이 시점에서 응용 프로그램은 로더 데이터에 대한 참조를 제거해야합니다.

개인적으로 저는 이를 위해 ContentProvider와 CursorLoader를 사용하겠습니다(각 데이터 행에는 고유한 _ID가 필요하지만 문제가 되지 않는 메시지의 경우).

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top