singleton - 安卓如何声明全局变量?

  显示原文与译文双语对照的内容

我正在创建一个需要登录的应用程序。 我创建了main和登录 Activity 。

在主 Activity 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

有人知道如何设置变量全局,以避免在用户已经成功身份验证后出现登录窗体?

谢谢!

时间:

当Android相对较新时,我在'09上写了这个答案,在Android开发中还没有很好的地方。 使用的使用单例模式而不是子类化application,我已经添加了一个长的齿顶底部的这篇文章,解决了一些批评,并详细列出一个哲学分歧我有。可以 以自己的风险阅读。

原始答案:

你遇到的更一般的问题是如何跨多个活动和应用程序的所有部分保存状态。 静态变量( 例如一个单独的) 是实现这里目的的通用Java方法。 然而,我发现在安卓中一种更加优雅的方式是将你的状态与应用程序上下文关联起来。

就像你所知道的,每个 Activity 也是一个上下文,它是关于它的执行环境的广义意义上的信息。 你的应用程序也有一个上下文,安卓保证它将作为一个实例存在于你的应用程序中。

这样做的方法是创建你自己的android.app.Application的子类,然后在你的Manifest 中的应用程序标记中指定该类。 现在,Android将自动创建该类的一个实例并使它对你的整个应用程序可用。 使用它从任何 Context.getApplicationContext() 方法可以访问 下面是一个极其简化的示例,需要注意以下几点:


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,你的进程完全可能被实例化以处理后台广播事件。 如果你的广播处理不需要这种状态,你可能只做了一系列复杂而缓慢的操作。 迟缓实例化是这里游戏的名称。 下面是一个使用应用程序的稍微复杂一点的方法,它对除最简单的用途之外的任何东西都更有意义:


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();
. . .
 }
}

应用程序subclass,而我更喜欢应用程序生成子类更优雅的解决办法,我倒希望开发人员使用数据在这里使用单例模式单例模式如果通过而没有想真的有必要在所有通过的性能和多线程处理涵义的缔合子来

注意也 1: 作为 anticafe Manifest 评论的,为了正确地把你的应用程序中重写到应用程序中一个标记是必要的文件。 再次,查看Android文档了解更多信息。 一个示例:


<application
 android:name="my.application.MyApp" 
 android:icon="..."
 android:label="...">
</application>

管理本机对象 lifecycles, 注意 2: user608578要求低于这个works.如何 我没有加快使用本机代码和Android的速度,我没有资格回答如何与我的解决方案交互。 如果有人有这个问题的答案,我愿意相信他们并把信息放在这篇文章中以获得最大的可视性。

附录:

在原有的作为有些人曾提到,这是 answer,用于持久状态,这点我也许应该已经强调了多种视频的解决方案 换句话说,这不是用来保存用户或者其他信息的解决方案,这些信息意味着在应用程序生命周期之间保持持久。 因此,我认为下面的大多数批评都与在任何时候被杀死的应用程序有关,等等 。 ,因为任何需要持久化到磁盘的东西都不应该通过应用程序子类存储。 在 nature, ( 用户是否登录例如) 它的意图是要被用于存储临时的解决方案,轻松re-creatable应用程序状态和组成部分是单实例( 应用程序网络管理器例如) ( 单例 ). !

Dayerman已经很好地指出了与 Reto Meier和 Dianne Hackborn的有趣的对话,不鼓励使用应用程序子类来支持Singleton模式。 Somatik以前也指出过这种性质,虽然当时我没有看到它。 由于Reto和dianne在维护Android平台方面的作用,我无法相信忽略它们的建议。 他们说,然后是的我的确希望不同意这个观点,表述的内容与上偏爱偏爱是如何运作的单一实例应用程序子类。 在我的不一致中,我将使用中最好解释的概念这个StackExchange设计模式的解释,所以我不需要在这个答案中定义术语。 我强烈鼓励在继续之前浏览链接。 逐点:

Dianne状态,"没有理由从应用程序中创建子类。 这与制作一个单一的。。"是不一样的"第一个声明不正确。 这有两个主要原因。 1 ) 应用程序类为应用程序开发人员提供更好的生命周期保证;它保证了应用程序的生命周期。 一个单例没有显式绑定到应用程序( 虽然它是有效的)的生命周期。 这可能是你的平均应用程序开发者的一个 non-issue,但我认为这正是 Android API应该提供的契约类型,它也为Android系统提供了更多的灵活性,通过最小化相关数据的生命周期。 2 ) 应用程序类为应用程序开发人员提供了一个用于状态的单一实例 holder,这与一个单一的状态持有者非常不同。 有关差异的列表,请参见上面的单独解释链接。

