У меня есть приложение, которое работает на эмуляторе, но периодически падает при тестировании на реальном устройстве. Я использую модифицированную версию отчета об ошибках кода 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 миллисекунд. Может быть, это исправление, а может и нет.