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

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

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


当前回答

我在09年写了这个答案,当时Android还相对较新,在Android开发中还有许多不成熟的领域。我在这篇文章的底部添加了一个很长的附录,解决了一些批评,并详细说明了我在使用单例而不是应用程序子类化方面的哲学分歧。请自行承担阅读风险。

最初的回答:

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

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

方法是创建你自己的android.app的子类。Application,然后在清单中的Application标记中指定该类。现在,Android将自动创建该类的实例,并使其可用于整个应用程序。您可以使用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;
  }
}

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:

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

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

注1:同样正如anticafe所评论的,为了正确地将你的应用程序覆盖绑定到你的应用程序,在manifest文件中有一个标签是必要的。同样,更多信息请参见Android文档。一个例子:

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

注2:user608578在下面询问如何管理本机对象的生命周期。我不知道如何在Android上使用本机代码,我也没有资格回答如何与我的解决方案交互。如果有人确实有这个问题的答案,我愿意相信他们,并把这些信息放在这篇文章中以获得最大的能见度。

附录:

As some people have noted, this is not a solution for persistent state, 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) (NOT singleton!) in nature.

Dayerman has been kind enough to point out an interesting conversation with Reto Meier and Dianne Hackborn in 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:

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 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.

她接着说,“这就更自然地引出了你应该如何管理这些东西——根据需要初始化它们。”这忽略了一个事实,即没有理由不能使用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 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!

以下是我从之前的StackExchange链接中偷来的关于singleton的缺点:

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

加上我自己的:

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

其他回答

您可以使用一个静态字段来存储这种状态。或者把它放到资源Bundle中,然后在onCreate(Bundle savedInstanceState)上从那里恢复。只要确保你完全理解Android应用程序管理的生命周期(例如,为什么login()会在键盘方向改变时被调用)。

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

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

在我的情况下,我有一个后台服务,所以我可以把这些变量移动到那里,因为服务有onDestroy事件,我可以很容易地保存这些值。

你可以使用两种方法来做到这一点:

使用应用程序类 使用共享首选项 使用应用程序类

例子:

class SessionManager extends Application{

  String sessionKey;

  setSessionKey(String key){
    this.sessionKey=key;
  }

  String getSessisonKey(){
    return this.sessionKey;
  }
}

你可以使用上面的类在MainActivity中实现登录。代码看起来像这样:

@override 
public void onCreate (Bundle savedInstanceState){
  // you will this key when first time login is successful.
  SessionManager session= (SessionManager)getApplicationContext();
  String key=getSessisonKey.getKey();
  //Use this key to identify whether session is alive or not.
}

This method will work for temporary storage. You really do not any idea when operating system is gonna kill the application, because of low memory. When your application is in background and user is navigating through other application which demands more memory to run, then your application will be killed since operating system given more priority to foreground processes than background. Hence your application object will be null before user logs out. Hence for this I recommend to use second method Specified above.

使用共享首选项。 字符串MYPREF = " com.your.application.session " SharedPreferences pref= context.getSharedPreferences(MyPREF,MODE_PRIVATE); //插入键如下: 编辑器编辑= pre .edit(); editor.putString(“关键”、“价值”); editor.commit (); //获取密钥如下所示。 sharedPref = getActivity().getPreferences(Context.MODE_PRIVATE); String key= getResources().getString("key");

BARACUS框架也使用了子类化的方法。从我的观点来看,子类化Application是为了与Android的生命周期一起工作;这是任何应用程序容器所做的。我没有使用全局变量,而是将bean注册到这个上下文中,让它们被注入到上下文可管理的任何类中。每个注入的bean实例实际上都是单例的。

有关详细信息,请参阅此示例

如果你能有这么多,为什么还要做体力活呢?

您可以创建一个扩展Application类的类,然后将您的变量声明为该类的字段,并为它提供getter方法。

public class MyApplication extends Application {
    private String str = "My String";

    synchronized public String getMyString {
        return str;
    }
}

然后在你的Activity中访问这个变量,使用这个:

MyApplication application = (MyApplication) getApplication();
String myVar = application.getMyString();