web-dev-qa-db-ja.com

Android:LoaderCallbacks.OnLoadFinishedが2回呼び出される

Androidローダーとフラグメントを使用して奇妙な状況に気づきました。向きの変更後にLoaderManager.initLoader()を呼び出すと、onLoadFinishedが呼び出されません(ただし、ドキュメントはこれを準備する必要があると示唆していますが)この後、同じ状況を説明するGoogleグループに投稿するためのリンクがあります https://groups.google.com/forum/?fromgroups#!topic/Android-developers/aA2vHYxSsk 。サンプルを書きましたFragment.onActivityCreated()で単純なLoaderのみを初期化して、これが発生するかどうかを確認するアプリケーション。

52
LukaszS

InitLoader()メソッドをFragmentのonResume()コールバック内に配置できます。ローダーのonLoadFinished()はもう2回呼び出されません。

    @Override
public void onResume()
{
    super.onResume();
    getLoaderManager().initLoader(0, null, this);
}
38
Bogdan Zurac

この問題は、すでに閉じられているカーソルを返すCursorLoaderで明らかになりました。

Android.database.StaleDataException: Attempted to access a cursor after it has been closed.

これはバグか見落としだと思います。 initLoader()をonResumeに移動すると動作する可能性がありますが、できたのはローダーを削除することでした。

ローダーを起動するには(onCreateで):

  getLoaderManager().initLoader(MUSIC_LOADER_ID, null, this);

その後、私はそれをやった後(基本的にonLoadFinishedの終わりに)

  getLoaderManager().destroyLoader(MUSIC_LOADER_ID);

これは予想通りに動作するようで、余分な呼び出しはありません。

26
Matt

initLoader documentation 言う、

呼び出し時点で呼び出し元が開始状態にあり、要求されたローダーが既に存在し、データを生成している場合、コールバックonLoadFinished(Loader、D)

これでonStartLoading関数のようなものを実装することをお勧めします sample

簡単なテストのために試すことができます:

@Override protected void onStartLoading() {
    forceLoad();
}

これにより、loadInBackground関数が起動され、次にFragmentでonLoadFinishedが起動されます。

いずれにせよ、もしあなたが何らかのコードを添付したら、私はあなたにもっと助けを与えようとします。

6
jperera

このように2回呼び出されるonLoadFinishedの問題を解決しました。 Fragment.onActivityCreated()で、このようにローダーを初期化します

if (getLoaderManager().getLoader(LOADER_ID) == null) {
    getLoaderManager().initLoader(LOADER_ID, bundle, loaderCallbacks);
} else {
    getLoaderManager().restartLoader(LOADER_ID, bundle, loaderCallbacks);

}

loaderCallbacksは通常のローダーコールバックを実装します

private LoaderManager.LoaderCallbacks<T> loaderCallbacks
        = new LoaderManager.LoaderCallbacks<T>() {
    @Override
    public Loader<T> onCreateLoader(int id, Bundle args) {
        ...
        ...
    }

    @Override
    public void onLoadFinished(Loader<T> loader, T data) {
        ...
        ...
    }

    @Override
    public void onLoaderReset(Loader<T> loader) {
        ...
        ...
    }
};
5
Amrendra Kumar

問題は、2回呼び出されることです。
1。 Fragment.onStartから
2。 FragmentActivity.onStartから

唯一の違いは、Fragment.onStartでmLoaderManager!= nullかどうかをチェックすることです。つまり、onActivityCreatedのように、onStartの前にgetLoadManagerを呼び出すと、ロードマネージャーを取得/作成して呼び出されます。これを回避するには、onResumeのように後で呼び出す必要があります。

3
vovkab

この主題のすべての検索は必然的にここに終わるため、自分の経験を追加したかっただけです。 @jpereraが述べたように、ローダーが既に存在する場合、LoaderManagerがonLoadFinished()を呼び出すことが原因でした。私の場合、FragmentPagerにフラグメントがあり、2つのタブをスクロールしてから、その隣で再度スクロールすると、古いフラグメントが作成を開始します。

OnCreate()内にinitLoader()を配置すると、二重のコールバックも発生するため、onResume()内にinitLoader()を配置しました。ただし、イベントのシーケンスはonCreate()になり、ローダーが存在するためLoaderManagerがコールバックを呼び出し、次にonResume()が呼び出されて、別のinitLoader()およびonLoadFinished()シーケンスをトリガーします。 IE、別の二重コールバック。

解決

"Matt" で簡単な解決策を見つけました。すべてのデータがロードされたら(ローダーが複数ある場合)、すべてのローダーを破棄して、コールバックが余分な時間と呼ばれないようにします。

0
1mike12

AppCompatActivityを実装する場合は、getSupportLoaderManager()をallケース(destroyLoader/initLoaderなど)で使用していることを再確認してください。 getLoaderManager()とともにgetSupportLoaderManager()を誤って使用したため、同じ問題が発生しました。

0
CoastalB

私はこの問題に直面していますが、loaderfinishedメソッドでdestroyloader(YOUR_ID)を呼び出すために使用されています。その後、ローダーはbackgrdoundタスクを2回呼び出しません。

0
prakash

OnLoadFinished(Loader loader、Object data)でデータオブジェクトを比較することもできます。データオブジェクトが既に持っているものと一致する場合、onLoadFinishedが呼び出されたときに何もできません。例えば:

public void onLoadFinished(Loader loader, Object data) {
        if(data != null && mData != data){
            //Do something
        }
}
0
Amer Meer