5

Versione corta:onResume() non ha invitato ViewPager frammento quando si utilizza Loader personalizzato

Ho un frammento che mantiene un ViewPager per la visualizzazione di altri due frammenti, diamo loro FragmentOne e FragmentTwo chiamano. Quando si avvia l'app FragmentOne è visibile e FragmentTwo è fuori schermo, diventando visibile solo quando si passa la vista a sinistra.

Normalmente onStart() e onResume() vengono richiamati immediatamente per entrambi i frammenti non appena l'app viene avviata.

Il problema che ho è quando FragmentOne inizia una consuetudine Loader poi onResume() non viene chiamato FragmentTwo fino a quando diventa completamente visibile.

Screenshot showing the problem

Domande:

Si tratta di un problema con il mio codice o un bug nel libreria di supporto di Android? (Il problema non si è verificato con la revisione 12 della libreria, è iniziato con la revisione 13.)

Se si tratta di un bug nelle revisioni 13 e 18, c'è una soluzione?

C'è qualcosa di sbagliato nella mia consueta Loader?

Versione lunga:

Ho creato un'applicazione di esempio che illustra il problema. Ho provato a ridurre il codice al minimo, ma è ancora molto, quindi per favore portami dietro.

Ho un MainActivity che carica uno MainFragment che crea un ViewPager. Per la mia app è importante che il ViewPager sia gestito da un frammento anziché da un'attività.

MainFragment crea una FragmentPagerAdapter che a sua volta crea i frammenti FragmentOne e FragmentTwo.

Cominciamo con il bit interessante, i due frammenti:

FragmentOne è un ListFragment che utilizza un personalizzato Loader per caricare il contenuto:

public class FragmentOne extends ListFragment implements LoaderCallbacks<List<String>> { 
    private ArrayAdapter<String> adapter; 

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

     adapter = new ArrayAdapter<String>(getActivity(), android.R.layout.simple_list_item_1); 
     setListAdapter(adapter); 

     setEmptyText("Empty"); 
    } 

    @Override 
    public void onResume() { 
     super.onResume(); 

     // initializing the loader seems to cause the problem! 
     getLoaderManager().initLoader(0, null, this); 
    } 

    @Override 
    public Loader<List<String>> onCreateLoader(int id, Bundle args) { 
     return new MyLoader(getActivity()); 
    } 

    @Override 
    public void onLoadFinished(Loader<List<String>> loader, List<String> data) { 
     adapter.clear(); 
     adapter.addAll(data); 
    } 

    @Override 
    public void onLoaderReset(Loader<List<String>> loader) { 
     adapter.clear(); 
    } 

    public static class MyLoader extends AsyncTaskLoader<List<String>> { 
     public MyLoader(Context context) { 
      super(context); 
     } 

     @Override 
     protected void onStartLoading() { 
      forceLoad(); 
     } 

     @Override 
     public List<String> loadInBackground() { 
      return Arrays.asList("- - - - - - - - - - - - - - - - - - - foo", 
        "- - - - - - - - - - - - - - - - - - - bar", 
        "- - - - - - - - - - - - - - - - - - - baz"); 
     } 
    } 
} 

È che Loader che sembra causare il problema . Commentando la riga initLoader, il ciclo di vita dei frammenti funziona come previsto.

FragmentTwo cambia il suo contenuto in base al fatto onResume() è stato invocato o no:

public class FragmentTwo extends Fragment { 
    private TextView text; 

    @Override 
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 
     text = new TextView(container.getContext()); 
     text.setText("onCreateView() called"); 
     return text; 
    } 

    @Override 
    public void onResume() { 
     super.onResume(); 
     Log.i("Fragment2", "onResume() called"); 
     text.setText("onResume() called"); 
    } 
} 

E qui è il resto noioso del codice.

MainActivity:

public class MainActivity extends FragmentActivity { 
    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 

     setContentView(R.layout.activity_main); 

     Fragment fragment = new MainFragment(); 
     getSupportFragmentManager().beginTransaction().add(R.id.container, fragment).commit(); 
    } 
} 

layout activity_main:

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    android:id="@+id/container" 
    android:layout_width="match_parent" 
    android:layout_height="match_parent" /> 

MainFragment:

public class MainFragment extends Fragment { 
    private ViewPager viewPager; 

    @Override 
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 
     View layout = inflater.inflate(R.layout.frag_master, container, false); 
     viewPager = (ViewPager) layout.findViewById(R.id.view_pager); 
     return layout; 
    } 

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

     viewPager.setAdapter(new MyPagerAdapter(getChildFragmentManager())); 
    } 

    private static final class MyPagerAdapter extends FragmentPagerAdapter { 
     public MyPagerAdapter(FragmentManager fragmentManager) { 
      super(fragmentManager); 
     } 

     @Override 
     public int getCount() { 
      return 2; 
     } 

     @Override 
     public Fragment getItem(int position) { 
      if (position == 0) 
       return new FragmentOne(); 
      else 
       return new FragmentTwo(); 
     } 
    } 
} 

layout frag_master:

<android.support.v4.view.ViewPager xmlns:android="http://schemas.android.com/apk/res/android" 
    android:id="@+id/view_pager" 
    android:layout_width="match_parent" 
    android:layout_height="match_parent" /> 

risposta

11

Sembra un errore nella libreria di supporto. La modifica seguente risolve il problema.

// FragmentOne.java 

@Override 
public void onResume() { 
    super.onResume(); 
    Handler handler = getActivity().getWindow().getDecorView().getHandler(); 
    handler.post(new Runnable() { 
     @Override public void run() { 
      // initialize the loader here! 
      getLoaderManager().initLoader(0, null, FragmentOne.this); 
     } 
    }); 
} 
+0

Sto avendo esattamente lo stesso problema come siete, lavori soluzione, ma con le prestazioni - prezzo ritardo. Come hai trovato la soluzione? –

+0

perché lo metti nel gestore? – Xenione

+2

@Xenione Questa è solo una soluzione, ma è sicura da usare. Ecco i miei pensieri. Vediamo che chiamare initLoader() nel primo Fragment impedisce di chiamare onResume() su altri frammenti (molto probabilmente a causa di un bug nella libreria). In questa chiamata non esiste alcuna chiamata, tutti i metodi onResume() vengono richiamati in un singolo frame di esecuzione. Logicamente, se vogliamo che framework invochi i metodi onResume() in tutti i Fragments, dobbiamo evitare di chiamare initLoader(). Logicamente, per inizializzare il loader immediatamente dopo onResume() dobbiamo farlo nel prossimo frame di esecuzione. Questo è dove handler.post() aiuta molto. –

3

Un'altra soluzione che ha funzionato per me è stato quello di utilizzare il gestore del MainFragment loader:

getParentFragment().getLoaderManager().initLoader(0, null, this); 
+0

Stavo cercando una soluzione per la mia domanda http://stackoverflow.com/q/34503845/2277631 quando ne ho trovato uno simile http://stackoverflow.com/q/17885053/2277631 che mi ha portato qui. Questo sembra funzionare. Hai trovato qualche inconveniente nell'usare il frammento del genitore per avviare il loader? –

Problemi correlati