2014-04-01 14 views
17

Tworzę nową przykładową aplikację na Androida z jednym działaniem i wieloma fragmentami.Nie można wykonać tej akcji wewnątrz aplikacji onLoadFinished

Najpierw uruchamiam "OverviewFragment" (mój pulpit), który powinien zawierać podstawowe informacje. Mój PrzeglądFragment ładuje informacje za pomocą "AccountProvider" (ContentProvider). Jeśli baza danych jest pusta, OverviewFragment powinny być zastępowane z moim WelcomeFragment ...

Oto kod:

public class OverviewFragment extends BaseFragment 
    implements LoaderManager.LoaderCallbacks { 

private static final int LOADER_ACCOUNTS = 10; 
private static final int LOADER_TRANSACTIONS = 20; 

@Override 
public void onActivityCreated(Bundle savedInstanceState) { 
    super.onActivityCreated(savedInstanceState); 

    getLoaderManager().initLoader(LOADER_ACCOUNTS, null, this); 
    //getLoaderManager().initLoader(LOADER_TRANSACTIONS, null, this); 
} 

@Override 
public Loader onCreateLoader(int id, Bundle args) { 
    Uri uri = null; 
    String[] projection = null; 
    String selection = null; 
    String[] selectionArgs = null; 
    String sortOrder = null; 

    switch (id) { 
     case LOADER_ACCOUNTS: 
      uri = WalletProvider.CONTENT_URI; 
      projection = new String[]{DBHelper.ACC_ID, DBHelper.ACC_TITLE}; 
      sortOrder = DBHelper.ACC_TITLE; 
      break; 
    } 
    CursorLoader cl = new CursorLoader(getActivity(), 
      uri, projection, selection, selectionArgs, sortOrder); 
    return cl; 
} 

@Override 
public void onLoadFinished(Loader loader, Object data) { 
    switch (loader.getId()) { 
     case LOADER_ACCOUNTS: 
      bindAccounts((Cursor) data); 
      break; 
    } 

} 

@Override 
public void onLoaderReset(Loader loader) { 

} 

private void bindAccounts(Cursor cursor) { 
    boolean showCreateWallet = true; 


    if (cursor != null && cursor.moveToFirst()) { 
     showCreateWallet = false; 
    } 

    if (showCreateWallet) { 
     listener.changeFragment(new WalletCreateFragment()); 
    } 
} 

i tu moja główna działalność

@Override 
public void changeFragment(Fragment fragmentToLoad) { 
    FragmentManager fragmentManager = getSupportFragmentManager(); 
    fragmentManager.beginTransaction() 
      .replace(R.id.container, fragmentToLoad) 
      .commit(); 
} 

... jeśli teraz uruchamiam moją aplikację z pustą bazą danych, otrzymuję komunikat o błędzie widoczny w tytule.

Wiem, że nie powinienem zmieniać fragmentu funkcji onLoadFinished .... ale gdzie mogę to zrobić? to? : P

przepraszam za mój angielski =)

+1

