我正在開發一個Android應用程序,用於顯示大量RSS源(是的,我知道這裏有很多這樣的應用程序)。要顯示的數據由內容提供商提供支持,並且我希望向後兼容API級別4.在UI線程上使用ExpandableListView/SimpleCursorTreeAdapter運行SQLite查詢
我正在使用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(偉大的新功能/工具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
的JavaDoc(API等級4)(超一流的SimpleCursorTreeAdapter
)和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模擬器的應用程序,並啓動它。它像一個魅力,再見不響應的UI!但喜悅並沒有持續多久......接下來要做的是在目標設備上運行相同的代碼(在這種情況下,運行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
實現才能夠異步刷新子游標?
有沒有人有同樣的問題,甚至找到了(可行的)解決方法?如果沒有,是否有人可以告訴我如何繼續!? 我真的很感謝所有幫助我可以得到!
在此先感謝!
問候, 雅各
我在做類似的東西這裏http://stackoverflow.com/questions/10611927/simplecursortreeadapter-and-cursorloader – toobsco42 2012-05-16 05:12:53