2011-01-12 27 views
3

我正在開發一個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上!

我一起來看看CursorTreeAdapterJavaDoc(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實現才能夠異步刷新子游標?

有沒有人有同樣的問題,甚至找到了(可行的)解決方法?如果沒有,是否有人可以告訴我如何繼續!? 我真的很感謝所有幫助我可以得到!

在此先感謝!

問候, 雅各

+0

我在做類似的東西這裏http://stackoverflow.com/questions/10611927/simplecursortreeadapter-and-cursorloader – toobsco42 2012-05-16 05:12:53

回答

2

如果他們固定在2.3 SimpleCursorTreeAdapter,你爲什麼不只是下載Java文件,並將其包含在您的項目,並使用該版本,而不是在手機上的一個?

我還沒有看過代碼,但只要他們不做任何新的2.3 API調用,它應該可以工作。

+0

只是想要說明的是,我需要類似的功能,但是,我已經使用了一個自定義適配器繼承自CursorTreeAdapter而不是使用SimpleCursorTreeAdapter。我的代碼從getChildrenCursor()返回null似乎在我的2.0模擬器和我的Galaxy Tab(2.2)模擬器上工作正常。所以也許創建一個自定義適配器是解決方案。 – mp2526 2011-01-21 22:30:59

2

感謝您的反饋,mp2526!對於我遲到的迴應,我表示歉意!

這是一個很好的建議!但是,我真的認爲應該可以在API 8中這樣做(在異步調用中檢索兒童遊標),並且我誤解了任何東西......顯然,這是不可能的(至少不會使用SimpleCursorTreeAdapter) !

無論如何,我做了API 8和9 API之間的差異爲SimpleCursorTreeAdapter,和他們有什麼的Android 2.3做的是,childFromgroupFrom列ID現在初始化懶洋洋,即在構造函數中不再。相反,這些成員現在在第一次調用bindView()時被初始化。這樣,現在有時間異步檢索兒童遊標。由於在API 4和API 9之間(除了新方法setViewText())此類的公共API似乎沒有任何更改,所以使用此類的API版本9的解決方案應該可以無需更改!我試了一下,它做到了!

非常感謝,mp2526!

相關問題