我正在开发一款Android应用程序,用于显示多个RSS源(是的,我知道已经有很多类似的应用程序了)。要显示的数据由内容提供程序支持,并且我希望向后兼容API级别4。
我使用一个“ExpandableListView”来显示三个不同的RSS源的内容。 “ExpandableListView”的适配器是作为“SimpleCursorTreeAdapter”的子类实现的:
使用这种设置,所有内容都按预期加载。然而,在加载列表内容时,UI会随机挂起/卡顿。通常不足以导致ANR,但仍然非常明显和令人恼火!
为了调试此问题,我启用了android.os.StrictMode(非常好的新功能/工具!),并启动了模拟器(在Android 2.3上)。当列表内容被加载时,我得到以下StrictMode输出:
严格模式违规; ~持续时间=1522毫秒:android.os.StrictMode $ StrictModeDiskReadViolation: policy = 23 violation = 2 在android.os.StrictMode $ AndroidBlockGuardPolicy.onReadFromDisk(StrictMode.java:745)处 在android.database.sqlite.SQLiteDatabase.rawQueryWithFactory(SQLiteDatabase.java:1345)处 在android.database.sqlite.SQLiteQueryBuilder.query(SQLiteQueryBuilder.java:330)处 在com.mycompany.myapp.provider.RSSFeedProvider.query(RSSFeedProvider.java:128)处 在android.content.ContentProvider $ Transport.query(ContentProvider.java:187)处 在android.content.ContentResolver.query(ContentResolver.java:262)处 在android.app.Activity.managedQuery(Activity.java:1550)处 在com.mycompany.myapp.activity.MultipleFeedsActivity $ RssFeedLatestListAdapter.getChildrenCursor(MultipleFeedsActivity.java:388)处 在android.widget.CursorTreeAdapter.getChildrenCursorHelper(CursorTreeAdapter.java:106)处 在android.widget.CursorTreeAdapter.getChildrenCount(CursorTreeAdapter.java:178)处 在android.widget.ExpandableListConnector.refreshExpGroupMetadataList(ExpandableListConnector.java:561)处 在android.widget.ExpandableListConnector.expandGroup(ExpandableListConnector.java:682)处 在android.widget.ExpandableListConnector.expandGroup(ExpandableListConnector.java:636)处 在android.widget.ExpandableListView.expandGroup(ExpandableListView.java:608)处 在com.mycompany.myapp.activity.MultipleFeedsActivity.onReceiveResult(MultipleFeedsActivity.java:335)处 在com.mycompany.myapp.service.FeedResultReceiver.onReceiveResult(FeedResultReceiver.java:40)处 在android.os.ResultReceiver$MyRunnable.run(ResultReceiver.java:43)处 在android.os.Handler.handleCallback(Handler.java:587)处 在android.os.Handler.dispatchMessage(Handler.java:92)处 在android.os.Looper.loop(Looper.java:123)处 在android.app.ActivityThread.main(ActivityThread.java:3647)处 在java.lang.reflect.Method.invokeNative(Native Method)处 在java.lang.reflect.Method.invoke(Method.java:507)处 在com.android.internal.os.ZygoteInit $ MethodAndArgsCaller.run(ZygoteInit.java:839)处 在com.android.internal.os.ZygoteInit.main(ZygoteInit.java:597)处 在dalvik.system.NativeStart.main(Native Method)处
这看起来很合理!在
我查看了
“如果要异步查询提供程序以防止阻塞UI,则可以返回null,并在稍后调用setChildrenCursor(int,Cursor)。”
太好了!让我们这样做!我更改了
我在2.3模拟器上重新安装了应用并启动它。它像魔法般地运行良好,再见不响应的用户界面!但是喜悦没能持续太久...接下来要做的事情是在目标设备(在本例中是运行Android 2.2的三星Galaxy S)上运行相同的代码。在加载列表提要内容时,我得到了以下
有人遇到过同样的问题,并且可能找到了(可行的)解决方法吗?如果没有,有人可以给我提示怎么继续吗?我真的非常感激任何帮助!
提前感谢!
问候, Jacob
我使用一个“ExpandableListView”来显示三个不同的RSS源的内容。 “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(非常好的新功能/工具!),并启动了模拟器(在Android 2.3上)。当列表内容被加载时,我得到以下StrictMode输出:
严格模式违规; ~持续时间=1522毫秒:android.os.StrictMode $ StrictModeDiskReadViolation: policy = 23 violation = 2 在android.os.StrictMode $ AndroidBlockGuardPolicy.onReadFromDisk(StrictMode.java:745)处 在android.database.sqlite.SQLiteDatabase.rawQueryWithFactory(SQLiteDatabase.java:1345)处 在android.database.sqlite.SQLiteQueryBuilder.query(SQLiteQueryBuilder.java:330)处 在com.mycompany.myapp.provider.RSSFeedProvider.query(RSSFeedProvider.java:128)处 在android.content.ContentProvider $ Transport.query(ContentProvider.java:187)处 在android.content.ContentResolver.query(ContentResolver.java:262)处 在android.app.Activity.managedQuery(Activity.java:1550)处 在com.mycompany.myapp.activity.MultipleFeedsActivity $ RssFeedLatestListAdapter.getChildrenCursor(MultipleFeedsActivity.java:388)处 在android.widget.CursorTreeAdapter.getChildrenCursorHelper(CursorTreeAdapter.java:106)处 在android.widget.CursorTreeAdapter.getChildrenCount(CursorTreeAdapter.java:178)处 在android.widget.ExpandableListConnector.refreshExpGroupMetadataList(ExpandableListConnector.java:561)处 在android.widget.ExpandableListConnector.expandGroup(ExpandableListConnector.java:682)处 在android.widget.ExpandableListConnector.expandGroup(ExpandableListConnector.java:636)处 在android.widget.ExpandableListView.expandGroup(ExpandableListView.java:608)处 在com.mycompany.myapp.activity.MultipleFeedsActivity.onReceiveResult(MultipleFeedsActivity.java:335)处 在com.mycompany.myapp.service.FeedResultReceiver.onReceiveResult(FeedResultReceiver.java:40)处 在android.os.ResultReceiver$MyRunnable.run(ResultReceiver.java:43)处 在android.os.Handler.handleCallback(Handler.java:587)处 在android.os.Handler.dispatchMessage(Handler.java:92)处 在android.os.Looper.loop(Looper.java:123)处 在android.app.ActivityThread.main(ActivityThread.java:3647)处 在java.lang.reflect.Method.invokeNative(Native Method)处 在java.lang.reflect.Method.invoke(Method.java:507)处 在com.android.internal.os.ZygoteInit $ MethodAndArgsCaller.run(ZygoteInit.java:839)处 在com.android.internal.os.ZygoteInit.main(ZygoteInit.java:597)处 在dalvik.system.NativeStart.main(Native Method)处
这看起来很合理!在
SimpleCursorTreeAdapter.getChildrenCursor()
中,内容提供者在UI线程上被查询,这当然是一个坏主意,肯定会挂起UI!我查看了
CursorTreeAdapter
的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)上运行相同的代码。在加载列表提要内容时,我得到了以下
Exception
:
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
实现才能异步刷新子游标吗?有人遇到过同样的问题,并且可能找到了(可行的)解决方法吗?如果没有,有人可以给我提示怎么继续吗?我真的非常感激任何帮助!
提前感谢!
问候, Jacob