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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/708012/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-20 02:24:16  来源:igfitidea点击:

How to declare global variables in Android?

androidsingletonglobal-variablesstate

提问by Niko Gamulin

I am creating an application which requires login. I created the main and the login activity.

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

In the main activity onCreatemethod I added the following condition:

在主要活动onCreate方法中,我添加了以下条件:

public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    ...

    loadSettings();
    if(strSessionString == null)
    {
        login();
    }
    ...
}

The onActivityResultmethod which is executed when the login form terminates looks like this:

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

The problem is the login form sometimes appears twice (the login()method is called twice) and also when the phone keyboard slides the login form appears again and I guess the problem is the variable strSessionString.

问题是登录表单有时会出现两次(该login()方法被调用两次),而且当手机键盘滑动时,登录表单再次出现,我猜问题是变量strSessionString.

Does anyone know how to set the variable global in order to avoid login form appearing after the user already successfully authenticates?

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

回答by sooniln

I wrote this answer back in '09 when Android was relatively new, and there were many not well established areas in Android development. I have added a long addendum at the bottom of this post, addressing some criticism, and detailing a philosophical disagreement I have with the use of Singletons rather than subclassing Application. Read it at your own risk.

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

ORIGINAL ANSWER:

原始答案:

The more general problem you are encountering is how to save state across several Activities and all parts of your application. A static variable (for instance, a singleton) is a common Java way of achieving this. I have found however, that a more elegant way in Android is to associate your state with the Application context.

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

As you know, each Activity is also a Context, which is information about its execution environment in the broadest sense. Your application also has a context, and Android guarantees that it will exist as a single instance across your application.

众所周知,每个Activity也是一个Context,从广义上讲,它是关于它的执行环境的信息。您的应用程序还有一个上下文,Android 保证它将作为整个应用程序中的单个实例存在。

The way to do this is to create your own subclass of android.app.Application, and then specify that class in the application tag in your manifest. Now Android will automatically create an instance of that class and make it available for your entire application. You can access it from any contextusing the Context.getApplicationContext()method (Activityalso provides a method getApplication()which has the exact same effect). Following is an extremely simplified example, with caveats to follow:

这样做的方法是创建您自己的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();
    ...
  }
}

This has essentially the same effect as using a static variable or singleton, but integrates quite well into the existing Android framework. Note that this will not work across processes (should your app be one of the rare ones that has multiple processes).

这与使用静态变量或单例的效果基本相同,但可以很好地集成到现有的 Android 框架中。请注意,这不适用于跨进程(如果您的应用程序是具有多个进程的罕见应用程序之一)。

Something to note from the example above; suppose we had instead done something like:

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

class MyApp extends Application {

  private String myState = /* complicated and slow initialization */;

  public String getState(){
    return myState;
  }
}

Now this slow initialization (such as hitting disk, hitting network, anything blocking, etc) will be performed every time Application is instantiated! You may think, well, this is only once for the process and I'll have to pay the cost anyways, right? For instance, as Dianne Hackborn mentions below, it is entirely possible for your process to be instantiated -just- to handle a background broadcast event. If your broadcast processing has no need for this state you have potentially just done a whole series of complicated and slow operations for nothing. Lazy instantiation is the name of the game here. The following is a slightly more complicated way of using Application which makes more sense for anything but the simplest of uses:

现在每次应用程序实例化时都会执行这种缓慢的初始化(例如命中磁盘,命中网络,任何阻塞等)!你可能会想,嗯,这个过程只有一次,反正我必须支付费用,对吧?例如,正如 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();
    ...
  }
}

While I prefer Application subclassing to using singletons here as the more elegant solution, I would rather developers use singletons if really necessary over not thinking at all through the performance and multithreading implications of associating state with the Application subclass.

虽然我更喜欢应用程序子类而不是在此处使用单例作为更优雅的解决方案,但我更愿意开发人员在确实需要时使用单例,而不是完全不考虑将状态与应用程序子类相关联的性能和多线程影响。

NOTE 1:Also as anticafe commented, in order to correctly tie your Application override to your application a tag is necessary in the manifest file. Again, see the Android docs for more info. An example:

