我正在创建一个需要登录的应用程序.我创建了主要和登录活动.
在主要活动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
.
有没有人知道如何设置变量global以避免在用户已经成功验证后出现登录表单?
我在2009年写了这个答案,当时Android相对较新,Android开发中还有许多不成熟的领域.我在这篇文章的底部添加了一个长篇补遗,解决了一些批评,并详细说明了我对使用单身人士而不是继承应用程序的哲学分歧.阅读它需要您自担风险.
原始答案:
您遇到的更普遍的问题是如何跨多个活动和应用程序的所有部分保存状态.静态变量(例如,单例)是实现此目的的常见Java方法.然而,我发现在Android中更优雅的方式是将您的状态与应用程序上下文相关联.
如您所知,每个Activity也是一个Context,它是有关其最广泛意义上的执行环境的信息.您的应用程序还具有上下文,Android保证它将作为整个应用程序中的单个实例存在.
这样做的方法是创建自己的android.app.Application子类,然后在清单中的应用程序标记中指定该类.现在,Android将自动创建该类的实例,并使其可用于整个应用程序.您可以context
使用该Context.getApplicationContext()
方法从任何方法访问它(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子类化在这里使用单例作为更优雅的解决方案,但我宁愿开发人员使用单例,如果真的有必要通过将状态与Application子类关联的性能和多线程含义完全不考虑.
注1:同样作为anticafe评论,为了正确地将您的Application覆盖绑定到您的应用程序,清单文件中需要一个标记.再次,请参阅Android文档以获取更多信息.一个例子:
注2: user608578在下面询问如何管理本机对象生命周期.我没有最快速地使用Android的本机代码,我没有资格回答这将如何与我的解决方案进行交互.如果有人确实对此有了答案,我愿意将它们归功于并将这些信息放在这篇文章中以获得最大的可见度.
附录:
有些人已经注意到,这不是持久状态的解决方案,我可能应该在原始答案中更多地强调这一点.即,这并不意味着保存用户或其他信息的解决方案,这些信息旨在跨应用程序生命周期持久化.因此,我认为下面的大多数批评都与应用程序在任何时候被杀死有关......等等,因为任何需要持久保存到磁盘的东西都不应该通过Application子类存储.它本质上是一种解决方案,用于存储临时的,易于重新创建的应用程序状态(例如,用户是否登录)和单个实例的组件(例如应用程序网络管理器)(非单例!).
Dayerman非常友好地指出了与Reto Meier和Dianne Hackborn的有趣对话,其中不鼓励使用Application子类来支持Singleton模式.Somatik早些时候也指出了这种性质,虽然我当时没有看到它.由于Reto和Dianne在维护Android平台方面的作用,我不能真诚地推荐忽视他们的建议.他们说的是什么.我希望不同意关于优先使用Singleton而不是Application子类的意见.在我的分歧中,我将使用在SingleEx设计模式的StackExchange解释中最佳解释的概念,这样我就不必在这个答案中定义术语.我强烈建议在继续之前浏览链接.逐点:
Dianne表示,"没有理由从申请中继承.这与制作单身人员没有什么不同......"这第一个主张是不正确的.这有两个主要原因.1)Application类为应用程序开发人员提供更好的终身保证; 它保证具有应用程序的生命周期.单例并不明显与应用程序的生命周期相关联(尽管它是有效的).对于普通应用程序开发人员来说,这可能不是问题,但我认为这正是Android API应该提供的合同类型,并且它通过最小化相关联的生命周期为Android系统提供了更大的灵活性.数据.2)Application类为应用程序开发人员提供了一个state实例持有者,这与州的Singleton持有者非常不同.有关差异的列表,请参阅上面的Singleton说明链接.
Dianne继续说道,"...当你发现你的应用程序对象变成了应该是独立应用程序逻辑的混乱时,你可能会感到遗憾." 这当然不是不正确的,但这不是选择Singleton over Application子类的原因.Diane的论点都没有提供使用Singleton比Application子类更好的原因,她试图建立的是使用Singleton并不比Application子类差,我认为这是假的.
她继续说道,"这更自然地导致你应该如何管理这些东西 - 按需初始化它们." 这忽略了这样一个事实,即没有理由不能使用Application子类按需初始化.再次没有区别.
Dianne的结尾是"框架本身为应用程序维护的所有小共享数据提供了大量的单例,例如加载资源的缓存,对象池等等.它非常有效." 我并不是说使用单身人士不能正常工作或不是合法的选择.我认为Singletons没有提供与使用Application子类的Android系统强大的合同,而且使用Singletons通常指向不灵活的设计,这不容易修改,并且导致许多问题.恕我直言,Android API为开发者应用程序提供的强大合同是Android编程中最吸引人和最令人愉悦的方面之一,并有助于早期开发人员的采用,从而推动Android平台取得今天的成功.建议使用Singletons隐含地摆脱强大的API合约,在我看来,削弱了Android框架.
Dianne也在下面评论过,提到使用Application子类的另一个缺点是,他们可能会鼓励或者更容易编写性能代码更少.这是非常正确的,我编辑了这个答案,强调了在这里考虑perf的重要性,并且如果你正在使用Application子类,那么采取正确的方法.正如Dianne所述,重要的是要记住每次加载进程时都会实例化Application类(如果你的应用程序在多个进程中运行,可以多次执行!)即使进程只是为后台广播加载事件.因此,重要的是将Application类更多地用作指向应用程序共享组件的指针的存储库,而不是作为进行任何处理的地方!
我从以前的StackExchange链接中窃取了以下Singletons的缺点列表:
无法使用抽象或接口类;
无法继承;
整个应用程序的高耦合(难以修改);
难以测试(不能在单元测试中伪造/模拟);
在可变状态的情况下难以并行化(需要大量锁定);
并添加我自己的:
不清楚且无法管理的终身合同不适合Android(或大多数其他)开发;
创建这个子类
public class MyApp extends Application { String foo; }
在AndroidManifest.xml中添加android:name
例
Soonil建议保持应用程序状态的方法很好,但它有一个弱点 - 有些情况下操作系统会杀死整个应用程序进程.这是关于此的文档 - 进程和生命周期.
考虑一个案例 - 你的应用程序进入后台,因为有人在给你打电话(手机应用程序现在在前台).在这种情况下&&在一些其他条件下(检查上面的链接,它们可能是什么)操作系统可能会杀死你的应用程序进程,包括Application
子类实例.结果,国家失去了.当您稍后返回应用程序时,操作系统将恢复其活动堆栈和Application
子类实例,但该myState
字段将是null
.
AFAIK,保证状态安全的唯一方法是使用任何类型的持久状态,例如使用private作为应用程序文件或SharedPrefernces
(它最终使用private作为内部文件系统中的应用程序文件).
只是一张纸条..
加:
android:name=".Globals"
或者您将子类命名为现有
标记的任何内容.我一直试图
在清单中添加另一个标签,并会得到一个例外.
我找不到如何指定应用程序标签,但经过大量的谷歌搜索后,从清单文件docs中可以看出:除了应用程序节中的默认图标和标签外,还使用了android:name.
android:name为应用程序实现的Application子类的完全限定名称.启动应用程序进程时,将在任何应用程序的组件之前实例化此类.
子类是可选的; 大多数应用程序不需要一个.在没有子类的情况下,Android使用基本Application类的实例.
如何确保采用这种全局结构收集本机内存?
活动有一个onPause/onDestroy()
在销毁时调用的方法,但Application类没有等价物.建议使用什么机制来确保在应用程序被终止或任务堆栈放在后台时,全局结构(特别是包含对本机内存的引用的结构)被适当地进行垃圾收集?
只需要定义一个如下所示的应用程序名称: