OnSaveInstanceState / RestCalls

时间:2016-08-11 14:15:37

标签: android rest android-asynctask singleton runnable

我对Android开发相对较新,我对onSaveInstanceState()有疑问。我目前正在为应用程序登录Activity。要检查用户是否可以登录其帐户,我会对服务器执行休息调用,并根据响应代码查看是否应授予用户访问权限。我的问题的根源是基于我试图避免将Activity的Context传递给我的rest-call类。为此,我在登录Activity中创建一个布尔字段,表示rest-call是否成功,以及一个runnable,它更新了我传递给rest-call类的boolean。我知道这违背了AsyncTask的想法,但我找不到任何替代方法,只需设置一个对话框,告诉用户在发生这种情况时等待。我的问题如下。

1)如果我在onCreate方法中使用savedInstanceState(),那么如何首次实例化这个布尔字段,禁止null检查一个Object boolean?我的意思是,在Activity因任何原因(例如方向改变等等)被销毁之后,我将使用存储在我的override onSaveInstanceState方法中的布尔值;但是,当它第一次创建时,它没有引用布尔值,所以它必须创建一个。

2)这个Runnable有帮助吗?我这样做是为了不必传递上下文,但是如果在RestCall(AsyncTask)完成之前要删除Activity,那么是否传递上下文或者影响一个字段的Runnable真的很重要吗活动?我越是想到这一点,我越相信它不会产生太大的差别,因为它仍然会导致它指向一个不存在的对象。我试图避免使用Singleton设计,因为我已经收集它并不是最佳的,但由于AsyncTask的潜在时间滞后,我开始认为它可能无法避免。

我知道onSaveInstanceState()是一个在StackOverflow上被提出很多的话题,但是,我找不到这些问题的答案。如果已经有了这方面的话,我很抱歉,但对此有任何帮助或指导将不胜感激!谢谢!

登录活动的设置:

public class LoginActivity extends Activity implements View.OnClickListener {

private EditText username_et;
private EditText password_et;
private Button login_b;
private boolean login_success = true;
private Runnable run;


/**
 * Instances created when app starts
 */

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.view_login);
//  login_success = false;
    login_success = savedInstanceState.getBoolean("login_success");
    username_et = (EditText) findViewById(R.id.username_text);
    username_et.setOnClickListener(LoginActivity.this);
    password_et = (EditText) findViewById(R.id.password_text);
    password_et.setOnClickListener(LoginActivity.this);
    login_b = (Button) findViewById(R.id.login_button);
    login_b.setOnClickListener(LoginActivity.this);
    run = new Runnable() {
        @Override
        public void run() {
            login_success = true;
        }
    };
}

@Override
public void onSaveInstanceState(Bundle savedInstanceState){
    super.onSaveInstanceState(savedInstanceState);
    savedInstanceState.putBoolean("login_success", login_success);
}

1 个答案:

答案 0 :(得分:2)

祝贺。你刚刚发现了Android肮脏的小秘密。

AsyncTask有一个固有的设计缺陷。由于您提到的问题确实存在,因此在后台任务执行期间发生的配置更改并不能很好地处理。它需要保存对活动的引用,但是不能保证在后台任务完成时引用仍然有效。

以下两种方法可以解决这个问题:

  1. 我建议Alex Lockwood's excellent blog post使用setRetainInstance(true)隐藏的片段来跨越活动破坏和娱乐。这是一个比下一个解决方案更复杂的解决方案,但此解决方案的优势在于您仍然可以使用回调报告进度。如果您打算在publishProgress()中致电AsyncTask,那么这就是您应该使用的方法。

  2. 使用Loader。加载器是在后台围绕数据库数据检索而设计的,但事实是它们也可以用于在后台处理远程服务器访问。我使用Loader作为我的大多数远程服务器任务。

    以下是一个例子:

    public static class ResetPasswordLoader extends AsyncTaskLoader<Pair<CharSequence, Exception>> {
    
        private static final String TAG = "ResetPasswordLoader ";
    
        private String mEmail;
    
        public ResetPasswordLoader(Context context, String email) {
            super(context);
            mEmail = email;
            // set the content-changed flag
            onContentChanged();
        }
    
        @Override
        protected void onStartLoading() {
    
            // only start the load if the content-changed flag is set
            // takeContentChanged() returns the value of the flag before it is cleared
            if (takeContentChanged()) {
                forceLoad();
            }
        }
    
        @Override
        public Pair<CharSequence, Exception> loadInBackground() {
    
            CharSequence result = null;
            Exception exc = null;
            try {
                result = Service.getInstance().resetPassword(mEmail);
            } catch (RemoteServiceException e) {
                exc = e;
                Log.e(TAG, "loadInBackground(), email = " + mEmail, e);
            }
    
            return new Pair<>(result, exc);
        }
    }
    

    此外,在我的onLoadFinished()覆盖中,我确保在加载程序的ID上调用loaderManager.destroyLoader()

    同样,Alex Lockwood的博客也有一些关于装载机的精彩文章。

  3. 对于用户界面,我经常做的事情是在调用loaderManager.initLoader()时在UI上设置一个不确定的进度条。我还设置了一个类似mProgressShown的布尔值。这个布尔值保存在onSaveInstanceState中,所以当再次创建活动/片段时,我恢复布尔值,告诉我立即显示进度条。一段时间之后onLoadFinished将被调用,我清除mProgressShown并隐藏进度条。