2010-04-25 2 views
8

Я запускаюсь в IllegalStateException, обновляя базовый список адаптера (может быть, ArrayAdapter или расширение BaseAdapter, я не помню). На данный момент у меня нет или не помню текст исключения, но он говорит что-то о том, что содержимое списка изменяется без изменения Адаптера об изменении.Каков наилучший способ обновления базовых данных адаптера?

Этот список/может быть обновлен из другой темы, кроме темы пользовательского интерфейса (main). После того как я обновил этот список (добавив элемент), я вызываю notifyDataSetChanged. Проблема заключается в том, что адаптер или ListView, подключенный к адаптеру, пытается обновить себя до вызова этого метода. Когда это происходит, вызывается исключение IllegalStateException.

Если я установил видимость ListView в GONE перед обновлением, то снова VISIBLE, ошибки не возникает. Но это не всегда практично.

Я где-то читал, что вы не можете изменить базовое значение этого из другого потока - это, по-видимому, ограничивает шаблон MVC, как и этот конкретный список, я хочу добавлять элементы из разных потоков. Я предположил, что до тех пор, пока я вызвал notifyDataSetChanged(), я был бы в безопасности - что адаптер не пересматривал базовый список до тех пор, пока этот метод не был вызван, но это, похоже, не так.

Я полагаю, что я спрашиваю, можно ли безопасно обновлять базовый список из потоков, отличных от пользовательского интерфейса? Кроме того, если я хочу изменить данные в адаптере, я могу изменить базовый список или сам адаптер (с помощью методов add() и т. Д.). Изменение данных через адаптер кажется неправильным.

Я наткнулся на нить на другом сайте от кого-то, у кого, похоже, есть аналогичная проблема с моей: http://osdir.com/ml/Android-Developers/2010-04/msg01199.html (это от того места, где я схватил идею Visibility.GONE и .VISIBLE).

Чтобы лучше узнать о моей конкретной проблеме, я расскажу немного о том, как настроены мой список, адаптер и т. Д.

У меня есть объект с именем Queue, который содержит LinkedList. Очередь расширяет Observable, и когда вещи добавляются во внутренний список через свои методы, я вызываю setChanged() и notifyListeners(). Этот объект Queue может иметь элементы, добавленные или удаленные из любого количества потоков.

У меня есть один вид «очереди», в котором содержится адаптер. Эта операция в своем методе onCreate() регистрирует прослушиватель Observer для моего объекта Queue. В методе update() Observer я вызываю notifyDataSetChanged() на адаптере.

Я добавил много выходных данных журнала и определил, что, когда это IllegalStateExcption происходит, мой обратный вызов наблюдателя никогда не вызывался. Так что, как будто адаптер заметил изменение List перед тем, как Observer получил возможность уведомить своих наблюдателей и вызвать мой метод для уведомления адаптера, что содержимое было изменено.

Итак, я полагаю, что я прошу, это хороший способ подгонять адаптер? Это проблема, потому что я обновляю содержимое адаптера из потока, отличного от потока пользовательского интерфейса? Если это так, у меня может возникнуть решение (передайте объект Queue обработчику потока пользовательского интерфейса при его создании и сделайте все изменения в List с помощью этого обработчика, но это кажется неправильным).

Я понимаю, что это очень открытое сообщение, но я немного потерял это и буду благодарен за любые комментарии к тому, что я написал.

ответ

8

Этот список/может/быть обновлена ​​с другого потока, отличного от UI нити (основной)

Это не будет работать.

Я где-то читал, что вы не можете изменить лежащий в основе этого от другого потока - это, казалось бы ограничивать шаблон MVC, как и с этим конкретным списком, я хочу, чтобы добавить элементы из разных потоков

MVC не имеет ничего общего с нитями.

это может быть безопасно обновить основной список из других , чем UI потоков?

No. Другие потоки могут вызвать обновления к адаптеру (например, с помощью post()), но и сами обновления должны быть обработаны на главном потоке приложения, для адаптера, который в настоящее время прикреплен к ListView.

Кроме того, если я хочу изменить данные в адаптере, я изменить основной список или адаптер сам (через его дополнения() и т.д. методы). Изменение данных через адаптер кажется неправильным.

