2011-01-12 13 views
3

多くのRSSフィードを表示するためのAndroidアプリの開発が進行中です(このようなアプリが多数あります)。表示されるデータはコンテンツプロバイダによってサポートされており、APIレベル4と下位互換性があります。ExpandableListView/SimpleCursorTreeAdapterを使用してUIスレッドでSQLiteクエリを実行

私はExpandableListViewを使用して、3種類の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を返します。

RefreshChildrenCursorTaskは次のように実装されて
@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; 
} 

、:

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を実行しているSamsung 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()から非同期に返されるヌルに対応できるという方法はありませんと言われます。彼らはAndroid 2.3でこの問題を解決したようですが、Androidの以前のバージョンではどうやって回避していますか?子カーソルを非同期にリフレッシュできるようにするには、実際にCursorTreeAdapterという独自の実装を作成する必要がありますか?

誰も同じ問題を抱えていて、おそらく(実現可能な)回避策が見つかったことがありますか?そうでなければ、誰でも私に進める方法のヒントを教えてもらえますか? 私は本当に助けていただければ幸いです私は得ることができます!

ありがとうございます!

よろしく、 ヤコブ

+0

私はここに似た何かをやっているhttp://stackoverflow.com/questions/10611927/simplecursortreeadapter-and-cursorloader – toobsco42

答えて

2

彼らはあなただけのjavaファイルをダウンロードしてプロジェクトに含めると、電話でそのバージョンではなく、いずれかを使用しない理由2.3 SimpleCursorTreeAdapter、それを固定した場合は?

私はコードを見ていませんが、新しい2.3 APIコールを作成していない限り、動作するはずです。

+0

ちょうど私がしかし、私はすでにCursorTreeAdapterから継承したカスタムアダプターを使用していた、同じような機能を必要なことに注意したいですSimpleCursorTreeAdapterを使用するのではなく、 getChildrenCursor()からヌルを返す私のコードは、私の2.0エミュレータと私のGalaxy Tab(2.2)エミュレータで正常に動作するようです。カスタムアダプタを作成するのが解決策です。 – mp2526

2

フィードバックいただきありがとうございます、mp2526!私は私の遅い応答をお詫び申し上げます!

これはすばらしい提案です。しかし、私は本当にこれを行うことが可能であるべきだと思っていました(子カーソルを非同期的に取り出す)API 8で、私は何か誤解していました...明らかに、それは不可能です(少なくともSimpleCursorTreeAdapterを使わないで) !

とにかく、私はSimpleCursorTreeAdapterためのAPI 8とAPI 9間の差分を作って、どのような彼らは、Android 2.3で行っていることはchildFromgroupFrom列IDが今、すなわち、もはやコンストラクタでは、遅延初期化されているということではありません。代わりに、これらのメンバーはbindView()への最初の呼び出しで初期化されます。そうすれば、子カーソルを非同期に取得する時間があります。 API 4とAPI 9(新しいメソッドsetViewText()を除く)の間でこのクラスのパブリックAPIに変更がないようであるため、このクラスのAPIバージョン9を使用するソリューションは変更なしで動作するはずです。私はそれを試してみました。

ありがとう、mp2526!