Autore Topic: Comportamento per me incomprensibile!  (Letto 715 volte)

Offline undead

  • Utente senior
  • ****
  • Post: 666
  • Respect: +113
    • Mostra profilo
  • Dispositivo Android:
    Samsung Galaxy S6
  • Play Store ID:
    DrKappa
  • Sistema operativo:
    Windows 10 64-bit, Windows 8.1 64-bit
Comportamento per me incomprensibile!
« il: 18 Dicembre 2014, 22:22:27 CET »
0
Ciao a tutti,

ho dei grossi problemi nel capire esattamente il funzionamento della combo ActionBarActivity ViewPager FragmentPagerAdapter Fragment.

Premetto che uso la support library.

Ho ovviamente una activity derivata da actionbaractivity con un viewpager nell'XML al quale setto una classe derivata da fragmentpageradapter e da lì creo i fragment. I fragment sono creati con delle new, non uso singleton.

Per semplificare diciamo che ho due fragment quindi in fase di swipe i fragment non sono distrutti/ricreati.

Tutto funziona in modo abbastanza lineare, lancio altre activity, torno indietro, chiudo riapro e così via.

Il comportamento sul cambio di orientamento però per me è un mistero!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

Viene sicuramente richiamata la onCreate della activity. Nella oncreate io setto la content view che viene correttamente "resettata" e da lì prendo l'oggetto viewpager. i fragment sono in stato resettato. Per dire se in una textview a seguito di un evento ho scritto "fatto" e all'avvio (da XML) la textview contiene "in attesa" io vedo scritto "in attesa". Il fragment dovrebbe essere "nuovo".

Perfetto, salvo il fragment in un membro della activity in fase di creazione nell'adapter. Dopo il cambio di orientamento provo a "scriverci" ma mi viene segnalato che che il fragment è detached dall'activity!!! Ma dovrebbe essere nuovo!
Come controprova forzo a null la copia del fragment nella oncreate, dopo il cambio di orientamento mi da nullpointerexception!!!

Ovviamente c'è una discrepanza nel processo di creazione del fragment tra l'avvio normale/resume e l'orientation change.

Finora ho capito questo:
la getitem NON viene chiamata a seguito di un cambio di orientamento.

Non capisco perché all'avvio si e in questo caso no.

Se io ho ricreato tutto come fa a "sapere" che ha già chiamato getitem prima del cambio di orientamento. Ripeto... io ho una new MioFragmentPagerAdapter nella oncreate!!! Viene chiamato il metodo instantiateItem.

Però... rullo di tamburi... NON VIENE CHIAMATA LA ONSTART DEL FRAGMENT.

Ho due domande:
-se è una nuova istanza di un fragment perché non viene richiamata la onStart()?
-come è possibile che rifacendo tutto nella oncreate (setcontentview findviewbyid new adapter set adapter) questo si comporti diversamente rispetto al primo avvio???

Sicuramente ha ragione Google e sono ignorante io. Però se viene chiamata la oncreate e io ricreo tutto fregandomene se è un cambio di orientamento o il primo avvio pretendo che il comportamento sia il medesimo.

Se una funzione che si chiama instantiateItem() del mio adapter mi riporta una istanza di un fragment e ho appena fatto una new adapter() mi aspetto che il fragment sia nuovo di pacca.
Essendo nuovo di pacca mi aspetto che qualcuno dica a questo NUOVO fragment che deve "partire", cioè mi devi richiamare la onStart!!!

Quale è il senso "filosofico" di questo comportamento? Cosa mi sfugge?

Offline nibbler

  • Nuovo arrivato
  • *
  • Post: 14
  • Respect: +2
    • Mostra profilo
  • Dispositivo Android:
    Htc sensantion XL
  • Sistema operativo:
    windows 7
Re:Comportamento per me incomprensibile!
« Risposta #1 il: 22 Dicembre 2014, 21:47:05 CET »
0
"...... salvo il fragment in un membro della activity in fase di creazione nell'adapter"

Cosa intendi con questa frase?

Se invece mantieni un riferimento all' activity nel onAttach() dell fragment non è forse la soluzione migliore?
Non devi avere nessun riferimento ai fragment nell' activity (oltretutto sono dentro a un ViewPager).
Se ti dovessero servire fai un findFRagmentByTag e controlli se diverso da null.


Offline undead

  • Utente senior
  • ****
  • Post: 666
  • Respect: +113
    • Mostra profilo
  • Dispositivo Android:
    Samsung Galaxy S6
  • Play Store ID:
    DrKappa
  • Sistema operativo:
    Windows 10 64-bit, Windows 8.1 64-bit
Re:Comportamento per me incomprensibile!
« Risposta #2 il: 23 Dicembre 2014, 00:45:18 CET »
0
"...... salvo il fragment in un membro della activity in fase di creazione nell'adapter"

Cosa intendi con questa frase?
Dichiaro un membro dell'activity di tipo MioFragmentBase.
Poi in fase di creazione del fragment lo copio così posso accederci.

