对于片段A,findFragmentByTag为null,如果片段B上为setRetain(true)

Dan*_*son 8 android android-configchanges android-fragments fragmentmanager

我的问题涉及托管三个支持片段的活动.一个是正​​常的程序片段(让我们称之为家庭片段).一个是在设备定向时添加在主片段顶部的肖像片段,一个是"无头"的,以便无论配置如何变化都能继续执行异步任务.很简单,我正在研究这个很好的例子.

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);
}
Run Code Online (Sandbox Code Playgroud)

}

在我的活动的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;
    }
Run Code Online (Sandbox Code Playgroud)

然后,在方向更改为纵向时添加的肖像片段的onCreateView()中启动6秒延迟(用于测试)之后,我启动异步任务(通过我的startFetching()函数).在活动的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);
    }
Run Code Online (Sandbox Code Playgroud)

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

要明确:

  • 标签是正确的
  • 如果我没有定位设备,则会找到片段,而是在活动的onResume()期间启动异步任务.
  • 如果我不告诉无头片段保留自己 - 从而失去不重新创建它的好处,也可以正确找到肖像片段.
  • 调试我可以看到管理器中的所有3个片段,如果无头的片段没有设置为保留自己.如果是的话,我只能看到无头碎片.

也许保留一个片段会积极地杀死其他未保留的片段或其他类似的片段?

小智 4

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

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

\n\n
    \n
  1. 使用弱引用来存储Activity的引用。像这样的东西:
    private WeakReference<RequestCustomerDetails> mRequest;
  2. \n
  3. 创建一个方法来HeadlessCustomerDetailFetchFragment更新此引用。
    \npublic void updateResultProcessor(RequestCustomerDetails requestCustomerDetails) {\n mRequest = new WeakReference(requestCustomerDetails);\n// Update ui if there is stored result of AsyncTask (see p.4b)\n}
  4. \n
  5. 每次从 Activity 的 onCreate() 调用此方法。
  6. \n
  7. 当 AsyncTask 完成时:
    \na) 如果mRequest.get()没有完成null则更新 UI。
    \nb) ifmRequest.get()则将null结果存储在无头片段内并在 p.2 中使用它。

    \n弱引用将允许 GC 处理被破坏的活动并在弱引用内设置 null。弱引用内的 Null 将表示没有 UI,也没有任何内容需要更新。将 AsyncTask 的结果存储在无头片段中将允许在重新创建后使用此结果来更新 UI。\n

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