当前位置:  开发笔记 > 编程语言 > 正文

如何在Android中声明全局变量?

如何解决《如何在Android中声明全局变量?》经验,为你挑选了7个好方法。

我正在创建一个需要登录的应用程序.我创建了主要和登录活动.

在主要活动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以避免在用户已经成功验证后出现登录表单?



1> sooniln..:

我在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(或大多数其他)开发;


谢谢Soonil - 这些答案是我非常喜欢Stack Overflow的原因.很好!
另外,让我们在这里非常清楚 - 当我们谈论你实际上在单身人士和另一种不是全球性的方法之间做出选择的情况时,你所有反对单身人士的论据都是完全有效的.单身人士是全球性的,所有关于全局变量的警告都适用.但是,*应用程序也是单例*.你不是通过切换到子类化应用程序来逃避这些问题,应用程序与单例完全相同(但更糟糕),它只是让你欺骗自己,你正在做一些更干净的事情.但你不是.
让我再重复一次,你不应该使用Application for globals.它是没有用的,没有单身人士的好处,并且可能是有害的,例如损害启动过程的性能.在创建应用程序时,您不知道正在为您创建的进程.通过懒惰地根据需要初始化单身人士,你只需要做必要的工作.例如,如果您的进程正在启动以处理有关某些后台事件的广播,则没有理由初始化UI所需的任何全局状态.
Soonil,你的答案是对的,但是你能否注意到我们应该将添加到Android Manifest文件中?
对于任何想知道如何"在清单中的应用程序标签中指定该类"的人来说,在撰写本文时,还有另外两个答案用于描述如何操作此问题(使用android:name),一个用ebuprofen和一个迈克布朗
最重要的是,如果您的应用程序使用多个进程,则Application对象意味着您需要在所有进程中执行所有全局初始化(时间和内存消耗).哎哟.在某些情况下,您的Application对象将不会被创建,特别是在恢复期间,可能会使您失望.
@Soonil:我刚刚添加了一个答案,实际上应该是对你答案的评论.但是,将它作为评论放在这里太长了.
看起来像这里解释的https://plus.google.com/u/0/117230458394250799679/posts/DsfpW51Vvow由核心Android工程师之一,不建议使用Application来存储数据而不是单例.
@Somatik来自:developer.android.com/reference/android/app/Application.html"通常不需要子类应用程序.在大多数情况下,静态单例可以以更模块化的方式提供相同的功能......." - 对于谷歌来说,这真是一件非常奇怪的事情.很快就解释说,没有令人信服的理由支持单身人士.如果他们觉得有必要表明单身人士是另类,而不是声称自己更好,那么人们可以理解.一个人得到的印象是有人对单身人士有一种痴迷.
@sooniln:静态数据成员与`Application`和`ContentProvider`实例具有相同的生命周期:进程的生命周期.毕竟,`Application`只是框架类的静态数据成员.

2> Guillaume..:

创建这个子类

public class MyApp extends Application {
  String foo;
}

在AndroidManifest.xml中添加android:name



只是想说,这是在MAIN应用程序标签已经存在...这不是第二个:)必须学习艰难的方式.
为了它为我工作,我不得不删除"." 在".MyApp"中
只需在*主活动后声明*,否则可能无法安装/部署

3> Vit Khudenko..:

Soonil建议保持应用程序状态的方法很好,但它有一个弱点 - 有些情况下操作系统会杀死整个应用程序进程.这是关于此的文档 - 进程和生命周期.

考虑一个案例 - 你的应用程序进入后台,因为有人在给你打电话(手机应用程序现在在前台).在这种情况下&&在一些其他条件下(检查上面的链接,它们可能是什么)操作系统可能会杀死你的应用程序进程,包括Application子类实例.结果,国家失去了.当您稍后返回应用程序时,操作系统将恢复其活动堆栈和Application子类实例,但该myState字段将是null.

AFAIK,保证状态安全的唯一方法是使用任何类型的持久状态,例如使用private作为应用程序文件或SharedPrefernces(它最终使用private作为内部文件系统中的应用程序文件).


+1用于持久化`SharedPreferences`; 这就是我看到它完成的方式.我发现滥用偏好系统保存状态很奇怪,但它运作良好,问题只是一个术语问题.
首选项,数据库,文件序列化等.如果用户使用onSaveInstanceState,则每个活动都可以保持状态,但如果用户退出活动并将其从历史堆栈中删除,强制关闭或关闭其设备,则无效.
在我看来,这是正确的答案.依赖于跨活动存在的相同应用程序实例是一个错误.根据我的经验,Android背景下完全拆除并重新创建整个过程是很常见的.背景可能意味着启动摄像头意图,浏览器意图或接听电话.

4> Gimbl..:

只是一张纸条..

加:

android:name=".Globals"

或者您将子类命名为现有 标记的任何内容.我一直试图在清单中添加另一个标签,并会得到一个例外.


**好** .**Bad**

5> 小智..:

我找不到如何指定应用程序标签,但经过大量的谷歌搜索后,从清单文件docs中可以看出:除了应用程序节中的默认图标和标签外,还使用了android:name.

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

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



6> user608578..:

如何确保采用这种全局结构收集本机内存?

活动有一个onPause/onDestroy()在销毁时调用的方法,但Application类没有等价物.建议使用什么机制来确保在应用程序被终止或任务堆栈放在后台时,全局结构(特别是包含对本机内存的引用的结构)被适当地进行垃圾收集?



7> Anand..:

只需要定义一个如下所示的应用程序名称:



推荐阅读
手机用户2402851155
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有