3

多数の RSS フィードを表示するための Android アプリを開発中です (そうです、このようなアプリは既にたくさんあることを知っています)。表示するデータはコンテンツプロバイダーがバックアップしており、API レベル 4 との下位互換性を維持したい。

を使用して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 を有効にし (すばらしい新機能/ツールです!)、エミュレーターを起動しました (Android 2.3 で)。リストのコンテンツが読み込まれると、次の StrictMode 出力が得られます。

D/StrictMode(395): StrictMode ポリシー違反。~期間 = 1522 ミリ秒: android.os.StrictMode$StrictModeDiskReadViolation: ポリシー = 23 違反 = 2
D/StrictMode(395): android.os.StrictMode$AndroidBlockGuardPolicy.onReadFromDisk(StrictMode.java:745) で
D/StrictMode (395): android.database.sqlite.SQLiteDatabase.rawQueryWithFactory (SQLiteDatabase.java:1345) で
D/StrictMode(395): android.database.sqlite.SQLiteQueryBuilder.query(SQLiteQueryBuilder.java:330)
D/StrictMode (395): com.mycompany.myapp.provider.RSSFeedProvider.query (RSSFeedProvider.java:128) で
D/StrictMode(395): android.content.ContentProvider$Transport.query(ContentProvider.java:187) で
D/StrictMode(395): android.content.ContentResolver.query(ContentResolver.java:262) で
D/StrictMode(395): android.app.Activity.managedQuery(Activity.java:1550) で
D/StrictMode(395): com.mycompany.myapp.activity.MultipleFeedsActivity$RssFeedLatestListAdapter.getChildrenCursor(MultipleFeedsActivity.java:388)
D/StrictMode(395): android.widget.CursorTreeAdapter.getChildrenCursorHelper(CursorTreeAdapter.java:106) で
D/StrictMode(395): android.widget.CursorTreeAdapter.getChildrenCount(CursorTreeAdapter.java:178) で
D/StrictMode(395): android.widget.ExpandableListConnector.refreshExpGroupMetadataList(ExpandableListConnector.java:561) で
D/StrictMode(395): android.widget.ExpandableListConnector.expandGroup(ExpandableListConnector.java:682) で
D/StrictMode(395): android.widget.ExpandableListConnector.expandGroup(ExpandableListConnector.java:636) で
D/StrictMode(395): android.widget.ExpandableListView.expandGroup(ExpandableListView.java:608) で
D/StrictMode (395): com.mycompany.myapp.activity.MultipleFeedsActivity.onReceiveResult (MultipleFeedsActivity.java:335) で
D/StrictMode (395): com.mycompany.myapp.service.FeedResultReceiver.onReceiveResult (FeedResultReceiver.java:40) で
D/StrictMode(395): android.os.ResultReceiver$MyRunnable.run(ResultReceiver.java:43) で
D/StrictMode(395): android.os.Handler.handleCallback(Handler.java:587) で
D/StrictMode(395): android.os.Handler.dispatchMessage(Handler.java:92) で
D/StrictMode(395): android.os.Looper.loop(Looper.java:123)
D/StrictMode(395): android.app.ActivityThread.main(ActivityThread.java:3647) で
D/StrictMode(395): java.lang.reflect.Method.invokeNative(ネイティブ メソッド) で
D/StrictMode(395): java.lang.reflect.Method.invoke(Method.java:507) で
D/StrictMode(395): com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:839) で
D/StrictMode (395): com.android.internal.os.ZygoteInit.main (ZygoteInit.java:597) で
D/StrictMode(395): dalvik.system.NativeStart.main(ネイティブメソッド)

これは合理的なようです!ではSimpleCursorTreeAdapter.getChildrenCursor()、コンテンツ プロバイダは UI スレッドで照会されますが、これはもちろん悪い考えであり、UI を確実にハングさせます。

(のスーパークラス)のJavaDoc(APIレベル4)を見て、次のように述べています。 CursorTreeAdapterSimpleCursorTreeAdaptergetChildrenCursor()

