2016-08-11 65 views
2

我对Android开发比较陌生,我对onSaveInstanceState()有个疑问。我目前正在为应用程序开发一个登录Activity。要检查用户是否可以登录到他们的帐户,我会对服务器执行一次休息呼叫,并根据响应代码查看是否应该授予用户访问权限。我的问题的根源在于,我试图避免将Activity的Context传递给我的rest-call类。为此,我在我的登录Activity中创建了一个布尔型字段,表示剩余调用是否成功,以及一个可更新布尔型的runnable,我将其传递给其余调用类。我知道这与AsyncTask的想法背道而驰,但我无法找到任何替代方法来简单地建立一个对话框,告诉用户在发生这种情况时要等待。我的问题如下。如果我在onCreate方法中使用savedInstanceState(),如何在第一次实例化这个布尔型字段barring null检查一个Object布尔值?我的意思是,在任何原因(例如方向改变等)下销毁活动后,我将使用存储在我的覆盖onSaveInstanceState方法中的布尔值;然而,当它第一次创建时,它没有引用一个布尔值,所以它必须创建一个。OnSaveInstanceState/RestCalls

2)这个Runnable甚至有帮助吗?我这样做是为了不必传递上下文,但是如果在RestCall(AsyncTask)完成之前将删除Activity,那么传递上下文还是影响Runnable的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); 
} 
+0

为什么你不使用Android的'AsyncTask'?它拥有你需要的一切,如果你需要更多的控制任务,你甚至可以通过回调。 –

回答

2

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

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

这里有两种方法来解决这个问题:

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

  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()覆盖我请确保调用loaderManager.destroyLoader()对装载机的ID。

    再一次,亚历克斯洛克伍德的博客也有一些关于装载机的伟大文章。

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

+0

谢谢!这正是我需要的! – James