注意 1:正如 anticafe 所评论的,为了将您的应用程序覆盖正确地绑定到您的应用程序,清单文件中需要一个标签。同样,请参阅 Android 文档以获取更多信息。一个例子:

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

NOTE 2:user608578 asks below how this works with managing native object lifecycles. I am not up to speed on using native code with Android in the slightest, and I am not qualified to answer how that would interact with my solution. If someone does have an answer to this, I am willing to credit them and put the information in this post for maximum visibility.

注意 2:user608578 在下面询问如何管理本机对象生命周期。我丝毫没有跟上在 Android 上使用本机代码的速度,而且我没有资格回答这将如何与我的解决方案进行交互。如果有人对此有答案,我愿意相信他们并将信息放在这篇文章中以获得最大的可见性。

ADDENDUM:

附录:

As some people have noted, this is nota solution for persistentstate, something I perhaps should have emphasized more in the original answer. I.e. this is not meant to be a solution for saving user or other information that is meant to be persisted across application lifetimes. Thus, I consider most criticism below related to Applications being killed at any time, etc..., moot, as anything that ever needed to be persisted to disk should not be stored through an Application subclass. It is meant to be a solution for storing temporary, easily re-creatable application state (whether a user is logged in for example) and components which are single instance (application network manager for example) (NOTsingleton!) in nature.

正如一些人所指出的,这不是持久状态的解决方案,我可能应该在原始答案中更加强调这一点。即,这不是用于保存用户或其他旨在跨应用程序生命周期持久化的信息的解决方案。因此,我认为下面的大多数批评都与应用程序随时被杀死等有关……没有实际意义,因为任何需要持久保存到磁盘的东西都不应该通过应用程序子类存储。它是一种用于存储临时的、易于重新创建的应用程序状态(例如用户是否登录)和本质上是单一实例(例如应用程序网络管理器)(不是单例!)的组件的解决方案。

Dayerman has been kind enough to point out an interesting conversation with Reto Meier and Dianne Hackbornin which use of Application subclasses is discouraged in favor of Singleton patterns. Somatik also pointed out something of this nature earlier, although I didn't see it at the time. Because of Reto and Dianne's roles in maintaining the Android platform, I cannot in good faith recommend ignoring their advice. What they say, goes. I do wish to disagree with the opinions, expressed with regards to preferring Singleton over Application subclasses. In my disagreement I will be making use of concepts best explained in this StackExchange explanation of the Singleton design pattern, so that I do not have to define terms in this answer. I highly encourage skimming the link before continuing. Point by point:

Dayerman 非常友好地指出了与 Reto Meier 和 DianneHackborn 的有趣对话,其中不鼓励使用 Application 子类,而支持 Singleton 模式。Somatik 早些时候也指出了这种性质,尽管我当时没有看到。由于 Reto 和 Dianne 在维护 Android 平台方面的作用,我不能真诚地建议忽略他们的建议。他们说什么,去。我确实不同意关于更喜欢 Singleton 而不是 Application 子类的意见。在我的不同意见中,我将使用在StackExchange 对单例设计模式的解释中最好地解释的概念,这样我就不必在这个答案中定义术语。我强烈建议在继续之前浏览链接。逐点:

Dianne states, "There is no reason to subclass from Application. It is no different than making a singleton..." This first claim is incorrect. There are two main reasons for this. 1) The Application class provides a better lifetime guarantee for an application developer; it is guaranteed to have the lifetime of the application. A singleton is not EXPLICITLY tied to the lifetime of the application (although it is effectively). This may be a non-issue for your average application developer, but I would argue this is exactly the type of contract the Android API should be offering, and it provides much more flexibility to the Android system as well, by minimizing the lifetime of associated data. 2) The Application class provides the application developer with a single instance holder for state, which is very different from a Singleton holder of state. For a list of the differences, see the Singleton explanation link above.

