周末我开始构建我的第一个 android 应用程序。由于我需要向我的应用程序的用户询问用户凭据 [用于进一步使用 Web 服务],因此我想模拟一个“登录系统”。在我的应用程序开始时,应该告诉用户输入他的凭据。当用户长时间处于非活动状态时,我想关闭输入的凭据并“注销”用户。在编码和之后的测试中,我意识到我认为我可以去的方式行不通。在一次又一次地阅读文档和几个 SO 问题后,我越来越质疑自己是否完全了解应用程序/活动生命周期及其可能性。所以我想寻求帮助以了解生命周期及其对我的应用程序的相关影响。所以是的,这可能是一个问题:/
目前我的应用程序包含以下活动:
- 搜索活动(应用程序启动后打开)
- 一个设置活动(可以从搜索对话框访问,并有一个返回搜索对话框的链接)
用户在搜索对话框中输入 ID 后,我想打开有关搜索结果 (NYI) 的活动。
在开始实施用户身份验证时,我的想法如下:每次onResume()
调用活动时,我都需要检查 a) 是否已经存储了用户凭据,以及 b) 用户的最后一次操作是否少于 X 分钟前。如果其中一个失败,我想显示一个“登录面板”,用户可以在其中输入他的凭据,然后将其存储在 SharedPreferences 中。为此,我做了以下事情:
我首先构建了一个父活动,其中包含 SharedPreferences 的检查和参考
public class AppFragmentActivity extends FragmentActivity {
protected SharedPreferences sharedPref;
protected SharedPreferences.Editor editor;
protected String WebServiceUsername;
protected String WebServicePassword;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_appfragmentactivity);
}
@Override
protected void onResume () {
super.onResume();
// Check if user is "logged in".
// Meaning: Are there given user credentials and are they valid of was the user inactive for too long?
// We only do this "onResume" because this callback is the only one, which is called everytime an user
// starts/restarts/resumes an application
checkForUserCredentials();
// Set new "last action" now "now"
setLastAction(new Date().getTime());
}
@Override
protected void onStart () {
// Fill content
super.onStart();
// Set global sharedPreferences
sharedPref = this.getSharedPreferences(getString(R.string.FILE_settings_file), Context.MODE_PRIVATE);
}
/*
* Checks if user credentials are valid meaning if they are set and not too old
*/
private void checkForUserCredentials() {
long TimeLastAction = sharedPref.getLong(getString(R.string.SETTINGS_USER_LAST_ACTION), 0);
long TimeNow = new Date().getTime();
// Ask for User credentials when last action is too long ago
if(TimeLastAction < (TimeNow - 1800)) {
// Inactive for too long
// Set back credentials
setUsernameAndPassword("", "");
}
else
{
WebServiceUsername = sharedPref.getString(getString(R.string.SETTINGS_USER_USERNAME), "");
WebServicePassword = sharedPref.getString(getString(R.string.SETTINGS_USER_PASSWORD), "");
}
}
/*
* Saves the given last action in the sharedPreferences
* @param long LastAction - Time of the last action
*/
private void setLastAction(long LastAction) {
editor = sharedPref.edit();
editor.putLong(getString(R.string.SETTINGS_USER_LAST_ACTION), LastAction);
editor.commit();
}
/*
* Saves the given username and userpassword sharedPreferences
* @param String username
* @param String password
*/
private void setUsernameAndPassword(String username, String password) {
editor = sharedPref.edit();
editor.putString(getString(R.string.SETTINGS_USER_USERNAME), username);
editor.putString(getString(R.string.SETTINGS_USER_PASSWORD), password);
editor.commit();
WebServiceUsername = username;
WebServicePassword = password;
}
/*
* Method called when pressing the OK-Button
*/
public void ClickBtnOK(View view) {
// Save User-Creentials
EditText dfsUsername = (EditText) findViewById(R.id.dfsUsername);
String lvsUsername = dfsUsername.getText().toString();
EditText dfsPassword = (EditText) findViewById(R.id.dfsPassword);
String lvsPassword = dfsPassword.getText().toString();
if(lvsUsername.equals("") || lvsPassword.equals("")) {
TextView txtError = (TextView) findViewById(R.id.txtError);
txtError.setText(getString(R.string.ERR_Name_or_Password_empty));
}
else
{
// Save credentials
setUsernameAndPassword(lvsUsername, lvsPassword);
setLastAction(new Date().getTime());
// open Searchactivity
Intent intent = new Intent(this, SearchActivity.class);
startActivity(intent);
}
}
“登录掩码”是setContentView(R.layout.activity_appfragmentactivity);
.
然后我创建的另外两个活动正在扩展这个父类。这是其中之一:
public class SearchActivity extends AppFragmentActivity {
SearchFragment searchfragment;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_search);
}
@Override
protected void onResume() {
super.onResume();
if(WebServiceUsername.equals("") && WebServicePassword.equals("")) {
// Username not set. Re"login".
Intent intent = new Intent(this, AppFragmentActivity.class);
startActivity(intent);
}
}
@Override
protected void onStart() {
super.onStart();
}
// ...
}
据我现在了解的生命周期,这应该如下工作:当我的应用程序启动(SearchActivity
设置为LAUNCH
)时,应用程序应该进入onResume()
我的父类。在那里它看到凭据尚未存储并打开AppFragmentActivity
登录的布局。输入后,用户将被重定向到SearchActivity
现在看到“ok 凭据在那里,让我们继续前进”。但这不会发生,因为登录没有显示出来。所以我认为我的onResume()
可能是错的。也许我的完整想法很糟糕?到这里为止,我以为我也了解生命周期,但显然我不了解?
然后,我在 SO 上查看了类似的问题。我在这里看到的一件事是对想要建立与我类似的“注销”机制的用户的评论,他必须在每个活动中实现这一点。我想了想并问自己“为什么我必须onResume()
在我的每个活动中覆盖它们,当它们都来自同一个父母时?当onResume()
孩子没有时,应该调用父母的那个”。建议 SO 问题中的用户使用服务作为后台线程来倒计时那里的注销。然后我阅读了文档中的服务文章,然后完全迷失了方向:
有两种类型的服务:已启动的和有界的。一个启动的服务一旦由一个活动启动,然后在后台运行,直到它没有停止时地狱冻结。所以它完全独立于任何应用程序,但程序员必须/应该在不再需要它时停止它。有界服务绑定到一个或多个应用程序组件,并在所有有界组件结束时停止。我认为这对我来说可能是一个不错的选择,但是当我进一步思考时,我问自己如何:如果我的一个启动它(比如说登录对话框)然后关闭,则服务停止并且其他活动总是从那里开始那些不可能是它的感觉。所以这个服务必须不是绑定到任何组件,而是绑定到我的应用程序。但安卓应用程序的生命周期是什么?如何保持信息“全球” 在我的应用程序中。我知道我可以使用“意图”在活动之间切换数据。
这越来越多的“雾云”导致我问自己:“我应该只使用一个活动并尝试使用片段来切换所有内容吗?”
所以我的问题是(我认为这就是全部,但我不确定了):
- 我的想法是编写一个父类来检查所有扩展孩子的好坏,并且它是否像我理解的那样工作?
- 我是否必须覆盖
onResume()
孩子中的每一个才能打电话给父母进行检查? - 你能告诉我为什么我的“登录系统”不起作用吗?
- android 应用程序的生命周期是什么,我如何与之交互?
- 我应该只使用一个活动并使用片段切换所有内容,还是有几个活动并且其中一些使用片段(重用经常使用的部分)是一种好方法?
谢谢指教