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

在主要活动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,以避免在用户成功通过身份验证后出现登录表单吗?

推荐答案

我在09年写下了这个答案,当时安卓系统还比较新,安卓系统开发中有很多不成熟的领域.我在这篇文章的底部添加了一个很长的附录,解决了一些批评,并详细说明了我在使用单例而不是子类化应用程序方面的一个哲学分歧.阅读本书的风险自负.

ORIGINAL ANSWER:

您遇到的更普遍的问题是如何跨多个活动和应用程序的所有部分保存状态.静态变量(例如单例变量)是实现这一点的常用Java方法.然而,我发现,在Android中,一种更优雅的方式是将您的状态与应用程序上下文关联起来.

正如您所知,每个活动也是一个上下文,它是关于其执行环境的最广泛意义上的信息.你的应用程序也有一个上下文,Android保证它将作为一个实例存在于你的应用程序中.

实现这一点的方法是创建自己的子类android.app.Application,然后在 list 中的应用程序标记中指定该类.现在,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();
    ...
  }
}

这在本质上与使用静电变量或单例具有相同的效果,但是可以很好地集成到现有的安卓框架中.请注意,这不能跨进程工作(如果您的应用程序是少数拥有多个进程的应用程序之一).

从上面的例子中需要注意的事项;假设我们做了如下事情:

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

虽然我更喜欢应用程序子类化,而不是在这里使用单例作为更优雅的解决方案,但如果真的有必要,我宁愿开发人员使用单例,而不是完全不考虑将状态与应用程序子类关联的性能和多线程含义.

NOTE 1:也正如Antiafe所 comments 的,为了正确地将应用程序覆盖绑定到您的应用程序, list 文件中需要一个标记.同样,请参阅Android文档了解更多信息.例如:

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

NOTE 2:用户608578在下面询问如何管理本机对象生命周期.我在Android上使用原生代码的速度一点也不快,我也没有资格回答这将如何与我的解决方案交互.如果有人对此有答案,我愿意信任他们,并将信息放在这篇文章中,以获得最大的可见度.

ADDENDUM:

正如一些人所指出的,这是persistent状态的一个解决方案,也许我应该在最初的答案中更加强调这一点.也就是说,这并不是一个用于保存用户或其他信息的解决方案,这些信息将在应用程序的整个生命周期中持久化.因此,我认为大多数批评下面的应用程序被杀害在任何时候,等等…毫无意义,因为任何需要持久化到磁盘的东西都不应该通过应用程序子类存储.它是一种解决方案,用于存储临时的、易于重新创建的应用程序状态(例如,用户是否登录)和组件(例如,应用程序网络管理器)(NOT singleton!)在自然界.

Dayerman非常友好地指出了一个有趣的conversation with Reto Meier and Dianne Hackborn点,其中不鼓励使用Application子类,而是支持Singleton模式.索马蒂克早些时候也指出了这种性质的东西,尽管我当时没有看到.由于Reto和Dianne在维护Android平台方面所扮演的角色,我不能真诚地建议忽略他们的建议.他们说什么,就说什么.我确实希望不同意关于更喜欢Singleton而不是Application子类的意见.在我的异议中,我将使用this StackExchange explanation of the Singleton design pattern中最好解释的概念,这样我就不必在这个答案中定义术语.我强烈建议在继续之前浏览一下链接.逐点:

Dianne说,"没有理由从Application中继承子类,这与创建一个单件没有什么不同……"这第一个说法是不正确的.这主要有两个原因.1)Application类为应用程序开发人员提供了更好的生命周期保证,保证了应用程序的生命周期.单例没有显式地绑定到应用程序的生存期(尽管它是有效的).对于普通的应用程序开发人员来说,这可能不是问题,但我认为这正是Android API应该提供的合约类型,而且它还通过最小化关联数据的生命周期为Android系统提供了更大的灵活性.2)Application类为应用程序开发人员提供了单个实例持有者的状态,这与Singleton的状态持有者有很大的不同.有关不同之处的列表,请参阅上面的单例说明链接.

Dianne继续说道,"……在future ,当你发现你的应用程序对象变成了一大堆本应是独立的应用程序逻辑时,你可能会后悔."这当然不是错误的,但这并不是 Select 单例而不是应用程序子类的原因.Diane的所有论点都没有提供使用单例优于应用程序子类的理由,她只是试图证明使用单例并不比应用程序子类差,我认为这是错误的.

她继续说,"这会更自然地 bootstrap 你如何管理这些东西——按需初始化它们."这忽略了一个事实,即没有理由不能使用应用程序子类按需初始化.同样,这没有区别.

Dianne以"框架本身有大量的单 routine 序来处理它为应用程序维护的所有小共享数据,比如加载资源的缓存、对象池等.它工作得很好."我并不是说使用单身人士不能很好地工作,或者不是一种合法的 Select .我认为单例并不像使用Application子类那样提供与Android系统的牢固契约,而且使用单例通常会导致设计不灵活,不易修改,并且会导致许多问题.IMHO,Android API为开发人员应用程序提供的强大合同是Android编程最吸引人和最令人愉快的方面之一,并帮助导致早期开发人员采用Android平台,推动了Android平台取得今天的成功.建议使用Singletons隐含地背离了强大的API契约,在我看来,这削弱了Android框架.

Dianne在下面也发表了 comments ,提到了使用应用程序子类的另一个缺点,它们可能会鼓励或使编写性能更低的代码变得更容易.这是非常正确的,我对这个答案进行了编辑,以强调在这里考虑性能的重要性,如果使用应用程序子类化,请采取正确的方法.正如Dianne所说,重要的是要记住,每次加载进程时,应用程序类都会被实例化(如果应用程序在多个进程中运行,可能会一次实例化多次!)即使进程仅为后台广播事件加载.因此,重要的是将应用程序类更多地用作指向应用程序共享组件的指针的存储库,而不是用作进行任何处理的地方!

我给大家留下以下单亲家庭的缺点列表,这些都是从之前的StackExchange链接中偷来的:

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

再加上我自己的:

  • 不明确且难以管理的终身合同不适合Android(或大多数其他)开发;

Android相关问答推荐

如何完全隐藏的元素堆叠在CardView?

如何处理穿戴构图上的长点击事件?

安卓Gradle看不到dagger 柄

在命令行Android应用程序开发中苦苦挣扎

请求标头为空/无法通过拦截器获取

Android系统应用程序启用编程以太网网络共享

返回并再次打开webview时webview无法打开相机

如何在jetpack compose中使可组合的屏幕zoom 到不同的手机(屏幕)尺寸?

尽管我在onCreate()期间已经初始化,但仍出现未初始化的late init变量错误

Android Compose 为什么 Checkbox 在父对象可点击时需要 onCheckChanged?

具有数据库和升级潜力的移动应用程序开发(Android)供朋友使用

在 MVVM Jetpack Compose 上添加依赖项时出现重复类错误

Android 设备断开连接后发送的 BLE 蓝牙数据

在 Compose 中停止键盘将顶部应用栏推离屏幕

Jetpack Compose Canvas drawText colored颜色 混合?

每次在 Jetpack Compose 中调用导航

Compose `Icons.Outlined.Star` 没有概述

Android活动系统导航栏 colored颜色 ?

Jetpack Compose Material3 - switch 标签

如何在 flow.stateIn() 之后从流中的另一个函数发出emits ?