Dianne 说,“没有理由从 Application 子类化。这与制作单例没有什么不同......” 第一个说法是不正确的。这有两个主要原因。1)Application类为应用开发者提供了更好的生命周期保障;它保证具有应用程序的生命周期。单例并没有明确地绑定到应用程序的生命周期(尽管它是有效的)。对于您的普通应用程序开发人员来说,这可能不是问题,但我认为这正是 Android API 应该提供的合同类型,并且通过最小化相关联的生命周期,它也为 Android 系统提供了更大的灵活性。数据。2) Application 类为应用程序开发人员提供了状态的单个实例持有者,这与状态的单例持有者非常不同。有关差异的列表,请参阅上面的 Singleton 解释链接。

Dianne continues, "...just likely to be something you regret in the future as you find your Application object becoming this big tangled mess of what should be independent application logic." This is certainly not incorrect, but this is not a reason for choosing Singleton over Application subclass. None of Diane's arguments provide a reason that using a Singleton is better than an Application subclass, all she attempts to establish is that using a Singleton is no worse than an Application subclass, which I believe is false.

Dianne 继续说道,“......很可能是你将来后悔的事情,因为你发现你的 Application 对象变成了应该是独立应用程序逻辑的大混乱。” 这当然没有错,但这不是选择 Singleton 而不是 Application 子类的原因。Diane 的论点都没有提供使用单例比应用程序子类更好的理由,她试图建立的是,使用单例并不比应用程序子类差,我认为这是错误的。

She continues, "And this leads more naturally to how you should be managing these things -- initializing them on demand." This ignores the fact that there is no reason you cannot initialize on demand using an Application subclass as well. Again there is no difference.

她继续说道,“这更自然地导致您应该如何管理这些东西——按需初始化它们。” 这忽略了一个事实,即您没有理由也不能使用 Application 子类按需初始化。再次没有区别。

Dianne ends with "The framework itself has tons and tons of singletons for all the little shared data it maintains for the app, such as caches of loaded resources, pools of objects, etc. It works great." I am not arguing that using Singletons cannot work fine or are not a legitimate alternative. I am arguing that Singletons do not provide as strong a contract with the Android system as using an Application subclass, and further that using Singletons generally points to inflexible design, which is not easily modified, and leads to many problems down the road. IMHO, the strong contract the Android API offers to developer applications is one of the most appealing and pleasing aspects of programming with Android, and helped lead to early developer adoption which drove the Android platform to the success it has today. Suggesting using Singletons is implicitly moving away from a strong API contract, and in my opinion, weakens the Android framework.

Dianne 以“框架本身为应用程序维护的所有小共享数据提供了大量的单例,例如加载资源的缓存、对象池等。它工作得很好。” 我并不是说使用 Singleton 不能正常工作或不是合法的替代方案。我认为单例没有像使用 Application 子类那样提供与 Android 系统的强大契约,而且使用单例通常指向不灵活的设计,不容易修改,并导致许多问题。恕我直言,Android API 为开发人员应用程序提供的强大合同是使用 Android 进行编程时最具吸引力和最令人愉悦的方面之一,并有助于导致早期的开发人员采用,从而推动 Android 平台取得今天的成功。

Dianne has commented below as well, mentioning an additional downside to using Application subclasses, they may encourage or make it easier to write less performance code. This is very true, and I have edited this answer to emphasize the importance of considering perf here, and taking the correct approach if you're using Application subclassing. As Dianne states, it is important to remember that your Application class will be instantiated every time your process is loaded (could be multiple times at once if your application runs in multiple processes!) even if the process is only being loaded for a background broadcast event. It is therefore important to use the Application class more as a repository for pointers to shared components of your application rather than as a place to do any processing!

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

I leave you with the following list of downsides to Singletons, as stolen from the earlier StackExchange link:

