【问题标题】:findFragmentByTag null for Fragment A, if setRetain(true) on Fragment B片段 A 的 findFragmentByTag null,如果片段 B 上的 setRetain(true)
【发布时间】:2015-10-30 22:25:31
【问题描述】:

我的问题涉及托管三个支持片段的活动。一个是普通的程序片段(我们称它为家庭片段)。一个是在设备定向时添加到主页片段顶部的纵向片段,一个是“无头”,以继续异步任务而不管配置更改。很简单,我在工作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);
}

}

在我的活动的 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;
    }

然后,在方向更改为纵向时添加的纵向片段的 onCreateView() 中启动 6 秒延迟(用于测试)后,我启动了一个异步任务(通过我的 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并尝试更新活动肖像片段的UI,但片段管理器找不到它,findFragmentByTag()返回null

要明确:

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

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

【问题讨论】:

  • 你为什么不能在服务中执行你的异步任务?我相信这消除了您的轮换问题,也是一种更简洁的方法?
  • 我也是这么认为的——我对服务了解不多,不过对于一项任务来说这似乎有点矫枉过正,但它可能是我唯一的选择——会调查一下谢谢
  • 酷豆。如果您需要任何帮助,请告诉我。
  • 也许setRetained 改变了popBackStackImmediate(name, flags) 的工作方式?我可能会尝试删除或修改getSupportFragmentManager().popBackStackImmediate(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);

标签: android android-fragments android-configchanges fragmentmanager


【解决方案1】:

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

    弱引用将允许 GC 处理被破坏的活动并在弱引用中设置 null。弱引用内部的空值将表明没有 UI,也没有什么可更新的。将 AsyncTask 的结果存储在无头片段中将允许在重新创建后使用此结果更新 UI。

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

【讨论】:

  • 稍后会调查,但这里是赏金,它看起来非常像一个正确的答案,所以谢谢
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多