Citazione
Se invece mantieni un riferimento all' activity nel onAttach() dell fragment non è forse la soluzione migliore?
Beh dipende. L'activity esegue delle operazioni asincrone che possono terminare anche nel giro di secondi e poi deve visualizzare il risultato su N fragment diversi.. pensavo che fosse logico copiare un riferimento al fragment visto che chi "comanda" è l'activity.

Non so se esiste un pattern migliore. Per esempio prendi la app di analytics o di adsense... ha N fragment e tutti vengono aggiornati leggendo i dati online. O hai un servizio per ogni pagina altrimenti se i dati arrivano da una singola richiesta mi sembrava normale fare la richiesta dall'activity e "riempire" i fragment quando il servizio mi risponde.

Questo anche perché da come ho capito i fragment dovrebbero essere riusabili... quindi il fragment che mostra un certo tipo di dato dovrebbe essere slegato dal processo di creazione/download di quel dato. :-)

Citazione
Non devi avere nessun riferimento ai fragment nell' activity (oltretutto sono dentro a un ViewPager).
Se ti dovessero servire fai un findFRagmentByTag e controlli se diverso da null.
Il tag è autogenerato dal sistema nel caso della combo che sto usando... e il tag dovrebbe essere il seguente: "android:switcher:IDVIEW:INDEX".
Il problema è che andare a usare questa stringa non garantisce che funzioni in nuove versioni di android.

 :-(

Però il mio problema più che il crash, che avevo forzato come controprova che getItem non fosse richiamato, era capire il comportamento di quella combinazione specifica.

Alla fine qualcosa ci ho capito. In pratica non vengono distrutti i fragment in fase di cambio di orientamento. Non solo questo crea grosse discrepanze tra il lifecycle dell'activity e quello dei fragment, ma le "creazioni" successive non richiamano la getItem() dell'adapter come invece succede all'avvio bensì la newInstance().

Quindi avendo ridefinito la getItem ma non la newInstance io mi trovavo con dei fragment "nuovi di pacca" che però non andavo a copiare da nessuna parte. Da lì il problema.
Se non li settavo null nella oncreate mandavo i risultati ai vecchi fragment.
Se facevo la controprova e mettevo null nella oncreate, poiché getitem non veniva richiamata, inviavo dei dati a null.

Continuo comunque a trovare poco intuitivo questo comportamento di fragmentmanager/viewpager/fragmentpageradapter.

Offline nibbler

  • Nuovo arrivato
  • *
  • Post: 14
  • Respect: +2
    • Mostra profilo
  • Dispositivo Android:
    Htc sensantion XL
  • Sistema operativo:
    windows 7
Re:Comportamento per me incomprensibile!
« Risposta #3 il: 23 Dicembre 2014, 10:08:44 CET »
+1
"..Questo anche perché da come ho capito i fragment dovrebbero essere riusabili... quindi il fragment che mostra un certo tipo di dato dovrebbe essere slegato dal processo di creazione/download di quel dato"

Proprio per questo motivo si usa dichiarare che l'activity implementa un interfaccia (tipo OnFragmentClick) e nella onAttacch del fragment mantieni questo riferimento come  OnFragmentClick myClick=(OnFragmentClick)activity.

Quando hai bisogno di comunicare qualcosa lo fai usando un interfaccia.

Dovresti guardare il sorgente del viewPager.
Probabilmente al suo interno gestisce il backstack dei fragment e quindi consente il loro ri utilizzo.

Sei comunque sempre sicuro che qualsiasi fragment visualizzato avra' eseguito la onAttach e avra' quindi un riferimento all'activity per comunicare con essa.

Ciao



Offline undead

  • Utente senior
  • ****
  • Post: 666
  • Respect: +113
    • Mostra profilo
  • Dispositivo Android:
    Samsung Galaxy S6
  • Play Store ID:
    DrKappa
  • Sistema operativo:
    Windows 10 64-bit, Windows 8.1 64-bit
Re:Comportamento per me incomprensibile!
« Risposta #4 il: 23 Dicembre 2014, 13:47:55 CET »
0
Capisco quello che intendi, il pattern è sicuramente efficiente.  :-)

Però prendi per esempio la app di analytics.
Tu la fai partire e senza cliccare niente inizia a ricercare dati aggiornati.

Per fare questo seguendo il pattern che hai descritto dovrei far partire la richiesta all'activity da dentro l'onattach.
Il problema è che scrivendo su N fragment dovrei far partire la richiesta dall'ultimo fragment che riceve l'onattach. Questo perchè potrei trovarmi nella situazione in cui il dato è disponibile localmente (attraverso un meccanismo di caching che anche analytics implementa) e quindi andrei a riempire fragment ancora non attaccati ad una activity.

Il pattern che descrivi funziona però molto bene in senso inverso.
Volendo aggiornare i dati dal fragment posso fare una richiesta a partire dalla pressione di un bottone. In questo caso si che il pattern è efficiente.
Bottone->onclick->chiamata a onfragmentclick->inizio aggiornamento asincrono->scrittura su N fragment.