Dianne继续,"当你发现你的应用程序对象变成了这个巨大混乱的应用程序逻辑时,你可能会后悔。"当然是不正确的,但这不是在应用程序子类中选择Singleton的原因。 中没有一个参数的黛安注明原因的,她只会尝试使用单体模式也是比应用程序子类构造使用单体模式也是最糟的是,一个应用程序子类化,以我认为是假的。

她继续,"这就更自然地说明了你应该如何管理这些东西--按需求初始化它们。"忽略了这样一个事实,即没有理由使用应用程序子类来初始化需求。 又没有区别了。

Dianne以"该框架本身拥有极其丰富和成千上万的单身人士对于所有资料很少的共享数据为该应用,如高速缓存中已经加载的资源,池的对象,它维护 等等 它运行得很顺利。"结束。我不是说使用单例程序不能正常工作或者不是一个合法的替代。 我所主张的,单例模式不提供尽可能强的一份与安卓系统使用应用程序通常指向子类,并进一步使用单例模式的灵活性设计,这是不容易修改,而且导致很多中出现的问题。 恕我直言,可以使用强合同Androidapi提供给开发人员的应用程序与Android是一个最吸引人的和令人愉悦的方面的编程,以及帮助过导致早期开发者的采用拉动安卓平台它有今天成功与否的力度。 建议使用单例模式隐含地背离一个强大的API契约,在我看来,这削弱了Android框架。

Dianne也在下面发表了评论,它提到了使用应用程序子类的另一个缺点,它们可能鼓励或者更容易编写更少的性能代码。 这是真的,我已经编辑了这个答案,强调在这里考虑性能的重要性,如果你使用应用程序子类化,那么采取正确的方法。 作为Dianne状态,重要的是记住每次加载进程时都要实例化应用程序类( 即使该进程只加载后台广播事件) 。 因此,更重要的是使用应用程序类作为指向应用程序共享组件的指针的储存库,而不是做任何处理 !

我将以下列表中的缺点留给你,因为从早期的StackExchange链接中偷取:

  • 无法使用抽象或者接口类;
  • 无法子类化;
  • 跨应用程序( 难以修改)的高耦合;
  • 难以测试( 无法在单元测试中伪造/mock ) ;
  • 在可变状态( 需要广泛的锁定)的情况下难于并行化;

并添加我自己的:

  • 不明确和不可管理的生命周期合同不适用于 Android ( 或者大多数其他) 开发;

创建这里子类


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 子类实例。 结果是状态丢失。 以后返回应用程序时,操作系统将恢复它的Activity 堆栈和 Application 子类实例,但是 myState 字段将是 null

但是,保证状态安全的唯一方法是使用任何一种持久状态,比如 使用私有的应用程序文件或者 SharedPrefernces ( 它最终为内部 文件系统 中的应用程序文件使用私有的) 。

只是一张纸条。

添加:


android:name=".Globals"

或者你所命名你的子类到现有<application> 标记。 我一直试图在清单中添加另一个 <application> 标记,并得到一个例外。

我找不到如何指定应用程序标签,但在大量的google搜索之后,从清单文件文档中显而易见: 使用 android: 名称,除了默认的icon 和应用程序节中的标签。

android:name 为应用程序实现的应用程序子类的完全限定名。 当应用程序进程启动时,这个类将在应用程序的任何组件之前实例化。

子类是可选的;大多数应用程序都不需要一个。 在没有子类的情况下,Android使用基本应用程序类的一个实例。

如何确保使用此类全局结构收集本机内存?

活动有一个在销毁时调用的onPause/onDestroy() 方法,但应用程序类没有等效的。 建议使用什么机制来确保当应用程序被杀死或者将任务堆栈放入后台时,全局结构( 特别是那些包含本机内存的引用) 是正确收集的垃圾?

只需要定义如下的应用程序名称即可


<application

 android:name="ApplicationName" android:icon="@drawable/icon">


</Application>

如果某些变量存储在SQLite中,你必须在应用程序的大多数活动中使用它们。 那么应用程序也许是实现它的最好方法。 当应用程序启动时从数据库查询变量并将它们存储在一个字段中。 然后你可以在你的活动中使用这些变量。

所以找到正确的方法,没有最好的方法

就像上面讨论的那样,操作系统可以杀死没有任何通知( 没有onDestroy事件)的应用程序,所以没有办法保存这些全局变量。

SharedPreferences可能是一个解决方案,除了你有复杂的结构化变量( 在我的例子中,我有一个整数数组来存储用户已经处理的IDs ) 。 SharedPreferences的问题是每次需要值时都很难存储和检索这些结构。

在我的案例中我有一个后台服务,所以我可以将这些变量移动到那里,因为服务有onDestroy事件,我可以轻松地保存这些值。

你可以有一个静态字段来存储这种状态。 或者把它放到资源包并从 onCreate(Bundle savedInstanceState) 上恢复。 确保你完全理解 Android 应用 管理生命周期( 例如。 为什么在键盘方向更改时调用 login()。

...