不要在Android的Application对象里存储数据

本文探讨了在Android应用程序中使用Application对象存储数据的潜在问题。详细分析了一个示例,展示了当应用被系统回收后重启时,可能导致的NullPointerException异常。并提出了正确的数据存储方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在一个App里面总有一些数据需要在多个地方用到。这些数据可能是一个 session token,一次费时计算的结果等。通常为了避免activity之间传递对象的开销 ,这些数据一般都会保存到持久化存储里面。

有人建议将这些数据保存到 Application 对象里面,这样这些数据对所有应用内的activities可用。这种方法简单,优雅而且……完全扯淡。

假设把你的数据都保存到Application对象里面去了,那么你的应用最后会以一个NullPointerException 异常crash掉。

一个简单的测试案例:

Application对象:

// access modifiers omitted for brevity
class MyApplication extends Application {

    String name;

    String getName() {
        return name;
    }

    void setName(String name) {
        this.name = name;
    }
}

第一个Activity,我们往Application中存储用户名:

// access modifiers omitted for brevity
class WhatIsYourNameActivity extends Activity {

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

        // Just assume that in the real app we would really ask it!
        MyApplication app = (MyApplication) getApplication();
        app.setName("Developer Phil");
        startActivity(new Intent(this, GreetLoudlyActivity.class));

    }

}

第二个Activity,调用第一个Activity设置并存在application中的用户名:

// access modifiers omitted for brevity
class GreetLoudlyActivity extends Activity {

    TextView textview;

    void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.reading);
        textview = (TextView) findViewById(R.id.message);
    }

    void onResume() {
        super.onResume();

        MyApplication app = (MyApplication) getApplication();
        textview.setText("HELLO " + app.getName().toUpperCase());
    }
}

测试场景:

1、用户启动app。
2、在 WhatIsYourNameActivity里面,要求用户输入姓名,并存储到 MyApplication。
3、在 GreetLoudlyActivity里面,你从MyApplication 对象中获得用户姓名,并且显示。
4、用户按home键离开这个app。
5、几个小时后,Android系统为了回收内存kill掉了这个app。到目前为止,一切尚好。接下来就是crash的部分了…
6、用户重新打开这个App。
7、Android系统创建一个新的 MyApplication 实例并恢复 GreetLoudlyActivity。
8、GreetLoudlyActivity 从新的 MyApplication 实例中获取用户姓名,可得到的为空,最后导致NullPointerException。

为什么会Crash?

在上面这个例子中,app会crash得原因是这个 Application 对象是全新的,所以这个name 变量里面的值为 null,当调用String#toUpperCase() 方法时就导致了NullPointerException。
整个问题的核心在于:application 对象不会一直呆着内存里面,它会被kill掉。与大家普遍的看法不同之处在于,实际上app不会重新开始启动。Android系统会创建一个新的 Application 对象,然后启动上次用户离开时的activity以造成这个app从来没有被kill掉得假象。
你以为你的application可以保存数据,却没想到你的用户在没有打开activity A 之前就就直接打开了 activity B ,于是你就收到了一个 crash 的 surprise。

有哪些替代方法呢?

  • 直接将数据通过intent传递给 Activity 。
  • 使用官方推荐的几种方式将数据持久化到磁盘上。
  • 在使用数据的时候总是要对变量的值进行非空检查。

如果模拟App被Kill掉

更新: Daniel Lew指出,kill app更简单的方式就是使用DDMS里面“停止进程” 。你在调试你的应用的时候可以使用这招。

总结

  • 不要在application对象里面储存数据,这容易出错,导致你的app crash。
  • 要么将你后面要用的数据保存到磁盘上面或者保存到intent得extra里面直接传递给activity 。
  • 这些结论不但对application对象有用,对你app里面的单例对象(singleton)或者公共静态变量(public
    static)同样适用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值