Listview notifydatachanged () временная проблема - PullRequest
1 голос
/ 23 декабря 2011

У меня есть приложение, которое работает на эмуляторе, но периодически падает при тестировании на реальном устройстве. Я использую модифицированную версию отчета об ошибках кода Android. (http://jyro.blogspot.com/2009/09/crash-report-for-android-app.html) Отправленная по электронной почте причина состояния:

Содержимое адаптера изменилось, но ListView не получил уведомление. Убедитесь, что содержимое вашего адаптера не изменено из фонового потока, но только из потока пользовательского интерфейса. [в ListView (2131296303, класс android.widget.ListView) с адаптером (класс android.widget.SimpleAdapter)]

Код не изменяет адаптер вне основного потока пользовательского интерфейса. Весь доступ к адаптеру осуществляется через onPostExecute AsyncTask. Код:

    @Override
    protected void onPostExecute(String result) {
        MyLog.d(TAG, "onPostExecute");
        foodDescArrayList.clear();
        int entries = holdFoodDescArrayList.size();
        HashMap<String, String> listEntry;
        for (int i = 0; i < entries; i++) {
            listEntry = new HashMap<String, String>();
            listEntry = holdFoodDescArrayList.get(i);
            foodDescArrayList.add(listEntry);
        }
        holdFoodDescArrayList.clear();
        hideProgress();
        foodDescAdapter.notifyDataSetChanged();
        String ents = " entries";
        if (rowsReturned == 1) {
            ents = " entry";
        }
        foodDescHeader.setText("Page " + Integer.toString(currentPage + 1)
                + " of " + Integer.toString(pageCount) + " (" + Integer.toString(rowsReturned) + ents + ")");
        loadActive = false;
    }

HoldFoodDescArrayList - это автономный список, заполненный фоновой задачей из базы данных SQLite. foodDescArrayList - это массив, связанный с адаптером ListView. (Я обнаружил, что при постановке в очередь повышается производительность. Может быть, потому что адаптер отключен во время доступа к базе данных. Меньше накладных расходов?)

Сбой всегда происходит при первом входе (onCreate) после выхода из главного ключа, а затем при повторном входе в действие верхнего уровня, которое вызывает действие списка. Время между сбоями составляет от 30 минут до 2 часов при непрерывном тестировании. Код, который дает сбой, был пройден сотни раз и является линейным без выходов.

Единственная возможная дыра, которую я могу найти при просмотре кода - это clear (), предшествующая загрузке массива. Функция очистки действует как одно изменение, а группа добавлений - как второе изменение? Есть ли временные соображения? В списке от 1 до 24 записей, поэтому загрузка должна занимать миллисекунды, а не секунды ...

Я ищу идеи и подсказки. Пожалуйста, отсканируйте код и посмотрите, нет ли в коде явной ошибки или побочного эффекта. Это единственное место, где данные массива ListView изменяются в приложении. Фоновый код изменяет только массив удержания.

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

----------- Обновление для добавления запрошенного кода ----------

Код в onCreate:

    setContentView(R.layout.fooddeslist);
    foodDescHeader = (TextView) findViewById(R.id.foodDescHeading);
    foodDescListView = (ListView) findViewById(R.id.foodDescList);
    foodDescArrayList = new ArrayList<HashMap<String, String>>();
    holdFoodDescArrayList = new ArrayList<HashMap<String, String>>();
    foodDescAdapter = new SimpleAdapter(this, foodDescArrayList,
            R.layout.longdescitem, new String[] { GC.FOODDESCLIST_LINE1,
                    GC.FOODDESCLIST_LINE2 },
            new int[] { R.id.longdescListItemLine1,
                    R.id.longdescListItemLine2 });
    foodDescListView.setAdapter(foodDescAdapter);
    registerForContextMenu(foodDescListView);

Записи массива:

    listEntry = new HashMap<String, String>();

Массив hold является дубликатом массива адаптера. Удерживающий массив загружается в функцию doInBackground в AsyncTask. Я бы показал фоновый код, но это около 500 строк кода. Конечный результат состоит в том, что массив Hold загружается с 2 строками для отображения и вспомогательными данными, уникальными для каждой записи. Идентификаторы строк для различных битов данных в других таблицах, которые используются при выборе записи. Дублирующиеся таблицы позволяют мне загружать массив в течение нескольких десятков (гигагерцевых процессоров). Передача в исполняемом посте UI выполняется всего за пару миллисекунд.

longdescitem.xml - это:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
     android:orientation="vertical">

     <TextView android:id="@+id/longdescListItemLine1"
        android:textStyle="italic"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textColor="#ff9900"
        />
     <TextView android:id="@+id/longdescListItemLine2"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:textColor="#b89300"

         />
</LinearLayout>

R.layout.fooddesclist - это:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <TextView
        android:id="@+id/foodDescHeading"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        android:layout_centerHorizontal="true"
        android:layout_marginLeft="2dip"
        android:text="Page 1 of 10 (nnn entries)"
        android:textAppearance="?android:attr/textAppearanceSmall"
        android:textColor="#b89300" />

    <ImageButton
        android:id="@+id/foodDescForward"
        android:layout_width="50px"
        android:layout_height="50px"
        android:layout_alignParentRight="true"
        android:layout_alignParentTop="true"
        android:layout_marginRight="12dip"
        android:layout_marginTop="5dip"
        android:background="@drawable/forward"
        android:clickable="true"
        android:onClick="foodDescForwardClicked" />

    <ImageButton
        android:id="@+id/foodDescBack"
        android:layout_width="50px"
        android:layout_height="50px"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true"
        android:layout_marginLeft="12dip"
        android:layout_marginTop="5dip"
        android:background="@drawable/back"
        android:clickable="true"
        android:onClick="foodDescBackClicked" />

    <View
        android:id="@+id/foodDescSpacer1"
        android:layout_width="match_parent"
        android:layout_height="2dp"
        android:layout_alignParentLeft="true"
        android:layout_below="@id/foodDescForward"
        android:layout_marginBottom="2dip"
        android:layout_marginTop="2dip"
        android:background="@drawable/divider" />

    <ListView
        android:id="@+id/foodDescList"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_below="@id/foodDescSpacer1"
        android:layout_marginLeft="2dip"
        android:layout_marginRight="2dip"
        android:cacheColorHint="#00000000"
        android:divider="#b89300"
        android:dividerHeight="1.0px" />

</RelativeLayout>

-------- Другое обновление --------------

Я добавил некоторый код в функцию opPostExecute.

    protected void onPostExecute(String result) {
        MyLog.d(TAG, "onPostExecute");
        foodDescArrayList.clear();
        foodDescAdapter.notifyDataSetChanged();
        try {
            Thread.sleep(300);
        } catch (InterruptedException e) {
            MyLog.d(TAG, "Sleep failed: " + e.getMessage());
        }
        int entries = holdFoodDescArrayList.size();
        HashMap<String, String> listEntry;
        for (int i = 0; i < entries; i++) {
            listEntry = new HashMap<String, String>();
            listEntry = holdFoodDescArrayList.get(i);
            foodDescArrayList.add(listEntry);
        }
        holdFoodDescArrayList.clear();
        hideProgress();
        foodDescAdapter.notifyDataSetChanged();
        String ents = " entries";
        if (rowsReturned == 1) {
            ents = " entry";
        }
        foodDescHeader.setText("Page " + Integer.toString(currentPage + 1)
                + " of " + Integer.toString(pageCount) + " (" + Integer.toString(rowsReturned) + ents + ")");
        loadActive = false;
    }

После двух часов непрерывных испытаний на Atrix он еще не рухнул. Перед кодом я получал бы сбой, по крайней мере, один раз, а иногда и два раза за 2 часа. Я добавил notifydatasetchange после очистки и следовал за ним со сном 300 мсек. Я думаю, что операционная система наступила на ноги, потому что первоначальное уведомление потеряно. Хотя запросы к базе данных могут занимать до 8 секунд, в большинстве случаев это мгновенный ответ. Диалог прогресса никогда не имел возможности полностью настроить. Экран будет мерцать, а затем отобразится список. Отображение прогресса никогда не было идентифицируемо. (Диалоговое окно хода выполнения включено в функции preExecute.) Весь AsyncTask выполнялся менее чем за 300 миллисекунд. Может быть, это исправление, а может и нет.

1 Ответ

0 голосов
/ 10 января 2012

Я отвечаю на это сам, чтобы закрыть пост в качестве ответа.

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