安卓碎片.在屏幕旋转或配置更改期间保留 AsyncTask [英] Android Fragments. Retaining an AsyncTask during screen rotation or configuration change

查看:23
本文介绍了安卓碎片.在屏幕旋转或配置更改期间保留 AsyncTask的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在开发一款智能手机/平板电脑应用,仅使用一个 APK,并根据屏幕大小按需加载资源,最佳设计选择似乎是通过 ACL 使用 Fragments.

I'm working on a Smartphone / Tablet app, using only one APK, and loading resources as is needed depending on screen size, the best design choice seemed to be using Fragments via the ACL.

此应用一直运行良好,直到现在仅基于活动.这是我如何处理活动中的 AsyncTasks 和 ProgressDialogs 的模拟类,以便即使在屏幕旋转或在通信过程中发生配置更改时,它们也能工作.

This app has been working fine until now being only activity based. This is a mock class of how I handle AsyncTasks and ProgressDialogs in the Activities in order to have them work even when the screen is rotated or a configuration change occurs mid communication.

我不会更改清单以避免重新创建 Activity,我不想这样做的原因有很多,但主要是因为官方文档说不推荐这样做,而且到目前为止我已经在没有它的情况下进行了管理,所以请不推荐那条路线.

I will not change the manifest to avoid recreation of the Activity, there are many reasons why I dont want to do it, but mainly because the official docs say it isnt recomended and I've managed without it this far, so please dont recomend that route.

public class Login extends Activity {

    static ProgressDialog pd;
    AsyncTask<String, Void, Boolean> asyncLoginThread;

    @Override
    public void onCreate(Bundle icicle) {
        super.onCreate(icicle);
        setContentView(R.layout.login);
        //SETUP UI OBJECTS
        restoreAsyncTask();
    }

    @Override
    public Object onRetainNonConfigurationInstance() {
        if (pd != null) pd.dismiss();
        if (asyncLoginThread != null) return (asyncLoginThread);
        return super.onRetainNonConfigurationInstance();
    }

    private void restoreAsyncTask();() {
        pd = new ProgressDialog(Login.this);
        if (getLastNonConfigurationInstance() != null) {
            asyncLoginThread = (AsyncTask<String, Void, Boolean>) getLastNonConfigurationInstance();
            if (asyncLoginThread != null) {
                if (!(asyncLoginThread.getStatus()
                        .equals(AsyncTask.Status.FINISHED))) {
                    showProgressDialog();
                }
            }
        }
    }

    public class LoginThread extends AsyncTask<String, Void, Boolean> {
        @Override
        protected Boolean doInBackground(String... args) {
            try {
                //Connect to WS, recieve a JSON/XML Response
                //Place it somewhere I can use it.
            } catch (Exception e) {
                return true;
            }
            return true;
        }

        protected void onPostExecute(Boolean result) {
            if (result) {
                pd.dismiss();
                //Handle the response. Either deny entry or launch new Login Succesful Activity
            }
        }
    }
}

此代码运行良好,我有大约 10.000 个用户没有抱怨,因此将此逻辑复制到新的基于片段的设计中似乎合乎逻辑,但是,当然,它不起作用.

This code is working fine, I have around 10.000 users without complaint, so it seemed logical to just copy this logic into the new Fragment Based Design, but, of course, it isnt working.

这是登录片段:

public class LoginFragment extends Fragment {

    FragmentActivity parentActivity;
    static ProgressDialog pd;
    AsyncTask<String, Void, Boolean> asyncLoginThread;

    public interface OnLoginSuccessfulListener {
        public void onLoginSuccessful(GlobalContainer globalContainer);
    }

    public void onSaveInstanceState(Bundle outState){
        super.onSaveInstanceState(outState);
        //Save some stuff for the UI State
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //setRetainInstance(true);
        //If I setRetainInstance(true), savedInstanceState is always null. Besides that, when loading UI State, a NPE is thrown when looking for UI Objects.
        parentActivity = getActivity();
    }

    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        try {
            loginSuccessfulListener = (OnLoginSuccessfulListener) activity;
        } catch (ClassCastException e) {
            throw new ClassCastException(activity.toString() + " must implement OnLoginSuccessfulListener");
        }
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        RelativeLayout loginLayout = (RelativeLayout) inflater.inflate(R.layout.login, container, false);
        return loginLayout;
    }

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        //SETUP UI OBJECTS
        if(savedInstanceState != null){
            //Reload UI state. Im doing this properly, keeping the content of the UI objects, not the object it self to avoid memory leaks.
        }
    }

    public class LoginThread extends AsyncTask<String, Void, Boolean> {
            @Override
            protected Boolean doInBackground(String... args) {
                try {
                    //Connect to WS, recieve a JSON/XML Response
                    //Place it somewhere I can use it.
                } catch (Exception e) {
                    return true;
                }
                return true;
            }

            protected void onPostExecute(Boolean result) {
                if (result) {
                    pd.dismiss();
                    //Handle the response. Either deny entry or launch new Login Succesful Activity
                }
            }
        }
    }
}

我不能使用 onRetainNonConfigurationInstance(),因为它必须从 Activity 而不是 Fragment 中调用,getLastNonConfigurationInstance() 也是如此.我在这里阅读了一些类似的问题,但没有答案.

I cant use onRetainNonConfigurationInstance() since it has to be called from the Activity and not the Fragment, same goes with getLastNonConfigurationInstance(). I've read some similar questions here with no answer.

我知道可能需要一些工作才能在片段中正确组织这些东西,也就是说,我想保持相同的基本设计逻辑.

I understand that it might require some working around to get this stuff organized properly in fragments, that being said, I would like to maintain the same basic design logic.

在配置更改期间保留 AsyncTask 的正确方法是什么,如果它仍在运行,请显示一个 progressDialog,考虑到 AsyncTask 是 Fragment 的内部类,它是 Fragment 本身谁调用了 AsyncTask.execute()?

推荐答案

Fragment 实际上可以让这件事变得更容易.只需使用方法 Fragment.setRetainInstance(boolean) 来让你的片段跨配置更改保留的实例.请注意,这是 Activity.onRetainnonConfigurationInstance() 的推荐替代品文档.

Fragments can actually make this a lot easier. Just use the method Fragment.setRetainInstance(boolean) to have your fragment instance retained across configuration changes. Note that this is the recommended replacement for Activity.onRetainnonConfigurationInstance() in the docs.

如果由于某种原因您真的不想使用保留的片段,您还可以采用其他方法.请注意,每个片段都有一个由 Fragment.getId().您还可以通过 Fragment 查看是否正在拆除片段以进行配置更改.getActivity().isChangingConfigurations().因此,当您决定停止 AsyncTask(最有可能在 onStop() 或 onDestroy() 中)时,您可以例如检查配置是否正在更改,如果更改,则将其粘贴在片段标识符下的静态 SparseArray 中,然后在您的 onCreate() 或 onStart() 中查看是否有可用的稀疏数组中的 AsyncTask.

If for some reason you really don't want to use a retained fragment, there are other approaches you can take. Note that each fragment has a unique identifier returned by Fragment.getId(). You can also find out if a fragment is being torn down for a config change through Fragment.getActivity().isChangingConfigurations(). So, at the point where you would decide to stop your AsyncTask (in onStop() or onDestroy() most likely), you could for example check if the configuration is changing and if so stick it in a static SparseArray under the fragment's identifier, and then in your onCreate() or onStart() look to see if you have an AsyncTask in the sparse array available.

这篇关于安卓碎片.在屏幕旋转或配置更改期间保留 AsyncTask的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