「UI のブロックを防ぐためにプロバイダーに非同期的にクエリを実行する場合は、null を返し、後で setChildrenCursor(int, Cursor) を呼び出すことができます。」.

すごい!それをしましょう!の実装を変更getChildrenCursor()して、クエリの実行とアダプターでの子カーソルの設定を担当する新しいタスクを開始します。タスクを開始した後、次のように null を返しますgetChildrenCursor()

@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 を実行する Samsung Galaxy S) で同じコードを実行することでした。次にException、リスト フィード コンテンツの読み込み中に次の情報を取得しました。

E/AndroidRuntime(23191): 致命的な例外: メイン
E/AndroidRuntime(23191): java.lang.NullPointerException
E/AndroidRuntime(23191): android.widget.SimpleCursorTreeAdapter.initFromColumns(SimpleCursorTreeAdapter.java:194) で
E/AndroidRuntime(23191): android.widget.SimpleCursorTreeAdapter.initChildrenFromColumns(SimpleCursorTreeAdapter.java:205) で
E/AndroidRuntime(23191): android.widget.SimpleCursorTreeAdapter.init(SimpleCursorTreeAdapter.java:186) で
E/AndroidRuntime(23191): android.widget.SimpleCursorTreeAdapter.(SimpleCursorTreeAdapter.java:136) で
E/AndroidRuntime (23191): com.mycompany.myapp.activity.MultipleFeedsActivity$RssFeedLatestListAdapter.(MultipleFeedsActivity.java:378) で
E/AndroidRuntime (23191): com.mycompany.myapp.activity.MultipleFeedsActivity$2.run (MultipleFeedsActivity.java:150) で
E/AndroidRuntime(23191): android.os.Handler.handleCallback(Handler.java:587) で
E/AndroidRuntime(23191): android.os.Handler.dispatchMessage(Handler.java:92) で
E/AndroidRuntime(23191): android.os.Looper.loop(Looper.java:123)
E/AndroidRuntime(23191): android.app.ActivityThread.main(ActivityThread.java:4627) で
E/AndroidRuntime(23191): java.lang.reflect.Method.invokeNative(ネイティブ メソッド) で
E/AndroidRuntime(23191): java.lang.reflect.Method.invoke(Method.java:521) で
E/AndroidRuntime(23191): com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:871) で
E/AndroidRuntime (23191): com.android.internal.os.ZygoteInit.main (ZygoteInit.java:629) で
E/AndroidRuntime(23191): dalvik.system.NativeStart.main(ネイティブメソッド)

のソース コードをざっと見てみると、SimpleCursorTreeAdapterから非同期に返される null に対処する方法がないことがわかりますgetChildrenCursor()。彼らは Android 2.3 でこの問題を解決したようですが、以前のバージョンの Android ではどのように回避すればよいのでしょうか? CursorTreeAdapter子カーソルを非同期的に更新できるようにするには、独自の実装を作成する必要がありますか?

誰かが同じ問題を抱えていて、おそらく (実行可能な) 回避策を見つけたことがありますか? そうでない場合は、続行する方法についてヒントを教えてもらえますか!? 私が得ることができる助けを本当に感謝します

前もって感謝します!

よろしく、ジェイコブ

4

2 に答える 2

2

2.3 SimpleCursorTreeAdapter で修正された場合は、Java ファイルをダウンロードしてプロジェクトに含め、電話のバージョンではなくそのバージョンを使用してみませんか?

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

于 2011-01-21T03:43:26.037 に答える
2

mp2526 さん、フィードバックありがとうございます。返事が遅くなってすみません!

それは素晴らしい提案です!しかし、私は API 8 でこれを行う (子カーソルを非同期的に取得する) ことが可能であるべきだと本当に思っていましたSimpleCursorTreeAdapter

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

どうもありがとう、mp2526!

于 2011-01-26T12:46:35.603 に答える