Каков наилучший способ обновления базовых данных адаптера? - PullRequest
8 голосов
/ 25 апреля 2010

Я сталкиваюсь с IllegalStateException, обновляющим базовый List на Adapter (может быть ArrayAdapter или расширение BaseAdapter, я не помню). В настоящий момент у меня нет или не помню текст исключения, но он говорит о том, что содержание Списка изменяется без уведомления адаптера об этом изменении.

Этот список / может / обновляться из другого потока, кроме потока пользовательского интерфейса (основного). После того, как я обновляю этот список (добавляя элемент), я вызываю notifyDataSetChanged. Похоже, проблема заключается в том, что Adapter или ListView, подключенный к Adapter, пытается обновить себя до вызова этого метода. Когда это происходит, создается исключение 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 (). Этот объект очереди может иметь элементы, добавленные или удаленные из любого числа потоков.

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

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

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

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

Ответы [ 2 ]

8 голосов
/ 25 апреля 2010

Этот список / может / обновляться с другой поток, кроме пользовательского интерфейса резьба (основная)

Это не сработает.

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

MVC не имеет ничего общего с потоками.

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

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

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

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

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

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

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

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

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

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

Итак, я предполагаю, что это хороший способ установить адаптер?

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

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

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

передать объекту очереди обработчик Пользовательский интерфейс потока, когда он создан, и сделать все модификации списка, использующие это Обработчик, но это кажется неправильным

Вы можете использовать Handler, или вы можете позвонить post() на подключенном ListView.

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

0 голосов
/ 13 февраля 2011

Общий хороший совет: http://developer.android.com/resources/articles/painless-threading.html

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

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

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

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.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...