możliwe duplikat [Android - problemy z wykorzystaniem FragmentActivity + Loader do aktualizacji FragmentStatePagerAdapter] (http://stackoverflow.com/questions/7746140/android-problems-using-fragmentactivity-loader-to-update-fragmentstatepagera) – rds

Odpowiedz

27

Jest to błąd z android. Aby rozwiązać ten problem, należy użyć Handler wymienić/dodać/Call fragment wnętrza onLoadFinished tak:

final int WHAT = 1; 
Handler handler = new Handler(){ 
    @Override 
    public void handleMessage(Message msg) {      
     if(msg.what == WHAT) changeFragment(fragmentToLoad);    
    } 
}; 
handler.sendEmptyMessage(WHAT); 
+13

'handler.post (Runnable)' działa również i wydaje się bardziej zwięzły. –

+0

dlaczego sendEmptyMessage? – Roel

+0

wywoływać metodę handleMessage – pablobaldez

8

To nie jest błąd, ale nie jest to proste zagadnienie. onLoadFinished może być uruchomiony w dowolnym momencie, także po zapisaniu stanu działania. Wynikowa zmiana fragmentu nie zostanie automatycznie zapisana przez strukturę. Trzeba wyraźnie pozwala na to używając commitAllowingStateLoss

W dokumentacji onLoadFinished:

Wywoływana gdy poprzednio utworzony ładowarka zakończyła swój ładunek. Zauważ, że normalnie aplikacja nie może zatwierdzać transakcji fragmentu podczas połączenia, ponieważ może się to zdarzyć po zapisaniu stanu aktywności. Zobacz FragmentManager.openTransaction() w celu dalszej dyskusji na ten temat.

Cytowanie Dianne Hackborn z the thread referred to in the comment on the other answer:

Wystarczy zrozumieć, że jeśli stan fragment aktywności został już zapisany, Twój kod będzie musiał poprawnie zaktualizować go, jeśli jest później wznowiona od państwa.

[...]

To złe doświadczenia użytkownika, aby wyświetlić okno dialogowe (czy jakichkolwiek innych poważnych zmian w interfejsie użytkownika) jako skutek ładowarki. Oto, co robisz: wybranie jakiejś operacji, aby działała w tle przez ustaloną ilość czasu, która po zakończeniu może rzucić coś przed użytkownikiem, wyrywając je z tego, co robią.

Moja sugestia to pokazanie użytkownikowi dowolnej informacji o wyniku programu ładującego w linii w taki sam sposób, w jaki wyświetlałaby dane z niego.

Jeśli (i użytkownicy) są ok, z doświadczenia użytkownika, można użyć commitAllowingStateLoss zaktualizować fragment.

+0

To naprawdę właściwa odpowiedź. Jasno określone ryzyko usuwa awarię i daje do zrozumienia, że ​​decydujesz się na coś, co może nie działać dokładnie tak, jak zamierzałeś. –

1

W obu odpowiedziach brakuje dobrego rozwiązania.To prawda, że ​​nie jest dobrym pomysłem zrobienie czegoś na urządzeniu onLoadfinished, ponieważ działanie lub fragment mógł zostać zniszczony.

Uaktualnienie interfejsu użytkownika nie jest również dobrym doświadczeniem, gdy zmieniło się coś w osobnym wątku, ponieważ może to być spowodowane akcją. Ale można to naprawić za pomocą przycisku odświeżania, który następnie aktualizuje to, co widzi użytkownik.

Istnieją jednak okoliczności, w których może być konieczne zaktualizowanie interfejsu użytkownika. Na przykład, jeśli potrzebujemy wykonać akcję, która wymaga od użytkownika oczekiwania po naciśnięciu przycisku następnego.

W przykładzie rozszerzyć Callbacks bezpośrednio przez Fragment, dzięki czemu można stworzyć metodę na swoim fragmencie, który pozwala bezpiecznie wykonać aktualizację UI tak:

private void update(Runnable runnable) { 
    View rootView = getView(); 
    if (isAdded() && rootView != null) { 
     rootView.post(runnable); 
    } 
} 

Sprawdza, czy fragment jest w stan, który może obsłużyć aktualizacje interfejsu użytkownika i uruchamia polecenie. Może być stosowany tak:

update(new Runnable() { 
    @Override 
    public void run() { 
     bindAccounts((Cursor) data); 
    } 
}) 

Powiedział, że nie jest to bardzo mądry pomysł, aby użyć fragmentu jak wywołania zwrotne, ponieważ może on wyciekać swój fragment w LoaderManager który mieszka z działalności.

Innym bezpiecznym sposobem radzenia sobie z tymi problemami są odbiorniki programów telewizyjnych (upewnij się, że są wyrejestrowane).

Jednak najbezpieczniejszym rozwiązaniem będzie korzystać z nowo ogłoszonych Składniki Architektura dla Androida, gdy są gotowe do produkcji:

https://developer.android.com/topic/libraries/architecture/index.html

Powiązane problemy