android - fragment A 的 findFragmentByTag 为 null,如果 fragment B 上有 setRetain(true)

标签 android android-fragments android-configchanges fragmentmanager

我的问题涉及托管三个支持 fragment 的 Activity 。一个是普通的程序化 fragment (我们称它为主页 fragment )。一种是在设备定向时添加到主页 fragment 顶部的肖像 fragment ,另一种是“ headless ” fragment ,无论配置更改如何继续异步任务。很简单,我正在工作 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() 中,我创建并在必要时添加 headless fragment 。

 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 秒延迟(用于测试)后,我在方向更改为纵向时添加的纵向 fragment 的 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);
    }

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

要清楚:

  • 标签正确
  • 如果我定位设备,而是在其他地方启动异步任务,例如在 Activity 的 onResume() 期间,就会找到该 fragment 。
  • 如果我不告诉 headless fragment 保留自己——从而失去不重新创建它的好处,肖像 fragment 也会被正确找到。
  • 调试 我可以在管理器中看到所有 3 个 fragment 如果没有将 headless fragment 设置为保留自身。如果是,我只能看到 headless fragment 。

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

最佳答案

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

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

  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然后更新用户界面。
    b) 如果mRequest.get()null然后将结果存储在 headless fragment 中并在 p.2 中使用它。

    弱引用将允许 GC 处理已销毁的 Activity 并在弱引用内设置 null。弱引用内部为空将表示没有 UI,也没有要更新的内容。将 AsyncTask 的结果存储在 headless fragment 中将允许在重新创建后使用此结果更新 UI。

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

关于android - fragment A 的 findFragmentByTag 为 null,如果 fragment B 上有 setRetain(true),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33445775/

相关文章:

android - 屏幕旋转使android应用程序崩溃

android - 如何处理夜间模式的转换?

Android - 我应该通过 savedinstancestate 保存我的对象还是只保存它的 ID

android - 启用 MTP 后,Nexus 设备在 Ubuntu 上对 ADB 不可见

java - Android Studio 无法识别 "task.isSuccesful"( "Cannot resolve symbol ' 任务 ' ")

android - 无法从 ubuntu 12.04 中的 adt 包启动 eclipse

android - 在 android 模块之间使用 Dagger 2 (aar)

java - NavigationDrawer fragment 样式/图标

java - TabMain 不是抽象的,不会覆盖 TabListener 中的抽象方法 onTabReselected(Tab,FragmentTransaction)

android - 如何在屏幕旋转时保留 View Pager 的 fragment ?