android-SQLite查询使用ExpandableListView / SimpleCursorTreeAdapter在UI线程上运行
作者:互联网
我正在开发用于显示大量RSS提要的Android应用程序(是的,我知道已经有很多这样的应用程序).内容提供者支持要显示的数据,我想与API级别4向后兼容.
我正在使用ExpandableListView来显示三个不同的RSS feed的内容. ExpandableListView的适配器被实现为SimpleCursorTreeAdapter的子类:
private class RssFeedLatestListAdapter extends SimpleCursorTreeAdapter {
public static final String FEED_NAME_COLUMN = "feedName";
public RssFeedLatestListAdapter(Context ctx, Cursor groupCursor, int groupLayout,
String[] groupFrom, int[] groupTo, int childLayout, String[] childFrom,
int[] childTo) {
super(ctx, groupCursor, groupLayout, groupLayout, groupFrom, groupTo, childLayout, childFrom, childTo);
}
@Override
protected Cursor getChildrenCursor(final Cursor groupCursor) {
final String feedName = groupCursor.getString(groupCursor.getColumnIndex(FEED_NAME_COLUMN));
return managedQuery(LATEST_URI, null,
FeedItemColumns.CATEGORY + " = ? AND " + FeedItemColumns.FEED + " = ?",
new String[] { mCategory.getId(), feedName }, null);
}
@Override
protected void bindGroupView(View view, Context context, Cursor cursor, boolean isExpanded) {
super.bindGroupView(view, context, cursor, isExpanded);
// Bind group view (impl details not important) ...
}
@Override
protected void bindChildView(View view, Context context, Cursor cursor, boolean isLastChild) {
super.bindChildView(view, context, cursor, isLastChild);
// Bind child view (impl details not important) ...
}
}
使用此设置,所有内容均按预期加载.但是,UI在列表内容加载期间随机挂起/结结.通常不足以获取ANR,但仍然很引人注目且非常烦人!
为了调试此问题,我启用了android.os.StrictMode(很棒的新功能/工具btw!),并启动了模拟器(在Android 2.3上).加载列表内容时,将获得以下StrictMode输出:
D/StrictMode( 395): StrictMode policy violation; ~duration=1522 ms: android.os.StrictMode$StrictModeDiskReadViolation: policy=23 violation=2 D/StrictMode( 395): at android.os.StrictMode$AndroidBlockGuardPolicy.onReadFromDisk(StrictMode.java:745) D/StrictMode( 395): at android.database.sqlite.SQLiteDatabase.rawQueryWithFactory(SQLiteDatabase.java:1345) D/StrictMode( 395): at android.database.sqlite.SQLiteQueryBuilder.query(SQLiteQueryBuilder.java:330) D/StrictMode( 395): at com.mycompany.myapp.provider.RSSFeedProvider.query(RSSFeedProvider.java:128) D/StrictMode( 395): at android.content.ContentProvider$Transport.query(ContentProvider.java:187) D/StrictMode( 395): at android.content.ContentResolver.query(ContentResolver.java:262) D/StrictMode( 395): at android.app.Activity.managedQuery(Activity.java:1550) D/StrictMode( 395): at com.mycompany.myapp.activity.MultipleFeedsActivity$RssFeedLatestListAdapter.getChildrenCursor(MultipleFeedsActivity.java:388) D/StrictMode( 395): at android.widget.CursorTreeAdapter.getChildrenCursorHelper(CursorTreeAdapter.java:106) D/StrictMode( 395): at android.widget.CursorTreeAdapter.getChildrenCount(CursorTreeAdapter.java:178) D/StrictMode( 395): at android.widget.ExpandableListConnector.refreshExpGroupMetadataList(ExpandableListConnector.java:561) D/StrictMode( 395): at android.widget.ExpandableListConnector.expandGroup(ExpandableListConnector.java:682) D/StrictMode( 395): at android.widget.ExpandableListConnector.expandGroup(ExpandableListConnector.java:636) D/StrictMode( 395): at android.widget.ExpandableListView.expandGroup(ExpandableListView.java:608) D/StrictMode( 395): at com.mycompany.myapp.activity.MultipleFeedsActivity.onReceiveResult(MultipleFeedsActivity.java:335) D/StrictMode( 395): at com.mycompany.myapp.service.FeedResultReceiver.onReceiveResult(FeedResultReceiver.java:40) D/StrictMode( 395): at android.os.ResultReceiver$MyRunnable.run(ResultReceiver.java:43) D/StrictMode( 395): at android.os.Handler.handleCallback(Handler.java:587) D/StrictMode( 395): at android.os.Handler.dispatchMessage(Handler.java:92) D/StrictMode( 395): at android.os.Looper.loop(Looper.java:123) D/StrictMode( 395): at android.app.ActivityThread.main(ActivityThread.java:3647) D/StrictMode( 395): at java.lang.reflect.Method.invokeNative(Native Method) D/StrictMode( 395): at java.lang.reflect.Method.invoke(Method.java:507) D/StrictMode( 395): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:839) D/StrictMode( 395): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:597) D/StrictMode( 395): at dalvik.system.NativeStart.main(Native Method)
这似乎是合理的!在SimpleCursorTreeAdapter.getChildrenCursor()中,将在UI线程上查询内容提供者,这当然是一个坏主意,它肯定会挂断UI!
我看了一下CursorTreeAdapter(SimpleCursorTreeAdapter的超类)的JavaDoc(API级别4),对于getChildrenCursor()它说:
“如果要异步查询提供程序以防止阻塞UI,则可以返回null,并在以后调用setChildrenCursor(int,Cursor).”
大!让我们开始吧!我更改了getChildrenCursor()的实现,以启动一个新任务,该任务负责执行查询并在适配器上设置子级光标.开始任务后,我只是在getChildrenCursor()中返回null:
@Override
protected Cursor getChildrenCursor(final Cursor groupCursor) {
final String feedName = groupCursor.getString(groupCursor.getColumnIndex(FEED_NAME_COLUMN));
new RefreshChildrenCursorTask(groupCursor.getPosition()).execute(feedName);
return null;
}
,其中RefreshChildrenCursorTask实现为:
private class RefreshChildrenCursorTask extends AsyncTask<String, Void, Cursor> {
private int mGroupPosition;
public RefreshChildrenCursorTask(int groupPosition) {
this.mGroupPosition = groupPosition;
}
@Override
protected Cursor doInBackground(String... params) {
String feedName = params[0];
return managedQuery(LATEST_URI, null,
FeedItemColumns.CATEGORY + " = ? AND " + FeedItemColumns.FEED + " = ?",
new String[] { mCategory.getId(), feedName }, null);
}
@Override
protected void onPostExecute(Cursor childrenCursor) {
mLatestListAdapter.setChildrenCursor(mGroupPosition, childrenCursor);
}
}
}
我在2.3模拟器上重新安装了该应用程序并启动了它.它像魅力一样,再见了无响应的用户界面!但是这种欢乐并没有持续很长时间……接下来要做的是在目标设备上运行相同的代码(在本例中为运行Android 2.2的三星Galaxy S).然后,我在加载列表供稿内容期间遇到了以下异常:
E/AndroidRuntime(23191): FATAL EXCEPTION: main E/AndroidRuntime(23191): java.lang.NullPointerException E/AndroidRuntime(23191): at android.widget.SimpleCursorTreeAdapter.initFromColumns(SimpleCursorTreeAdapter.java:194) E/AndroidRuntime(23191): at android.widget.SimpleCursorTreeAdapter.initChildrenFromColumns(SimpleCursorTreeAdapter.java:205) E/AndroidRuntime(23191): at android.widget.SimpleCursorTreeAdapter.init(SimpleCursorTreeAdapter.java:186) E/AndroidRuntime(23191): at android.widget.SimpleCursorTreeAdapter.(SimpleCursorTreeAdapter.java:136) E/AndroidRuntime(23191): at com.mycompany.myapp.activity.MultipleFeedsActivity$RssFeedLatestListAdapter.(MultipleFeedsActivity.java:378) E/AndroidRuntime(23191): at com.mycompany.myapp.activity.MultipleFeedsActivity$2.run(MultipleFeedsActivity.java:150) E/AndroidRuntime(23191): at android.os.Handler.handleCallback(Handler.java:587) E/AndroidRuntime(23191): at android.os.Handler.dispatchMessage(Handler.java:92) E/AndroidRuntime(23191): at android.os.Looper.loop(Looper.java:123) E/AndroidRuntime(23191): at android.app.ActivityThread.main(ActivityThread.java:4627) E/AndroidRuntime(23191): at java.lang.reflect.Method.invokeNative(Native Method) E/AndroidRuntime(23191): at java.lang.reflect.Method.invoke(Method.java:521) E/AndroidRuntime(23191): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:871) E/AndroidRuntime(23191): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:629) E/AndroidRuntime(23191): at dalvik.system.NativeStart.main(Native Method)
快速浏览一下SimpleCursorTreeAdapter的源代码可知,它无法应付从getChildrenCursor()异步返回的null.他们似乎已经在Android 2.3中解决了这个问题,但是您应该如何在Android的早期版本中解决它呢?我是否真的必须编写自己的CursorTreeAdapter实现才能异步刷新子游标?
是否有人遇到过同样的问题,甚至找到了(可行的)解决方法?如果没有,有人可以提示我如何进行!!我会很感激我能得到的任何帮助!
提前致谢!
问候,
雅各布
解决方法:
如果他们在2.3 SimpleCursorTreeAdapter中进行了修复,为什么不下载Java文件并将其包含在项目中并使用该版本而不是电话上的版本呢?
我没有看过代码,但是只要他们没有进行任何新的2.3 API调用,它就应该起作用.
标签:ui-thread,cursor,expandablelistview,android 来源: https://codeday.me/bug/20191105/1995749.html