tangguo

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

android

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

在主要活动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。

有谁知道如何设置全局变量以避免在用户已经成功通过身份验证后出现登录表单?


阅读 1190

收藏
2020-10-19

共1个答案

一尘不染

我在09年Android相对较新的时候就写下了这个答案,并且在Android开发中有很多尚未完善的领域。我在本文的底部添加了很长的附录,以解决一些批评,并详细说明了我对使用Singleton而不是对Application进行子类化时的哲学分歧。阅读它需要您自担风险。

原始答案:

您遇到的更普遍的问题是如何在多个活动和应用程序的所有部分之间保存状态。静态变量(例如,单例)是实现此目的的常见Java方法。但是,我发现,Android中一种更优雅的方法是将您的状态与Application上下文相关联。

如您所知,每个Activity也是一个Context,它是广义上有关其执行环境的信息。您的应用程序还具有上下文,Android保证它会在您的应用程序中作为单个实例存在。

这样做的方法是创建自己的android.app.Application子类,然后在清单的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)在下面提到的那样,完全有可能实例化您的流程,以处理后台广播事件。如果您的广播处理不需要这种状态,则可能只是无所事事地完成了一系列复杂而缓慢的操作。延迟实例化是此处的游戏名称。以下是使用应用程序的稍微复杂的方法,除了最简单的使用方法之外,其他方法都更有意义:

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:同样,如反咖啡厅所评论,为了正确地将应用程序替代项与应用程序相关联,清单文件中必须有一个标记。同样,请参阅Android文档以获取更多信息。一个例子:

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

注意2: user608578在下面询问如何与管理本机对象生命周期一起工作。我丝毫没有加快在Android上使用本机代码的速度,也没有资格回答如何与我的解决方案进行交互。如果有人对此有答案,我愿意将其归功于此,并将信息放在此文章中,以实现最大的可视性。

附录:

正如某些人指出的那样,这不是持久状态的解决方案,我也许应该在原始答案中更加强调这一点。即,这并不意味着它是一种用于保存用户或其他信息的解决方案,这些信息或信息将在应用程序的整个生命周期中保持不变。因此,我认为下面的大多数批评都与应用程序在任何时候被杀死等无关,因为任何需要保留到磁盘的内容都不应通过Application子类存储。它旨在成为一种解决方案,用于存储临时的,易于重新创建的应用程序状态(例如,是否已登录用户)和本质上是单个实例的组件(例如,应用程序网络管理器)(不是单例!)。

Dayerman一直很友善地指出与Reto Meier和Dianne Hackborn的有趣对话,在该对话中,不鼓励使用Application子类来支持Singleton模式。Somatik还早些时候指出了这种性质的东西,尽管当时我没有看到。由于Reto和Dianne在维护Android平台方面所扮演的角色,因此我不能真诚地建议您忽略他们的建议。他们说的去。我确实不同意关于优先选择Singleton而不是Application子类的观点。在我的不同意中,我将利用在StackExchange对Singleton设计模式的解释中最好地解释的概念,这样我就不必在此答案中定义术语。我强烈建议先跳过链接,然后再继续。逐点:

Dianne指出:“没有理由从Application进行子类化。这与创建单例没有什么不同……”第一个主张是不正确的。这有两个主要原因。1)Application类为应用程序开发人员提供了更好的生命周期保证;它保证了应用程序的生命周期。单例并没有明确地依赖于应用程序的生命周期(尽管有效)。对于您的普通应用程序开发人员来说,这可能不是问题,但是我认为这正是Android API应该提供的合同类型,并且通过最大程度地减少关联的生命周期,它还为Android系统提供了更大的灵活性。数据。2)Application类为应用程序开发人员提供了状态的单个实例持有者,这与单身汉的州持有人有很大的不同。有关差异的列表,请参见上面的Singleton说明链接。

Dianne继续说道:“ …将来很可能会后悔,因为您发现您的Application对象变成了本应独立的应用程序逻辑的一团糟。” 这当然不是不正确的,但这不是选择Singleton而不是Application子类的原因。Diane的论点都没有提供使用Singleton比Application子类更好的理由,她试图建立的唯一理由是使用Singleton并不比Application子类差,我认为这是错误的。

她继续说:“这更自然地导致您应该如何管理这些东西-根据需要初始化它们。” 这忽略了以下事实:您也没有理由也无法使用Application子类按需初始化。再次没有区别。

Dianne最后说:“框架本身为应用程序维护的所有少量共享数据都有成千上万的单例,例如已加载资源的缓存,对象池等。它运作良好。” 我不是在争论使用Singletons不能正常工作或不是合法的选择。我在争辩说,Singletons与Android系统之间的合同不如使用Application子类强大,而且使用Singletons通常指向不灵活的设计,这种设计不容易修改,并且会导致很多问题。恕我直言,Android API为开发人员应用程序提供的强大合同是使用Android进行编程的最吸引人和令人愉悦的方面之一,它有助于导致早期开发人员的采用,从而推动了Android平台取得今天的成功。

Dianne在下面也进行了评论,并提到了使用Application子类的其他缺点,它们可能会鼓励或简化编写性能较低的代码的过程。这是真的,我已经编辑了此答案,以强调此处考虑性能的重要性,如果您正在使用应用程序子类化,则应采用正确的方法。正如Dianne所说,重要的是要记住,即使只为后台广播而加载该进程,您的Application类也会在每次加载该进程时实例化(如果您的应用程序在多个进程中运行,则可能一次被多次!)。事件。因此,重要的是将Application类更多地用作指向应用程序共享组件的指针的存储库,而不是用作进行任何处理的地方!

我留下了以下从SingleStack链接中窃取的Singletons的缺点:

  • 无法使用抽象或接口类;
  • 无法分类;
  • 整个应用程序之间的高度耦合(难以修改);
  • 难以测试(在单元测试中不能伪造/模拟);
  • 在可变状态下很难并行化(需要广泛的锁定);

并添加我自己的:

  • 不清楚且难以管理的终身合同,不适合Android(或大多数其他)开发;
2020-10-19