gpt4 book ai didi

android - 我们真的应该在 onActivityCreated 中调用 getLoaderManager().initLoader,这会导致 onLoadFinished 被调用两次

转载 作者:IT老高 更新时间:2023-10-28 22:18:30 26 4
gpt4 key购买 nike

Google 建议我们在 Fragment 的 onActivityCreated

中调用 getLoaderManager().initLoader(0, null, this);

http://developer.android.com/reference/android/content/AsyncTaskLoader.html

但是,这会产生以下问题:onLoadFinished 将在配置更改(轮换)期间被调用两次

我们可以模拟如下问题。

代码

package org.yccheok.gui;

import android.content.Context;
import android.os.Bundle;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.AsyncTaskLoader;
import android.support.v4.content.Loader;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import com.actionbarsherlock.app.SherlockFragment;

public class HomeMenuFragment extends SherlockFragment implements LoaderManager.LoaderCallbacks<HomeMenuFragment.Infos> {
private static class InfosLoader extends AsyncTaskLoader<Infos> {

private Infos infos = null;

public InfosLoader(Context context) {
super(context);
}

@Override
public Infos loadInBackground() {
Log.i(TAG, "loadInBackground");

this.infos = Infos.newInstance();
return infos;
}

/**
* Handles a request to cancel a load.
*/
@Override
public void onCanceled(Infos infos) {
super.onCanceled(infos);
}

/**
* Handles a request to stop the Loader.
* Automatically called by LoaderManager via stopLoading.
*/
@Override
protected void onStopLoading() {
// Attempt to cancel the current load task if possible.
cancelLoad();
}

/**
* Handles a request to start the Loader.
* Automatically called by LoaderManager via startLoading.
*/
@Override
protected void onStartLoading() {
if (this.infos != null) {
Log.i(TAG, "deliverResult");
deliverResult(this.infos);
}

if (takeContentChanged() || this.infos == null) {
Log.i(TAG, "forceLoad");
forceLoad();
}
}

/**
* Handles a request to completely reset the Loader.
* Automatically called by LoaderManager via reset.
*/
@Override
protected void onReset() {
super.onReset();

// Ensure the loader is stopped
onStopLoading();

// At this point we can release the resources associated with 'apps'
// if needed.
this.infos = null;
}
}

static class Infos {

private Infos() {
}

public static Infos newInstance() {
return new Infos();
}
}

@Override
public void onActivityCreated (Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
Log.i(TAG, "onActivityCreated");
// Prepare the loader. Either re-connect with an existing one,
// or start a new one.
getLoaderManager().initLoader(0, null, this);
}

@Override
public Loader<Infos> onCreateLoader(int arg0, Bundle arg1) {
return new InfosLoader(this.getSherlockActivity());
}

@Override
public void onLoadFinished(Loader<Infos> arg0, Infos arg1) {
Log.i(TAG, "onLoadFinished! -> " + arg1);
}

@Override
public void onLoaderReset(Loader<Infos> arg0) {
}

public void reloadAfterOpenFromCloud() {
this.getLoaderManager().getLoader(0).onContentChanged();
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.home_menu, container, false);
return v;
}

private static final String TAG = HomeMenuFragment.class.getSimpleName();
}

记录

I/HomeMenuFragment(14776): onActivityCreated
I/HomeMenuFragment(14776): forceLoad
I/HomeMenuFragment(14776): loadInBackground
I/HomeMenuFragment(14776): onLoadFinished! -> org.yccheok.gui.HomeMenuFragment$Infos@4195ad58

[Rotation happens right here]

I/HomeMenuFragment(14776): onActivityCreated
I/HomeMenuFragment(14776): onLoadFinished! -> org.yccheok.gui.HomeMenuFragment$Infos@4195ad58
I/HomeMenuFragment(14776): onLoadFinished! -> org.yccheok.gui.HomeMenuFragment$Infos@4195ad58

根据Android: LoaderCallbacks.OnLoadFinished called twice ,建议的解决方案之一是在 onResume 中调用 initLoader

@Override
public void onActivityCreated (Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
Log.i(TAG, "onActivityCreated");
//getLoaderManager().initLoader(0, null, this);
}

