13

Android developer exampleに基づいて、ActionBar タブを使用してタブをスワイプするアクティビティがあります。

各タブには Fragment が表示され、各 Fragment (実際には SherlockFragment) は、カスタム AsyncTaskLoader を介して異なる種類のリモート API 要求を読み込みます。

問題は、移動しようとしているタブのフラグメント (古いフラグメント) が結果をロードしている間にタブをタップして 2 つのタブ/ページを移動すると、その結果が移動先のタブのフラグメント (新しいフラグメント)。私の場合、予想される結果が互換性のない型であるため、これは ClassCastException につながります。

コードでは、状況の要点は次のとおりです。

ローダー:

public class FooLoader extends AsyncTaskLoader<Foo>
public class BarLoader extends AsyncTaskLoader<Bar>

フラグメント:

public class FooFragment extends Fragment implements LoaderManager.LoaderCallbacks<Foo> {
...
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        getLoaderManager().initLoader(0, null, this);
    }
    public Loader<Foo> onCreateLoader(int id, Bundle args) { return new FooLoader(); }
...
}
public class BarFragment extends Fragment implements LoaderManager.LoaderCallbacks<Bar> {
    ...
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        getLoaderManager().initLoader(0, null, this);
    }
    public Loader<Bar> onCreateLoader(int id, Bundle args) { return new BarLoader(); }
    ...
}

タブ管理コードは前述の例のとおりです。Foo タブと Bar タブの間に 3 番目のタブがあります (Baz と呼びます)。FooFragment が LoaderManager で initLoader を呼び出した後、FooFragment.onLoadFinished が呼び出される前に、Bar タブをタップして Foo タブから Bar タブにスキップすると、BarFragment.onLoadFinished の呼び出しで ClassCastException が発生します。

java.lang.ClassCastException: com.example.Foo cannot be cast to com.example.Bar
at com.example.BarFragment.onLoadFinished(BarFragment.java:1)
at android.support.v4.app.LoaderManagerImpl$LoaderInfo.callOnLoadFinished(LoaderManager.java:427)
at android.support.v4.app.LoaderManagerImpl.initLoader(LoaderManager.java:562)
at com.example.BarFragment.onCreate(BarFragment.java:36)
at android.support.v4.app.Fragment.performCreate(Fragment.java:1437)
...

なぜこれが起こっているのでしょうか?どうすれば防ぐことができますか? 同じ LoaderManager が Bar フラグメントで再利用されているようにデバッグ ログから見えますが (Baz フラグメントには独自のものがあります)、なぜそれが発生するのかわかりません。

更新:各フラグメントで異なるローダー ID を使用すると、クラッシュが解消されます (またはそう思われます - 理由はよくわかりません) が、私はむしろこれを行いません。フラグメントの 1 つで、実際に ID を動的に作成し、衝突がないと仮定したくありません。また、その解決策は私には奇妙です-ローダーIDは各フラグメントに対してローカルである必要があります(そうでなければ、通常の状況で異なるフラグメントに同じIDを持つローダーを使用できるのはなぜですか?)

setOffscreenPageLimit(2)Bar ビューに切り替えたときに Foo ビューが破棄されないように、ViewPagerを呼び出してクラッシュを解消することもできるようです。ただし、これは回避策であり、一般的な解決策ではありません。

完全なコード:エラーを示すサンプル アプリケーションを作成しました。エラーを強制するmonkeyrunnerスクリプトが含まれています(ただし、すべての画面サイズで機能するとは限りません)。

4

3 に答える 3

10

IDとして0を使用しないでください。LoaderManager知っている限り、彼らは彼と同じローダーであることが意図されています。

XMLリソースファイルで一意のIDを定義できます

<item type="id" name="loader_foo" />
<item type="id" name="loader_bar" />

そして、Rからそれらにアクセスします。

loaderManager.initLoader(R.id.loader_foo, null, new LoaderCallbacks(){});

のドキュメントにLoaderManagerは、「識別子は特定のLoaderManagerインスタンスにスコープされています」と記載されています。そして、のインスタンスはLoaderManangerに関連付けられていActivitiesます。

一意のIDを生成するために、それらの間に大きなギャップがあるIDを手動で割り当てることができます。

private static final int LOADER_FOO = 1;
private static final int LOADER_BAR = 100;

for(int i = 0; i < 10; ++i){
    loaderManager.initLoader(LOADER_FOO + i, null, new LoaderCallbacks(){});
}
于 2013-03-19T05:38:43.047 に答える
2

この問題は、質問のコメントでAlex Lockwoodが指摘したように、参加するのではなく電話initLoaderすることで回避できます。以下の変更されたコード。onActivityCreatedonCreate

修正されたフラグメント:

public class FooFragment extends Fragment implements LoaderManager.LoaderCallbacks<Foo> {
...
    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        getLoaderManager().initLoader(0, null, this);
    }
    public Loader<Foo> onCreateLoader(int id, Bundle args) { return new FooLoader(); }
...
}
public class BarFragment extends Fragment implements LoaderManager.LoaderCallbacks<Bar> {
    ...
    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        getLoaderManager().initLoader(0, null, this);
    }
    public Loader<Bar> onCreateLoader(int id, Bundle args) { return new BarLoader(); }
    ...
}
于 2013-03-21T21:16:54.990 に答える
0

最近、SimpleCursorAdapter とローダーを使用して、sql lite テーブルのデータを表示していました。「そのような列 '_id' はありません」というエラーのデバッグに長い時間を費やしました。これは、2 番目のフラグメントの onLoadFinished() で発生しました。最初のテーブルには「_id」列がありましたが、2 番目のテーブルにはありませんでした。そのため、ローダーが間違ったフラグメントに配信していると思われました。したがって、同様のことを行った場合に備えて、SQL テーブルに最初に「_id」列があることを確認してください。これが私と同じ問題を抱えている他の人の助けになることを願っています.

于 2014-04-29T17:10:53.173 に答える