Android的碎片。保留屏幕旋转或配置更改过程中的AsyncTask过程中、碎片、屏幕、Android

2023-09-11 12:11:34 作者:胆小色小怕狗咬。

我工作的智能手机/平板电脑的应用程序,只使用一个APK,和加载资源,根据屏幕尺寸是必要的,最好的设计选择似乎通过访问控制列表,使用残卷。

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.

我是不会改变的清单,以避免活动的娱乐,有很多原因,我不想这样做,但主要是因为官方的文档说它不是recomended,我已经离不开它管理的这一步,所以请不建议更换这条道路。

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
            }
        }
    }
}

这code工作正常,我身边有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.

下面是LoginFragment:

Here is the LoginFragment:

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(),因为它必须从活动而不是片段叫,也一样有 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的是一个内部类的片段,它是碎片本身谁调用AsyncTask.execute()?

推荐答案

片段实际上可以使这是一个容易得多。只是使用的方法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.