Вы изменяете ваш Adapter через сам Adapter для ArrayAdapter. Вы изменяете свой Adapter через базовую базу данных/поставщика контента для CursorAdapter. Другие адаптеры могут отличаться.

У меня есть объект с именем Queue, который содержит LinkedList. Queue extends Наблюдается, и когда вещи добавляются во внутренний список через его методы , я вызываю setChanged() и notifyListeners().

Рассматривали ли вы с помощью LinkedBlockingQueue, а не реализовать собственные поточно-Queue?

Эта активность, в OnCreate() метода, регистрирует слушатель Observer к моему объекту очереди. В методе update() Observer notifyDataSetChanged() на адаптере.

Adapters должны вызывать notifyDataSetChanged() на себя (если изменение производится ими), либо он призвал их объектом, который изменяет данные (например, Cursor для CursorAdapter). Это это MVC. Activity не должен знать и не заботиться о том, когда изменяется модель данных.

Так как если адаптер заметил изменения в List перед наблюдателем, возможности уведомить свои наблюдатель, и называет мой метод, чтобы уведомить адаптер , что содержание изменилось.

Возможно, вы используете ArrayAdapter, в этом случае все дополнительные наблюдатель/уведомит материал становится на вашем пути, так что это обрабатывается для вас. Вам просто нужно организовать обновление ArrayAdapter в главном потоке приложения.

Так что я полагаю, что я спрашиваю, есть это хороший способ вышкомонтажных закрепительный?

Не особо, ИМХО.

Является ли это проблема, потому что я обновляю содержимое адаптера из нити , кроме потока пользовательского интерфейса?

Если вы не заставляете обновления возвращаться к основной теме приложения, это в конечном итоге сбой, как только вы устраните другие проблемы.

дать очереди объекта обработчику нити UI, когда он создан, и сделать все изменения списка с помощью этого Handler, но это, кажется, неправильное

Вы можете использовать Handler, или может позвонить по телефону post() на вашем прилагаемом ListView.

С помощью манжеты создайте подкласс ArrayAdapter с именем ThreadSafeArrayAdapter и используйте его вместо Queue. ThreadSafeArrayAdapter заменяет add(), insert() и remove() с теми, у которых суперкласс выполняет свою задачу на основной прикладной нити, через Handler или post().

+0

Спасибо за ваши комментарии, но я все еще смущен несколькими вещами, если вы не возражаете помочь дальше. Имя объекта «Очередь» является немного неправильным - это скорее очередь медиа-роликов для воспроизведения, или песни для воспроизведения. Это список элементов с currentIndex int. Я понял, что адаптер является просто мостом между моей моделью (моим списком) и представлением (ListView). Что делать, если мне нужно обновить данные, отображаемые в представлении другого объекта, где у меня нет доступа к адаптеру? Кажется естественным обновить базовый список, возможно, из Runnable, если задача, выполняющая это добавление, может блокироваться. – skyler

+0

«Я понял, что адаптер является просто мостом между моей моделью (мой список) и представлением (ListView)». Это вообще правда. «Что делать, если мне нужно обновить данные, отображаемые в представлении другого объекта, где у меня нет доступа к адаптеру?» Скорее всего, у вас нет доступа к списку, тогда вам понадобится более сложная стратегия. «Кажется естественным обновить базовый список, возможно, из Runnable, если задача, выполняющая этот add, может блокироваться». IMHO, проблема с потоками является проблемой пользовательского интерфейса, поэтому я рекомендую, чтобы ваш адаптер был одним из них для решения проблемы безопасности потоков. – CommonsWare

+0

Но вы можете реализовать его, как хотите, до тех пор, пока вы обновляете адаптер только в основном приложении. – CommonsWare

0

В целом хороший совет http://developer.android.com/resources/articles/painless-threading.html

Лично я использую свой собственный поток (класс расширения Thread), но отправить ответ на поток пользовательского интерфейса через сообщение. Таким образом, в функции потока Run() есть:

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

mExtHandler был назначен на экземпляр внешнего обработчика в конструкторе потока. Нить UI определяет обработчик сообщений:

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); 
    } 
};     

Это на самом деле проще, чем AsyncTask.

+0

Будьте осторожны с нестационарными внутренними классами Handler. Это может быть риск утечки памяти. – HAxxor

+0

Хорошая точка, спасибо. – Yar

Смежные вопросы