android - IllegalStateException fragment 在旋转后未附加到 onPostExecute 中的 Activity ,但未调用 onDetach

标签 android android-fragments android-asynctask android-dialogfragment

我有一个使用 NavigationDrawer 模式的 AppCompatActivity,用于管理一些 fragment 。其中一个没有 setRetainInstance(true),我显示了一个 DialogFragment,其中包含一个 ProgressDialog 和一个带有以下代码的 AsyncTask:

SavingLoader savingLoader = SavingLoader.newInstance(savingLoaderMaxValue);
savingLoader.show(getChildFragmentManager(), SAVING_LOADER_TAG);
new MyAsyncTask().execute();

其中 SavingLoader 类是这个:

public class SavingLoader extends DialogFragment {

    private static final String MAX_VALUE_TAG = "MAX_VALUE_TAG";
    private static final String PROGRESS_VALUE_TAG = "PROGRESS_VALUE_TAG";

    public static SavingLoader newInstance(int max_value){
        SavingLoader s = new SavingLoader();
        Bundle args = new Bundle();
        args.putInt(MAX_VALUE_TAG, max_value);
        s.setArguments(args);
        return s;
    }

    private ProgressDialog dialog;

    public SavingLoader(){}

    @Override
    public void onCreate(Bundle savedInstanceState){
        super.onCreate(savedInstanceState);
        setCancelable(false);
    }

    @NonNull
    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState){
        dialog = new ProgressDialog(getActivity(), getTheme());
        dialog.setTitle(getString(R.string.dialog_title_saving));
        dialog.setMessage(getString(R.string.dialog_message_saving));
        dialog.setIndeterminate(false);
        int max = (savedInstanceState == null ?
            getArguments().getInt(MAX_VALUE_TAG) : savedInstanceState.getInt(MAX_VALUE_TAG));
        if (max >= 1){
            dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
            dialog.setProgress((savedInstanceState == null ?
                0 : savedInstanceState.getInt(PROGRESS_VALUE_TAG)));
            dialog.setMax(max);
        } else dialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
        return dialog;
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putInt(MAX_VALUE_TAG, dialog.getMax());
        outState.putInt(PROGRESS_VALUE_TAG, dialog.getProgress());
    }

    public int getProgress(){
        return dialog.getProgress();
    }

    public int getMax(){
        return dialog.getMax();
    }

    public void incrementProgressBy(int value){
        if (dialog.getProgress() + value <= dialog.getMax())
            dialog.incrementProgressBy(value);
    }

}

onPostExecute() 方法中,我需要执行一些 UI 更新,所以这是我的问题:如果我启动对话框和 AsyncTask (如上)并且我不旋转手机,所有按预期工作。如果我在 onPostExecute() 方法之后旋转手机,也会发生同样的情况。但是,如果我在 AsyncTask 仍在运行时旋转手机,当它完成并到达 onPostExecute() 方法时,它会给出 IllegalStateException ,表示托管 AsyncTask 的 fragment 和Dialogfragment 不再附加到该 Activity 。因此,我尝试重写 fragment 的 onAttach()onDetach() 方法(使用简单的 System.out.println) ,查看 onPostExecute() 何时被调用。结果是,当我旋转手机时,我总是得到以下输出:

onDetach
onAttach
... (if I rotate more my phone)
onPostExecute

那么当 AsyncTask 完成时不应该附加 fragment 吗?感谢大家的时间和关注。

最佳答案

我终于通过停止使用 AsyncTask 并使用 LoaderManager 解决了这个问题。 + AsyncTaskLoader遵循此article 。简而言之,您的 fragment 必须实现 LoaderCallbacks接口(interface)并管理AsyncTaskLoader。骨架 fragment 可能是这样的:

public class MyFragment extends Fragment implements LoaderManager.LoaderCallbacks {

    @Override
    public View onCreateView(LayoutInflater inflater, final ViewGroup container,
                         Bundle savedInstanceState) {
        // Inflate here your view as you usually do and find your components
        // For example imagine to have a button tha will fire the task
        Button b = (Button) view.findViewById(R.id.my_button);
        b.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // Use this to start task for the first time
                getLoaderManager().initLoader(0, null, this);
                // .. or this for restart the task, details in
                // the provided article
                // getLoaderManager().restartLoader(0, null, this);
            }
        });

        // Get fragments load manager
        LoaderManager lm = getLoaderManager();
        if (lm.getLoader(0) != null) {
            // Reconnect to an existing loader
            lm.initLoader(0, null, this);
        }

        // Return your view here
        return view;
    }

    // LoaderCallbacks methods to override
    @Override
    public Loader onCreateLoader(int id, Bundle args) {
        // Create an instance of the loader passing the context
        MyTaskLoader loader = new MyTaskLoader(getActivity());
        loader.forceLoad();
        return loader;
    }

    @Override
    public void onLoadFinished(Loader loader, Object data) {
        // Use this callback as you would use the AsyncTask "onPostExecute"
    }

    @Override
    public void onLoaderReset(Loader loader) {}

    // Now define the loader class
    private static class MyTaskLoader extends AsyncTaskLoader {
        public MyTaskLoader(Context context){
            super(context);
        }

        @Override
        public Object loadInBackground() {
            // Do here your async work
        }
    }

}

关于android - IllegalStateException fragment 在旋转后未附加到 onPostExecute 中的 Activity ,但未调用 onDetach,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31057766/

相关文章:

android-fragments - 在Kotlin中..我如何从片段返回MainActivity

android - 为什么我的 YouTube 全屏崩溃了?

android - 这个 NetworkOnMainThreadException 的原因是什么

java - 获取屏幕宽度 Android

java - 谷歌地图 Android fragment 崩溃

android - 在横向模式下将 View 伪造为纵向

java - Android AsyncTask doInBackground 适用于 1 部手机,但不适用于其他手机

android - 使用AsyncTask如何判断listview中item是否被回收?

android - 如何让 intellij android 预览窗口保持打开状态?

android - ScrollView ScrollBy 方法有 2 个参数