我给你留下了以下单例的缺点列表,这些缺点是从早期的 StackExchange 链接中窃取的:

  • Inability to use abstract or interface classes;
  • Inability to subclass;
  • High coupling across the application (difficult to modify);
  • Difficult to test (can't fake/mock in unit tests);
  • Difficult to parallelize in the case of mutable state (requires extensive locking);
  • 无法使用抽象类或接口类;
  • 无法进行子类化;
  • 整个应用程序的高耦合(难以修改);
  • 难以测试(在单元测试中不能伪造/模拟);
  • 在可变状态的情况下难以并行化(需要大量锁定);

and add my own:

并添加我自己的:

  • Unclear and unmanageable lifetime contract unsuited for Android (or most other) development;
  • 不适合Android(或大多数其他)开发的不明确和难以管理的终身合同;

回答by Guillaume

Create this subclass

创建这个子类

public class MyApp extends Application {
  String foo;
}

In the AndroidManifest.xml add android:name

在 AndroidManifest.xml 添加 android:name

Example

例子

<application android:name=".MyApp" 
       android:icon="@drawable/icon" 
       android:label="@string/app_name">

回答by Vit Khudenko

The suggested by Soonil way of keeping a state for the application is good, however it has one weak point - there are cases when OS kills the entire application process. Here is the documentation on this - Processes and lifecycles.

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

Consider a case - your app goes into the background because somebody is calling you (Phone app is in the foreground now). In this case && under some other conditions (check the above link for what they could be) the OS may kill your application process, including the Applicationsubclass instance. As a result the state is lost. When you later return to the application, then the OS will restore its activity stack and Applicationsubclass instance, however the myStatefield will be null.

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

AFAIK, the only way to guarantee state safety is to use any sort of persisting the state, e.g. using a private for the application file or SharedPrefernces(it eventually uses a private for the application file in the internal filesystem).

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

回答by Gimbl

Just a note ..

只是一个注释..

add:

添加:

android:name=".Globals"

or whatever you named your subclass to the existing<application>tag. I kept trying to add another <application>tag to the manifest and would get an exception.

或者您将子类命名为现有<application>标签的任何内容。我一直试图向<application>清单中添加另一个标签,但会出现异常。

回答by user608578

What about ensuring the collection of native memory with such global structures?

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

Activities have an onPause/onDestroy()method that's called upon destruction, but the Application class has no equivalents. What mechanism are recommended to ensure that global structures (especially those containing references to native memory) are garbage collected appropriately when the application is either killed or the task stack is put in the background?

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

回答by Mike Brown

I couldn't find how to specify the application tag either, but after a lot of Googling, it became obvious from the manifest file docs: use android:name, in addition to the default icon and label in the application stanza.

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

android:name The fully qualified name of an Application subclass implemented for the application. When the application process is started, this class is instantiated before any of the application's components.

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

The subclass is optional; most applications won't need one. In the absence of a subclass, Android uses an instance of the base Application class.

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

回答by Anand

Just you need to define an application name like below which will work:

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

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

回答by user716653

If some variables are stored in sqlite and you must use them in most activities in your app. then Application maybe the best way to achieve it. Query the variables from database when application started and store them in a field. Then you can use these variables in your activities.

如果某些变量存储在 sqlite 中并且您必须在应用程序的大多数活动中使用它们。那么应用程序可能是实现它的最佳方式。应用程序启动时从数据库中查询变量并将它们存储在一个字段中。然后您可以在您的活动中使用这些变量。

So find the right way, and there is no best way.

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

回答by Adorjan Princz

Like there was discussed above OS could kill the APPLICATION without any notification (there is no onDestroy event) so there is no way to save these global variables.

就像上面讨论的那样,操作系统可以在没有任何通知的情况下终止应用程序(没有 onDestroy 事件),因此无法保存这些全局变量。

SharedPreferences could be a solution EXCEPT you have COMPLEX STRUCTURED variables (in my case I had integer array to store the IDs that the user has already handled). The problem with the SharedPreferences is that it is hard to store and retrieve these structures each time the values needed.

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

In my case I had a background SERVICE so I could move this variables to there and because the service has onDestroy event, I could save those values easily.

就我而言,我有一个后台 SERVICE,因此我可以将这个变量移动到那里,并且因为该服务具有 onDestroy 事件,我可以轻松保存这些值。

回答by yanchenko

You can have a static field to store this kind of state. Or put it to the resource Bundle and restore from there on onCreate(Bundle savedInstanceState). Just make sure you entirely understand Android app managed lifecycle (e.g. why login() gets called on keyboard orientation change).

你可以有一个静态字段来存储这种状态。或者将其放入资源包并在 onCreate(Bundle savedInstanceState) 上从那里恢复。只需确保您完全了解 Android 应用程序管理的生命周期(例如,为什么在键盘方向更改时调用 login() )。