FragmentManager.findFragmentByTag返回null

时间:2012-10-01 16:26:14

标签: android android-fragments actionbarsherlock progressdialog android-support-library

我正在使用Android支持库(v4)和ActionBarSherlock。我正在尝试以编程方式关闭进度对话框。我编写了一个小实用程序类来帮助管理对话框。

对话框显示在AsyncTask.onPreExecute。它会正确显示。然后我通过旋转设备来触发配置更改,这会破坏活动(onDestroy调用AsyncTask.cancel(true))。 AsyncTask.onCancelled被调用,并且在我尝试关闭对话框的方法中。但没有任何反应。以下是显示和关闭对话框的辅助函数:

    public abstract class DialogHelperActivity extends SherlockFragmentActivity {

        protected void showProgressDialog(final String msg, final String tag){      
            FragmentTransaction ft = this.getSupportFragmentManager().beginTransaction();
            DialogFragment dialogFragment = ProgressDialogFragment.newInstance(msg);

            ft.add(dialogFragment, tag);
            ft.disallowAddToBackStack();
            ft.commitAllowingStateLoss(); //If I try with regular commit(), exceptions are thrown.
        }

        protected void closeDialog(final String tag){
            FragmentManager fm = this.getSupportFragmentManager();
            Fragment dialogFragment = fm.findFragmentByTag(tag);        

            if(dialogFragment != null){
                FragmentTransaction ft = fm.beginTransaction();
                ft.remove(dialogFragment);
                ft.commitAllowingStateLoss();
            } else {
                System.err.println("dialog not found!"); //This line is hit always
            }               
        }


        public static class ProgressDialogFragment extends SherlockDialogFragment {     

            static ProgressDialogFragment newInstance(final String msg) {
                ProgressDialogFragment adf = new ProgressDialogFragment();
                Bundle bundle = new Bundle();

                bundle.putString("alert-message", msg);

                adf.setArguments(bundle);
                return adf;
            }

            @Override
            public void onCreate(Bundle savedInstanceState) {
                super.onCreate(savedInstanceState);
                this.setCancelable(false);
                int style = DialogFragment.STYLE_NORMAL, theme = 0;
                setStyle(style,theme);
            }

            @Override
            public Dialog onCreateDialog(Bundle savedInstanceState) {
                Bundle bundle = this.getArguments();

                String message = bundle.getString("alert-message");


                ProgressDialog dialog = new ProgressDialog(getActivity());
                if(message != null){
                    dialog.setMessage(message);
                }

                dialog.setCancelable(false);
                dialog.setIndeterminate(true);

                return dialog;
            }       
        }

    }

旋转设备后,AsyncTask被取消。我从closeDielogonPostExecute致电onCancelled。对话框永远不会关闭,因为找不到标记ID(findFragmentByTag返回null)。我对此感到困惑。标签是我的实施活动中的静态字符串,因此在调用showProgressDialogcloseDialog之间不会丢失或更改标签。

非常感谢任何想法/提示/建议。

感谢。

1 个答案:

答案 0 :(得分:7)

问题在于我取消了活动AsyncTask中的onDestroy。这可以摆脱bg线程,但是AsyncTask.onCancelled不能关闭片段,因为它在活动被破坏后运行。在此之前,会创建一个新活动,片段管理器会恢复一个新对话框(即使它是使用setRetainInstance(false)创建的,我猜这是默认的)。

电话的时间表是这样的:

  1. 屏幕旋转触发配置更改
  2. 旧活动进入onDestroy,取消asynctask。
  3. 旧对话框输入onDetach
  4. 创建了新活动。
  5. 创建新对话框,附加到新活动并显示。
  6. 执行旧任务onCancel,调用closeDialog,但未找到该标记。
  7. 我的错误是假设字符串标记在应用程序上下文中全局标识了一个片段,但事实证明片段管理器分配的实际片段ID是片段标记/ id及其活动ID的组合。当活动被销毁时,它们的片段被分离,在此之后,即使具有相同标记/ id的新片段在前景中,因为它附加到不同的活动,片段管理器在旧活动时返回null调用findFragmentByTag。

    然而,这个标签/ id足以让新片段传递旧片段的参数包。这种二元性令人困惑,但它也可以实现黑客攻击:我们可以在onStop回调中使用“已取消”标记填充片段的参数包,在onResume回调中对其进行查询,其中如果找到标志,它会调用自己解散。这样我就可以拥有一个概念上属于AsyncTask的进度对话框,并随之消失。