往Android的Application对象里存储数据的陷阱

转载 2016年06月01日 10:36:10

http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0204/2409.html

编辑推荐:稀土掘金,这是一个针对技术开发者的一个应用,你可以在掘金上获取最新最优质的技术干货,不仅仅是Android知识、前端、后端以至于产品和设计都有涉猎,想成为全栈工程师的朋友不要错过!

本文为转载,原译文的标题是 为什么不能往Android的Application对象里存储数据  ,之所以去掉了“不能往”是因为在读完之后,发现作者因为这种做法可能会导致的问题而全面否定它,事实上,我认为还是可以往Application对象里存储数据的,只是需要考虑周全。另外我也赞成Application中不适合放太多或者太大的数据,在Application中存放数据是比较无奈的做法。

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

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

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

一个简单的测试案例

代码

Application 对象:

1
2
3
4
5
6
7
8
9
10
11
12
13
// access modifiers omitted for brevity
class MyApplication extends Application {
  
    String name;
  
    String getName() {
        return name;
    }
  
    void setName(String name) {
        this.name = name;
    }
}

第一个activity, 我们往application对象里面存储了用户姓名:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 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里面的用户姓名:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 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里面“停止进程” 。你在调试你的应用的时候可以使用这招。

为了测试这个,你必须使用一个Android模拟器或者一台root过的Android手机。

  1. 使用home按钮退出app。

  2. 在终端里:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# find the process id
adb shell ps
# then find the line with the package name of your app
  
# Mac/Unix: save some time by using grep:
adb shell ps | grep your.app.package
  
# The result should look like:
# USER      PID   PPID  VSIZE  RSS     WCHAN    PC         NAME
# u0_a198   21997 160   827940 22064 ffffffff 00000000 S your.app.package
  
# Kill the app by PID
adb shell kill -9 21997
  
# the app is now killed
  1. 长按home按钮回到之前的app。
    你现在是出于一个新的application实例中了。

总结

不要在application对象里面储存数据,这容易出错,导致你的app crash。
要么将你后面要用的数据保存到磁盘上面或者保存到intent得extra里面直接传递给activity 。

这些结论不但对application对象有用,对你app里面的单例对象(singleton)或者公共静态变量(public static)同样适用。

本文翻译自:http://www.developerphil.com/dont-store-data-in-the-application-object/



为什么不能往Android的Application对象里存储数据

转载自:http://greenrobot.me/devpost/dont-store-data-in-the-application-object/ 在一个App里面总有一些数据需要在多个地...

为什么不能往Android的Application对象里存储数据

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

Android中的Application对象里尽量不要存储数据

问题: 在做一个项目的时候需要将登陆后后台返回的数据进行保存,然后再其他地方调用。 解决方案: 公司中的一个员工是这样处理的 在MyApplication中定义了一个User ...

为什么不能往Android的Application对象里存储数据

译文原文:http://greenrobot.me/devpost/dont-store-data-in-the-application-object/ 原文:http://www.develope...
  • howlaa
  • howlaa
  • 2015年05月07日 16:10
  • 707

Android开发之千万不要把数据存储在Application对象中

在我们的应用程序中有些数据需要在多处使用。有可能是一个会话令牌,花费很大代价才得来的结果,等等。而且我们总是想避免在两个Activity之间传递数据或者不想把数据存储在持久存储器中。 一个解...

android 告诉你Application中存储对象,为什么有的时候为空的情况

android 告诉你Application中存储对象,为什么有的时候为空的情况

在Android中不要将数据存储在Application类中,

最近在开发中发现了一个比较严重的问题,当我们将应用按home键放入后台运行,一段时间后,当我们再次打开应用的时候,十有八九会出现一个NullPointException的空指针异常,根据logcat的...

Android应用之——不要将数据存储在Application类中

前言:最近在开发中发现了一个比较严重的问题,当我们将应用按home键放入后台运行,一段时间后,当我们再次打开应用的时候,十有八九会出现一个NullPointException的空指针异常,根据logc...

不要在Android的Application对象中缓存数据!

转自:http://zmywly8866.github.io/2014/12/26/android-do-not-store-data-in-the-application-object.html ...

Android笔记 Application对象的使用-数据传递以及内存泄漏问题

Application的使用 What is Application Application和Actovotu,Service一样是android框架的一个系统组件,当android程序启...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:往Android的Application对象里存储数据的陷阱
举报原因:
原因补充:

(最多只允许输入30个字)