2010-04-25 26 views
8

Sto correndo in un IllegalStateException aggiornando un List sottostante ad un Adapter (potrebbe essere un ArrayAdapter o un'estensione di BaseAdapter, non ricordo). Al momento non ho o non ricordo il testo dell'eccezione, ma dice qualcosa sull'effetto del contenuto dell'elenco che cambia senza che l'adattatore sia stato informato della modifica.Qual è il modo migliore per aggiornare i dati sottostanti di un adapter?

Questo elenco/può/essere aggiornato da un altro thread diverso dal thread dell'interfaccia utente (principale). Dopo aver aggiornato questo elenco (aggiungendo un elemento), chiamo notifyDataSetChanged. Il problema sembra essere che l'adattatore o ListView collegato all'adaprovetto tenta di aggiornarsi prima che questo metodo venga richiamato. Quando ciò accade, viene generata l'IllegalStateException.

Se si imposta la visibilità di ListView su GONE prima dell'aggiornamento, quindi VISIBILE di nuovo, non si verifica alcun errore. Ma questo non è sempre pratico.

Ho letto da qualche parte che non è possibile modificare il sottostante da un altro thread - questo sembrerebbe limitare un pattern MVC, come con questo particolare List, voglio aggiungere elementi da thread diversi. Supponevo che, fino a quando avessi chiamato notifyDataSetChanged(), sarei sicuro che l'Adapter non avesse rivisitato l'Elenco sottostante fino a quando questo metodo non fosse stato invocato, ma non sembra essere così.

Suppongo che sia quello che sto chiedendo, può essere sicuro aggiornare l'Elenco sottostante da thread diversi dall'interfaccia utente? Inoltre, se voglio modificare i dati all'interno di un adapter, posso modificare l'elenco sottostante o l'adapter stesso (tramite i suoi metodi add(), ecc.). La modifica dei dati tramite l'adattatore sembra errata.

Mi sono imbattuto in un thread su un altro sito da qualcuno che sembra avere un problema simile al mio: http://osdir.com/ml/Android-Developers/2010-04/msg01199.html (questo è da dove ho afferrato l'idea Visibility.GONE e .VISIBLE).

Per dare una migliore idea del mio particolare problema, descriverò un po 'di come sono impostati la mia lista, l'adattatore, ecc.

Ho un oggetto denominato Queue che contiene una lista collegata. Queue estende Observable e quando le cose vengono aggiunte al suo elenco interno attraverso i suoi metodi, chiamo setChanged() e notifyListeners(). Questo oggetto Queue può avere oggetti aggiunti o rimossi da qualsiasi numero di thread.

Ho una singola "coda vista" Attività che contiene un adattatore. Questa attività, nel suo metodo onCreate(), registra un listener Observer sul mio oggetto Queue. Nel metodo update() di Observer chiamo notifyDataSetChanged() sull'Adapter.

Ho aggiunto un sacco di output di registro e determinato che quando si verifica questo IllegalStateExcption la mia chiamata di Observer non è mai stata invocata. Quindi è come se l'adattatore avesse notato la modifica della lista prima che l'osservatore avesse la possibilità di notificare i suoi osservatori e chiamare il mio metodo per notificare all'adapter che il contenuto era cambiato.

Quindi suppongo che quello che sto chiedendo è, è un buon modo per montare un adattatore? È un problema perché sto aggiornando il contenuto dell'adapter da un thread diverso dal thread dell'interfaccia utente? Se questo è il caso, potrei avere una soluzione in mente (dare l'oggetto Queue un Handler al thread dell'interfaccia utente al momento della sua creazione, e fare tutte le modifiche di List usando questo Handler, ma ciò sembra inappropriato).

Mi rendo conto che questo è un post molto aperto, ma sono un po 'perso su questo e apprezzerei qualsiasi commento su ciò che ho scritto.

risposta

8

Questo elenco/può/essere aggiornato da un altro thread diverso dal thread UI (principale)

che non funzionano.

ho letto da qualche parte che non si può modificare la base di questa da un altro thread - questo sembrerebbe limite un pattern MVC, come con questo particolare elenco, voglio aggiungere elementi da diversi thread

MVC non ha nulla a che fare con i thread.

esso può essere sicuro per aggiornare l'elenco sottostante dai filetti altri quella UI?

No. Altri thread possono attivare gli aggiornamenti all'adattatore (ad esempio, tramite post()), ma gli aggiornamenti stessi devono essere elaborati nel thread dell'applicazione principale, per un adattatore che è attualmente collegato a un ListView.

Inoltre, se voglio modificare i dati all'interno di un adattatore, modifico la lista sottostante o l'adattatore stesso (tramite il suo add(), ecc metodi). La modifica dei dati tramite l'adattatore sembra errata.

di modificare i Adapter tramite il Adapter se stesso per ArrayAdapter. È possibile modificare il Adapter tramite il provider di database/contenuto sottostante per CursorAdapter. Altri adattatori possono variare.

Ho un oggetto denominato Queue che contiene una lista collegata. La coda si estende Osservabile e quando le cose vengono aggiunte al proprio elenco interno tramite i suoi metodi , chiamo setChanged() e notifyListeners().

Hai pensato di usare LinkedBlockingQueue, piuttosto che attuare il proprio thread-safe Queue?

Questa attività, nel suo metodo onCreate(), registra un listener Observer al mio oggetto coda. Nel metodo update() di Observer, chiamata notifyDataSetChanged() sull'adattatore.

Adapters deve chiamare notifyDataSetChanged() su se stessi (se la modifica viene effettuata da loro) o averlo chiesto loro dall'entità che sta cambiando i dati (per esempio, un Cursor per un CursorAdapter). Che è MVC. Activity non dovrebbe sapere né preoccuparsi quando il modello di dati cambia.

Quindi è come se l'adattatore notato il cambiamento della Lista prima che l'Observer ha avuto la possibilità di comunicare i suoi osservatori, e chiamare il mio metodo per notificare l'adattatore che i contenuti erano cambiate.

Forse si sta utilizzando un ArrayAdapter, nel qual caso tutto questo osservatore extra/notifica roba sta ottenendo nel vostro senso, dato che è gestito per voi. Hai solo bisogno di organizzare per aggiornare il ArrayAdapter sul thread dell'applicazione principale.

Quindi suppongo che quello che sto chiedendo è, è questo un buon modo per montare un adattatore?

Non particolarmente, IMHO.

È questo un problema perché sto aggiornando il contenuto della scheda da un thread diverso dal thread UI?

Se non si forzano gli aggiornamenti indietro al thread dell'applicazione principale, questo alla fine si bloccherà, una volta chiarito gli altri problemi.

danno la coda oggetto un Handler al thread interfaccia utente quando viene creato, e fare tutte le modifiche lista usando che Handler, ma questo sembra improprio

si potrebbe usare un Handler, oppure Potrei chiamare post() sul tuo allegato ListView.

Al polsino, vorrei creare una sottoclasse di ArrayAdapter denominata ThreadSafeArrayAdapter e utilizzarla al posto di Queue. ThreadSafeArrayAdapter sostituisce add(), insert() e remove() con quelli in cui la superclasse fa il suo oggetto nella thread principale dell'applicazione, tramite Handler o post().

+0

Grazie per i vostri commenti, ma sono ancora confuso su alcune cose, se non vi dispiacerebbe aiutare ulteriormente. Il nome dell'oggetto "Queue" è un po 'improprio - è più una coda multimediale di video da riprodurre, o canzoni da riprodurre. È un elenco di elementi con un intIndex current. Ho capito che l'adattatore è solo un ponte tra il mio modello (la mia lista) e la vista (ListView). Cosa succede se ho bisogno di aggiornare i dati che la vista mostra da un'altra attività in cui non ho accesso alla scheda? Sembrerebbe naturale aggiornare l'Elenco sottostante, forse da un Runnable se l'attività che esegue questo add potrebbe bloccare. – skyler

+0

"Ho capito che l'adattatore era solo un ponte tra il mio modello (la mia lista) e la vista (ListView)." Questo è generalmente vero. "Che cosa succede se ho bisogno di aggiornare i dati che la visualizzazione mostra da un'altra attività in cui non ho accesso alla scheda?" Molto probabilmente, non hai nemmeno accesso alla lista, quindi avrai bisogno di una strategia più elaborata. "Sembrerebbe naturale aggiornare l'Elenco sottostante, forse da un Runnable se l'attività che esegue questo add potrebbe bloccare". IMHO, il problema di threading è un problema di interfaccia utente, motivo per cui raccomando che il tuo adattatore sia quello che si occupa della sicurezza dei thread. – CommonsWare

+0

Tuttavia, è possibile implementarlo come si desidera, a condizione che si aggiorni l'adattatore solo sul thread dell'applicazione principale. – CommonsWare

0

Nel complesso un buon consiglio è http://developer.android.com/resources/articles/painless-threading.html

Personalmente io uso il mio thread personalizzato (una estensione Discussione di classe), ma inviare risposta al thread UI attraverso un messaggio. Quindi, in funzione del thread run() c'è:

Message msg; 
msg = Message.obtain(); 
msg.what = MSG_IMG_SET;      
mExtHandler.sendMessage(msg); 

Il mExtHandler è stato assegnato all'istanza gestore esterno nel costruttore thread. Il thread dell'interfaccia utente definisce il gestore di messaggi:

private Handler mImagesProgressHandler; 

public void onCreate(Bundle bundle) { 

    mImagesProgressHandler = new Handler() { 
    @Override 
    public void handleMessage(Message msg) { 
     switch (msg.what) {    
     case LoadImagesThread.MSG_IMG_SET: 
      mArrayAdapter.setBitmapList(mImagesList); 
      mArrayAdapter.notifyDataSetChanged(); 
      break; 
     case LoadImagesThread.MSG_ERROR: 
      break; 
     } 
     super.handleMessage(msg); 
    } 
};     

Questo in realtà è più facile che AsyncTask.

+0

Fai attenzione alle classi interne del Gestore non statiche. Il può essere un rischio per perdite di memoria. – HAxxor

+0

Buon punto, grazie. – Yar

Problemi correlati