片段 A 的 findFragmentByTag 为 null,如果片段 B 上有 setRetain(true)

findFragmentByTag null for Fragment A, if setRetain(true) on Fragment B

我的问题涉及 activity 托管三个支持片段。一个是普通的程序化片段(我们称它为主页片段)。一种是在设备定向时添加在家庭片段之上的肖像片段,一种是 'headless',无论配置更改如何继续异步任务。很简单,我正在工作this nice example

public class HeadlessCustomerDetailFetchFragment extends Fragment{
private RequestCustomerDetails mRequest;
private AsyncFetchCustomerDetails mAsyncFetchCustomerDetails;

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setRetainInstance(true);

    mRequest = (RequestCustomerDetails)getActivity();
}

public void startFetching(String scannedBarcode) {
    if(mAsyncFetchCustomerDetails != null && mAsyncFetchCustomerDetails.getStatus() == AsyncTask.Status.RUNNING) return;

    if(mAsyncFetchCustomerDetails == null || mAsyncFetchCustomerDetails.getStatus() == AsyncTask.Status.FINISHED)
        mAsyncFetchCustomerDetails = new AsyncFetchCustomerDetails(getActivity(), mRequest, mPartner, scannedBarcode);
}

public void stopFetching() {
    if(mAsyncFetchCustomerDetails != null && mAsyncFetchCustomerDetails.getStatus() != AsyncTask.Status.RUNNING) return;
    mAsyncFetchCustomerDetails.cancel(true);
}

}

在我的 activity 的 onCreate() 中,我创建并在必要时添加无头片段。

 mHeadlessCustomerDetailFetchFragment = (HeadlessCustomerDetailFetchFragment)getSupportFragmentManager()
            .findFragmentByTag(HeadlessCustomerDetailFetchFragment.class.getSimpleName());

if(mHeadlessCustomerDetailFetchFragment == null) {
         mHeadlessCustomerDetailFetchFragment = HeadlessCustomerDetailFetchFragment.instantiate(this, HeadlessCustomerDetailFetchFragment.class.getName());
    getSupportFragmentManager().beginTransaction()
            .add(mHeadlessCustomerDetailFetchFragment, mHeadlessCustomerDetailFetchFragment.getClass().getSimpleName())
            .commit();
    getSupportFragmentManager().executePendingTransactions();
        id = null;
    }

然后,在 6 秒延迟(用于测试)后,我在方向更改为纵向时添加的纵向片段的 onCreateView() 中启动异步任务(通过我的 startFetching() 函数)。在 activity 的 onCreate():

中检测到方向变化
if (savedInstanceState == null) { 
   // Do some initial stuff for the home fragment
} 
else {
    getSupportFragmentManager().popBackStackImmediate(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
    if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
        //Launch portrait fragment
        FragmentLauncher.launchPortraitFragment(this);
    }

任务完成后,我return到activity并尝试更新活动肖像片段的UI,但片段管理器找不到它,findFragmentByTag() returns null.

要明确:

也许保留一个片段会主动杀死其他未保留的片段或类似的东西?

问题的根源在于您如何在无头片段中维护对 activity 的引用。
从提供的代码中不清楚在 AsyncTask 完成后如何更新 UI,让我们假设您使用第一个代码片段中的 mRequest。当您需要新的 AsyncTask 并在 AsyncTask 完成后使用此引用时,您将 mRequest 提供给构造函数。
没关系,如果在创建 activity 和更新 UI 之间没有屏幕旋转。这是因为您使用了对仍然有效的 activity 的引用。
如果你旋转屏幕是不行的。轮换后每次都有新的 activity。但是当你在 activity 的 onCreate() 的第一次调用中创建无头片段时,mRequest 只被分配一次。因此它包含对 activity 的第一个实例的引用,该实例在旋转后不活动。在您的情况下,轮换后有 2 个 activity 实例:第一个 - 由 mRequest 引用,第二个 - 可见且活动。您可以通过在 onCreate 中记录 activity 的引用来确认这一点:Log.i(TAG, "onCreate: this=" + this); 以及在异步任务后更新 UI 的 activity 方法中:[=15] =]
此外,第一个 activity 处于 Destroyed 状态。所有片段都从此 activity 分离,非保留片段被销毁。这就是为什么 findFragmentByTag returns null.
如果无头片段未设置为保留自身,则 activity 的 onCreate() 会在每次调用时重新创建它。所以 mRequest 总是引用最后创建的 activity 和所有片段。在这种情况下 findFragmentByTag returns 不为空。

为了避免这个问题,我建议:

  1. 使用弱引用存储Activity的引用。像这样:
    private WeakReference<RequestCustomerDetails> mRequest;
  2. HeadlessCustomerDetailFetchFragment 中创建一个方法来更新此引用。
    public void updateResultProcessor(RequestCustomerDetails requestCustomerDetails) { mRequest = new WeakReference(requestCustomerDetails); // Update ui if there is stored result of AsyncTask (see p.4b) }
  3. 每次从 activity 的 onCreate() 调用此方法。
  4. AsyncTask 完成时:
    a) 如果 mRequest.get() 不是 null 则更新 UI.
    b) 如果 mRequest.get()null 然后将结果存储在无头片段中并在 p.2.

    中使用它 弱引用将允许 GC 处理 destroyed activity 并在弱引用中设置 null。弱引用中的 Null 将表示没有 UI 并且没有要更新的内容。将 AsyncTask 的结果存储在无头片段中将允许在重新创建后使用此结果更新 UI。

    希望这会有所帮助。对不起我的英语不好。如果有什么不清楚的地方,我会尽力解释。