Diciamo che nel caso base in cui tutti i fragment sono "attivi" se clicco su un bottone sul fragment 1 sono sicuro che fragment 2 sia attached.
Far partire qualcosa nella onstart o nella onattached o altro non garantisce che gli altri fragment siano pronti.  :-(

Offline nibbler

  • Nuovo arrivato
  • *
  • Post: 14
  • Respect: +2
    • Mostra profilo
  • Dispositivo Android:
    Htc sensantion XL
  • Sistema operativo:
    windows 7
Re:Comportamento per me incomprensibile!
« Risposta #5 il: 23 Dicembre 2014, 21:15:34 CET »
+1
Ma no hai detto che stai usando un ViewPager?
In questo caso il fragment attivo è solo uno e su questo hai il riferimento all'activity.
L'altro fragment viene pre caricato dal viewPager ma non è visibile e quindi non vedo perchè dovresti scrivergli dentro.
Quando sarà visibile attraverso il findviewby... ecc hai accesso al fragment.

Se invence hai anche altri fragment non visibili allora essi saranno magari retain e quindi non verranno distrutti e li puoi fare quello che vuoi.

Eventualmente puoi usare anche librerie che usano Bus (Otto) per de-accoppiare activity e frament.
Non trovo utilizzabile la soluzione di tenere riferimenti ai fragment quando usi un viewpager.
Poi per l'amor di dio...ognuno scrive ciò che più gli piace !! eheheehhehh
Ciao e alla prossima

Offline arlabs

  • Utente normale
  • ***
  • Post: 434
  • Respect: +49
    • Mostra profilo
  • Dispositivo Android:
    GalaxyS6, Nexus5
  • Play Store ID:
    AR Labs
  • Sistema operativo:
    Windows 10
Re:Comportamento per me incomprensibile!
« Risposta #6 il: 28 Maggio 2015, 17:23:49 CEST »
0
Undead,
Mi sono ritrovato nella tua stessa condizione e sono arrivato alla tua stessa conclusione. Che i fragment vengono mantenuti.

Fino ad ora avevo seguito una filosofia diversa per il ViewPager. Mettere i Fragment nel layout (all'interno del ViewPager) e nella instantiateItem non allocavo nulla ma facevo semplicemente findFragmentById
Funzionava con 2 pagine. ha cominciato a crearmi problemi con 3 pagine.

Ho quindi seguito la filosofia di bradipao (http://www.anddev.it/index.php/topic,6717.0.html) che ricrea i Fragment ad ogni onCreate dell'Activity e mi sono ritrovato con i fragment allocati diversi da quelli visualizzati.

Ma alla fine come hai risolto?
Nella Activity come ottieni i fragment che creai nell'adapter?
Hai usato il TAG interno creato dal FragmentPagerAdapter (makeFragmentName)?
Ho pensato anche a fare l'override di instantiateItem, chiamare super.instantiateItem e di salvarmi tutte le istanze. O anche di mettere un metodo "present" nel Listener di callback (fa Fragment ad Activity) da chiamare nella onAttach.

Possibile che non ci sia un metodo più semplice e diretto?

Ciao

Offline nibbler

  • Nuovo arrivato
  • *
  • Post: 14
  • Respect: +2
    • Mostra profilo
  • Dispositivo Android:
    Htc sensantion XL
  • Sistema operativo:
    windows 7
Re:Comportamento per me incomprensibile!
« Risposta #7 il: 04 Giugno 2015, 01:06:00 CEST »
0
Forse può tornarvi utile...
Non è che state usando un PagerAdapter ?
Il PagerAdapter  non de-alloca i fragment ma li mantiene.
Nel caso vi servisse visualizzare un maggior numero di fragment allora vi conviene usare il FragmentStatePagerAdapter  che sicuramente de alloca i Fragment come spiegato in questo link:

android - Difference between FragmentPagerAdapter and FragmentStatePagerAdapter - Stack Overflow

Spero vi sia utile per il vostro problema.
Ciao!!

Offline arlabs

  • Utente normale
  • ***
  • Post: 434
  • Respect: +49
    • Mostra profilo
  • Dispositivo Android:
    GalaxyS6, Nexus5
  • Play Store ID:
    AR Labs
  • Sistema operativo:
    Windows 10
Re:Comportamento per me incomprensibile!
« Risposta #8 il: 04 Giugno 2015, 09:42:39 CEST »
0
Sì, nibbler, grazie.
Effettivamente sto usando un PagerAdapter e appunto mi sono accorto, e anche Undead, che non rilascia i fragment.
Il PagerAdapter è effettivamente ciò che mi serve (ho 3 pagine con 3 fragment diversi)
La cosa che mi ha lasciato basito è che vengono mantenuti anche se l'intera Activity (e pure il ViewPager) vengono distrutti e ricreati.
Questa cosa non è ben documentata e se non lo sai e pensi che vengano ricreati, rischi di avere diversi side-effects.

Alla fine ho risolto facendo un override di instanciateItem (vedi http://www.anddev.it/index.php/topic,6717.msg82437.html#msg82437)

Ciao.