@Override
public void onResume()
{
super.onResume();
Log.i(TAG, "onResume");
// Prepare the loader. Either re-connect with an existing one,
// or start a new one.
getLoaderManager().initLoader(0, null, this);
}

这里是日志。在我们将 initLoader 移动到 onResume 之后,它现在看起来 OK

记录

I/HomeMenuFragment(15468): onActivityCreated
I/HomeMenuFragment(15468): onResume
I/HomeMenuFragment(15468): forceLoad
I/HomeMenuFragment(15468): loadInBackground
I/HomeMenuFragment(15468): onLoadFinished! -> org.yccheok.gui.HomeMenuFragment$Infos@4195aed0


I/HomeMenuFragment(15468): onActivityCreated
I/HomeMenuFragment(15468): onResume
I/HomeMenuFragment(15468): onLoadFinished! -> org.yccheok.gui.HomeMenuFragment$Infos@4195aed0

我想知道

  1. 为什么建议的解决方案有效?
  2. 这是一个错误吗?我们是否应该向 Google 提交有关此行为的错误?也许有人向 Google 提交了罚单,但我找不到。

最佳答案

建议的解决方案为何有效

如果我们在 onActivityCreated() 中调用 getLoaderManager() 然后我们初始化变量 Fragment.mLoaderManager

因此,我们在 FragmentActivity.onStart() 上的 Fragment.performStart() 中调用了 mLoaderManager.doReportStart():

  void performStart() {
if (mChildFragmentManager != null) {
mChildFragmentManager.noteStateNotSaved();
mChildFragmentManager.execPendingActions();
}
mCalled = false;
onStart();
if (!mCalled) {
throw new SuperNotCalledException("Fragment " + this
+ " did not call through to super.onStart()");
}
if (mChildFragmentManager != null) {
mChildFragmentManager.dispatchStart();
}
if (mLoaderManager != null) {
mLoaderManager.doReportStart();
}
}

这是第一次调用 onLoadFinished() 的原因。

稍后在 FragmentActivity.onStart() 中我们调用了 lm.finishRetain()(参见代码 fragment ):

 if (mAllLoaderManagers != null) {
LoaderManagerImpl loaders[] = new LoaderManagerImpl[mAllLoaderManagers.size()];
mAllLoaderManagers.values().toArray(loaders);
if (loaders != null) {
for (int i=0; i<loaders.length; i++) {
LoaderManagerImpl lm = loaders[i];
lm.finishRetain();
lm.doReportStart();
}
}
}

这是第二次调用onLoadFinished()的原因。


好的。现在考虑我们在 onResume() 中调用 getLoaderManager().initLoader(0, null, this) 的情况:

如果我们这样做,我们在 onActivityCreated() 之后既没有 mLoaderManager.doReportStart() 也没有 lm.finishRetain() code>,但是我们在 initLoader() 期间调用了 onLoadFinished():

public <D> Loader<D> initLoader(int id, Bundle args, LoaderManager.LoaderCallbacks<D> callback) {
if (mCreatingLoader) {
throw new IllegalStateException("Called while creating a loader");
}

LoaderInfo info = mLoaders.get(id);

if (DEBUG) Log.v(TAG, "initLoader in " + this + ": args=" + args);

if (info == null) {
// Loader doesn't already exist; create.
info = createAndInstallLoader(id, args, (LoaderManager.LoaderCallbacks<Object>)callback);
if (DEBUG) Log.v(TAG, " Created new loader " + info);
} else {
if (DEBUG) Log.v(TAG, " Re-using existing loader " + info);
info.mCallbacks = (LoaderManager.LoaderCallbacks<Object>)callback;
}

if (info.mHaveData && mStarted) {
// If the loader has already generated its data, report it now.
info.callOnLoadFinished(info.mLoader, info.mData);
}

return (Loader<D>)info.mLoader;
}

您可以在此 fragment 中看到 info.callOnLoadFinished() 调用:

if (info.mHaveData && mStarted) {
// If the loader has already generated its data, report it now.
info.callOnLoadFinished(info.mLoader, info.mData);
}

我认为很清楚:)

关于android - 我们真的应该在 onActivityCreated 中调用 getLoaderManager().initLoader,这会导致 onLoadFinished 被调用两次,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15515799/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com