如何在Android中声明全局变量?
我正在创建一个需要登录的应用程序。 我创建了主要和登录活动。
在主要活动onCreate
方法中,我添加了以下条件:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
...
loadSettings();
if(strSessionString == null)
{
login();
}
...
}
在登录表单终止时执行的onActivityResult
方法如下所示:
@Override
public void onActivityResult(int requestCode,
int resultCode,
Intent data)
{
super.onActivityResult(requestCode, resultCode, data);
switch(requestCode)
{
case(SHOW_SUBACTICITY_LOGIN):
{
if(resultCode == Activity.RESULT_OK)
{
strSessionString = data.getStringExtra(Login.SESSIONSTRING);
connectionAvailable = true;
strUsername = data.getStringExtra(Login.USERNAME);
}
}
}
问题是登录表单有时出现两次( login()
方法被调用两次),并且当电话键盘滑动登录表单时又出现了,我猜测问题是变量strSessionString
。
有谁知道如何设置变量全局,以避免登录表单出现后,用户已经成功验证?
我在09年的时候写了这个答案,当时Android相对较新,并且在Android开发中有许多不是很成熟的领域。 我在这篇文章的底部增加了一个附录,解释了一些批评,并且详细说明了我使用Singletons而不是子类化Application的哲学分歧。 阅读它需要您自担风险。
原文答案:
您遇到的更一般的问题是如何在多个活动和应用程序的所有部分之间保存状态。 一个静态变量(例如,一个单例)是实现这一点的常用Java方法。 然而,我发现Android中更优雅的方式是将您的状态与应用程序上下文相关联。
如你所知,每个Activity也是一个Context,它是关于其最广泛意义上的执行环境的信息。 你的应用程序也有一个上下文,并且Android保证它将作为单个实例存在你的应用程序中。
做到这一点的方法是创建自己的android.app.Application的子类,然后在清单中的应用程序标签中指定该类。 现在,Android会自动创建该类的一个实例,并使其可用于整个应用程序。 您可以使用Context.getApplicationContext()
方法从任何context
访问它( Activity
也提供了一个方法getApplication()
,它具有完全相同的效果)。 以下是一个非常简单的例子,需要注意以下几点:
class MyApp extends Application {
private String myState;
public String getState(){
return myState;
}
public void setState(String s){
myState = s;
}
}
class Blah extends Activity {
@Override
public void onCreate(Bundle b){
...
MyApp appState = ((MyApp)getApplicationContext());
String state = appState.getState();
...
}
}
这与使用静态变量或单例的效果基本相同,但很好地集成到现有的Android框架中。 请注意,这不适用于各个流程(如果您的应用程序是具有多个进程的罕见应用程序之一)。
从上面的例子中可以注意到一些事情; 假设我们做了类似的事情:
class MyApp extends Application {
private String myState = /* complicated and slow initialization */;
public String getState(){
return myState;
}
}
现在,每次应用程序实例化时,都会执行这种缓慢的初始化(例如点击磁盘,点击网络,阻止任何事物等)! 你可能会认为,这只是一次过程,我必须付出代价,对吧? 例如,正如Dianne Hackborn在下面提到的那样,您的流程完全可以被实例化 - 只需处理背景广播事件。 如果你的广播处理不需要这个状态,你可能只需要做一系列复杂而缓慢的操作。 懒惰实例化是这里游戏的名称。 以下是使用Application的稍微更复杂的方式,它对于除最简单的用途之外的任何内容都更有意义:
class MyApp extends Application {
private MyStateManager myStateManager = new MyStateManager();
public MyStateManager getStateManager(){
return myStateManager ;
}
}
class MyStateManager {
MyStateManager() {
/* this should be fast */
}
String getState() {
/* if necessary, perform blocking calls here */
/* make sure to deal with any multithreading/synchronicity issues */
...
return state;
}
}
class Blah extends Activity {
@Override
public void onCreate(Bundle b){
...
MyStateManager stateManager = ((MyApp)getApplicationContext()).getStateManager();
String state = stateManager.getState();
...
}
}
虽然我更喜欢Application子类化,在这里使用singleton作为更优雅的解决方案,但我宁愿开发人员在真正需要时使用单例,而不考虑将状态与Application子类关联的性能和多线程含义。
注1:同样作为anticafe评论,为了正确地将你的应用程序重写与你的应用程序绑定,标签在清单文件中是必需的。 再次请参阅Android文档以获取更多信息。 一个例子:
<application
android:name="my.application.MyApp"
android:icon="..."
android:label="...">
</application>
注2: user608578询问如何在管理原生对象生命周期时如何工作。 我没有加速在Android上使用本地代码的速度,我没有资格回答这将如何与我的解决方案进行交互。 如果有人确实回答了这个问题,我愿意将这些信息放在这个帖子中以获得最大的可见度。
附录:
正如一些人所指出的那样,这不是 持久状态的解决方案,在原始答案中我应该强调更多。 也就是说,这并不意味着成为保存用户或其他信息的解决方案,这些信息应该在应用程序的生命周期中持续存在。 因此,我认为以下大多数批评与应用程序在任何时候都被杀死等等无关,因为任何永久需要保存到磁盘的内容都不应该通过应用程序子类来存储。 它旨在成为存储临时的,易于重新创建的应用程序状态(例如用户是否登录)以及本质上是单一实例(例如应用程序网络管理器)( 非单例!)的组件的解决方案。
Dayerman非常友好地指出了与Reto Meier和Dianne Hackborn的有趣对话,其中使用Application子类不鼓励使用Singleton模式。 Somatik早些时候也指出了这种性质,尽管我当时没有看到它。 由于Reto和Dianne在维护Android平台方面的角色,我不能真诚地推荐忽视他们的建议。 他们说,去。 我希望不同意这些意见,表达了关于比Singleton更适用于Application子类的观点。 在我的意见分歧中,我将利用StackExchange对Singleton设计模式的解释中最好的解释,以便我不必在此答案中定义术语。 我强烈建议在继续之前浏览链接。 逐点:
Dianne表示:“没有理由从应用程序中进行子类化,它与创建单例模式没有区别......”这第一个要求是不正确的。 这有两个主要原因。 1)Application类为应用程序开发人员提供了更好的生命周期保证; 它保证有应用程序的生命周期。 单身并不明显与应用程序的生命周期有关(尽管它是有效的)。 对于普通的应用程序开发人员来说,这可能不是问题,但我认为这正是Android API应该提供的契约类型,它还为Android系统提供了更大的灵活性,方法是尽量减少关联的生命周期数据。 2)Application类为应用程序开发人员提供了一个状态的单个实例持有者,与Singleton持有者非常不同。 有关这些差异的列表,请参阅上面的单例解释链接。
“Dianne继续说道,”......当你发现你的应用程序对象变成这个应该是独立应用程序逻辑的大混乱时,你可能会后悔你将来的事情。“ 这当然不是不正确的,但这不是选择Singleton over Application子类的原因。 Diane的论点没有提供使用Singleton比Application子类更好的原因,她试图建立的所有原则是使用Singleton并不比应用程序子类更糟糕,我认为它是错误的。
她继续说道:“这更自然地引导你如何管理这些东西 - 按需求初始化它们。” 这忽略了这样一个事实,即没有理由不能使用Application子类按需进行初始化。 再次没有区别。
Dianne的结尾是“该框架本身拥有大量的单身人士,用于为应用程序维护的所有小共享数据,例如加载资源的缓存,对象池等等。它的功能非常好。” 我不争论说使用单身人士不能正常工作或不是合法的选择。 我认为Singletons不会像使用Application子类一样提供与Android系统的强大合同,而且使用Singletons通常指的是不灵活的设计,这种设计不容易修改,并且导致许多问题出现。 恕我直言,Android API为开发人员应用程序提供的强大合同是使用Android编程最吸引人和令人愉悦的方面之一,并且有助于促进开发人员早期采用,从而推动Android平台取得今天的成功。 建议使用单身人士隐含地远离强大的API合同,并且在我看来,削弱了Android框架。
Dianne也在下面进行了评论,提到了使用Application子类的另外一个缺点,他们可能鼓励或者更容易编写更少的性能代码。 这是非常真实的,我编辑了这个答案,强调了在这里考虑perf的重要性,并且如果你使用Application子类化,采取正确的方法。 正如Dianne所说,重要的是要记住,每当您的进程被加载时(如果您的应用程序在多个进程中运行,可以同时多次执行),您的Application类将被实例化,即使进程仅被加载用于后台广播事件。 因此,更重要的是使用Application类作为指向应用程序共享组件指针的存储库,而不是作为任何处理的地方!
我将下面列出的Singletons列表从以前的StackExchange链接中偷走:
并添加我自己的:
创建这个子类
public class MyApp extends Application {
String foo;
}
在AndroidManifest.xml中添加android:name
例
<application android:name=".MyApp"
android:icon="@drawable/icon"
android:label="@string/app_name">
Soonil为应用程序保留状态的方式提出的建议很好,但它有一个弱点 - 有些情况下操作系统会杀死整个应用程序进程。 这里是关于这个的文档 - 进程和生命周期。
考虑一个案例 - 你的应用进入后台,因为有人打电话给你(电话应用现在处于前台)。 在这种情况下&&在其他一些条件下(检查上面的链接可能是什么),操作系统可能会终止您的应用程序进程,包括Application
子类实例。 结果是国家失去了。 当您稍后返回到应用程序时,操作系统将恢复其活动堆栈和Application
子类实例,但myState
字段将为null
。
AFAIK,保证国家安全的唯一方法是使用任何形式的持久化状态,例如使用专用于应用程序文件或SharedPrefernces
(它最终使用专用于内部文件系统中的应用程序文件)。