OnSaveInstanceState/ RestCalls
OnSaveInstanceState/ RestCalls
我是 Android 开发的新手,我对 onSaveInstanceState() 有疑问。我目前正在为一个应用程序登录 Activity。为了检查用户是否可以登录他们的帐户,我对服务器执行了一次 rest 调用,并根据响应代码查看我是否应该授予用户访问权限。我的问题的根源是基于这样一个事实,即我试图避免将 Activity 的上下文传递给我的休息呼叫 class。为此,我在我的登录 Activity 中创建了一个布尔值字段,表示 rest-call 是否成功,并创建了一个 runnable 来更新我传递给 rest-call class 的布尔值。我知道这与 AsyncTask 的想法背道而驰,但除了简单地设置一个对话框告诉用户等待发生这种情况之外,我找不到任何替代方法。我的问题如下。
1) 如果我在 onCreate 方法中使用 savedInstanceState() ,我如何在第一次实例化这个布尔字段时禁止空检查对象布尔值?我的意思是,在 Activity 由于任何原因(例如方向改变等)被销毁后,我将使用存储在我重写的 onSaveInstanceState 方法中的布尔值;但是,当它第一次创建时,它没有引用布尔值,所以它必须创建一个。
2) 这个 Runnable 有帮助吗?我这样做是为了不必传递上下文,但是如果要在 RestCall(AsyncTask) 完成之前删除 Activity,那么传递上下文还是影响 Runnable 真的很重要吗Activity的一个字段?我想得越多,我就越相信它不会产生太大的影响,因为它仍然会导致它指向一个不存在的对象。我试图避免使用 Singleton 设计,因为我收集到它不是最优的,但由于 AsyncTask 可能存在时间滞后,我开始认为它可能无法避免。
我知道 onSaveInstanceState() 是 Whosebug 上经常提到的一个话题,但是,我找不到这些问题的答案。如果已经有与此相关的主题,我深表歉意,但我们将不胜感激任何对此的帮助或指导!谢谢!
登录活动设置:
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);
}
恭喜。您刚刚发现了 Android 的肮脏小秘密。
AsyncTask
存在固有的设计缺陷。由于您提到的问题,它不能很好地处理后台任务执行期间发生的配置更改。它需要保存对 activity 的引用,但不能保证在后台任务完成时该引用仍然有效。
这里有两种方法可以解决这个问题:
我建议你参考 Alex Lockwood's excellent blog post 使用隐藏片段 setRetainInstance(true)
跨越 activity 破坏和重建。这是一个比下一个更复杂的解决方案,但这个解决方案的优点是您仍然可以使用回调报告进度。如果您打算在 AsyncTask
中调用 publishProgress()
,那么您应该使用这种方法。
使用一个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 的博客也有一些关于加载程序的精彩文章。
对于 UI,我经常做的事情是在调用 loaderManager.initLoader()
时在 UI 上设置一个不确定的进度条。我还设置了一个布尔值,如 mProgressShown
。此布尔值保存在 onSaveInstanceState
中,因此当再次创建 activity/fragment 时,我恢复了告诉我立即显示进度条的布尔值。稍后 onLoadFinished
将被调用,我清除 mProgressShown
并隐藏进度条。
我是 Android 开发的新手,我对 onSaveInstanceState() 有疑问。我目前正在为一个应用程序登录 Activity。为了检查用户是否可以登录他们的帐户,我对服务器执行了一次 rest 调用,并根据响应代码查看我是否应该授予用户访问权限。我的问题的根源是基于这样一个事实,即我试图避免将 Activity 的上下文传递给我的休息呼叫 class。为此,我在我的登录 Activity 中创建了一个布尔值字段,表示 rest-call 是否成功,并创建了一个 runnable 来更新我传递给 rest-call class 的布尔值。我知道这与 AsyncTask 的想法背道而驰,但除了简单地设置一个对话框告诉用户等待发生这种情况之外,我找不到任何替代方法。我的问题如下。
1) 如果我在 onCreate 方法中使用 savedInstanceState() ,我如何在第一次实例化这个布尔字段时禁止空检查对象布尔值?我的意思是,在 Activity 由于任何原因(例如方向改变等)被销毁后,我将使用存储在我重写的 onSaveInstanceState 方法中的布尔值;但是,当它第一次创建时,它没有引用布尔值,所以它必须创建一个。
2) 这个 Runnable 有帮助吗?我这样做是为了不必传递上下文,但是如果要在 RestCall(AsyncTask) 完成之前删除 Activity,那么传递上下文还是影响 Runnable 真的很重要吗Activity的一个字段?我想得越多,我就越相信它不会产生太大的影响,因为它仍然会导致它指向一个不存在的对象。我试图避免使用 Singleton 设计,因为我收集到它不是最优的,但由于 AsyncTask 可能存在时间滞后,我开始认为它可能无法避免。
我知道 onSaveInstanceState() 是 Whosebug 上经常提到的一个话题,但是,我找不到这些问题的答案。如果已经有与此相关的主题,我深表歉意,但我们将不胜感激任何对此的帮助或指导!谢谢!
登录活动设置:
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);
}
恭喜。您刚刚发现了 Android 的肮脏小秘密。
AsyncTask
存在固有的设计缺陷。由于您提到的问题,它不能很好地处理后台任务执行期间发生的配置更改。它需要保存对 activity 的引用,但不能保证在后台任务完成时该引用仍然有效。
这里有两种方法可以解决这个问题:
我建议你参考 Alex Lockwood's excellent blog post 使用隐藏片段
setRetainInstance(true)
跨越 activity 破坏和重建。这是一个比下一个更复杂的解决方案,但这个解决方案的优点是您仍然可以使用回调报告进度。如果您打算在AsyncTask
中调用publishProgress()
,那么您应该使用这种方法。使用一个
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 的博客也有一些关于加载程序的精彩文章。
对于 UI,我经常做的事情是在调用 loaderManager.initLoader()
时在 UI 上设置一个不确定的进度条。我还设置了一个布尔值,如 mProgressShown
。此布尔值保存在 onSaveInstanceState
中,因此当再次创建 activity/fragment 时,我恢复了告诉我立即显示进度条的布尔值。稍后 onLoadFinished
将被调用,我清除 mProgressShown
并隐藏进度条。