在一个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
  3. 长按home按钮回到之前的app。
    你现在是出于一个新的application实例中了。

总结

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

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

【安卓开发】为什么不能往Android的Application对象里存储数据的更多相关文章

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

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

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

    前言   在你的App中的很多地方都需要使用到数据信息,它可能是一个session token,一次费时计算的结果等等,通常为了避免Activity之间传递数据的开销,会将这些数据通过持久化来存储. ...

  3. 安卓开发_浅谈Android动画(四)

    Property动画 概念:属性动画,即通过改变对象属性的动画. 特点:属性动画真正改变了一个UI控件,包括其事件触发焦点的位置 一.重要的动画类及属性值: 1.  ValueAnimator 基本属 ...

  4. Android 利用Application对象存取公共数据

    本文章来给大家介绍Android 利用Application对象存取公共数据. Android系统在运行每一个程序应用的时候,都会创建一个Application对象,用于存储与整个应用相关的公共变量. ...

  5. 安卓开发第一步:Android Studio安装配置

    虽然本人是JAVA开发工程师平时主要开发Web App,但因为项目需求需要开发对应的移动端.一时又找不到合适的安卓开发人员,兄弟我只好被项目经理"抓来当壮丁了".俗话说好" ...

  6. Application对象的使用-数据传递以及内存泄漏

    Application的使用 What is Application Application和Activity,Service一样是android框架的一个系统组件,当android程序启动时系统会创 ...

  7. 【Bugly安卓开发干货分享】Android APP 快速 Pad 化实现

    项目背景 采用最新版本手机 APP(之后称为 MyApp)代码,实现其 Pad 化,为平板和大屏手机用户提供更好的体验.为实现 MyApp 的 Pad 化工作,需要我们首先来了解一下 MyApp 项目 ...

  8. 重拾安卓_01_安卓开发环境搭建(android studio)

    一.下载安装SDK 参考:搭建Android开发环境——Eclipse  的安装SDK部分 二.安装android studio 参考: Android Studio 入门级教程(一) 三.andro ...

  9. 安卓开发学习日记 DAY2——android项目文件

    当一个android项目建立时,会有一个目录,以下为目录所包含内容 src:放置java源代码 gen:基本不会做任何更改,放置自动生成的配置文件(主要是R文件) Android4.4.2:放置当前版 ...

随机推荐

  1. Ubantu16.04系统优化

    系统清理篇 系统更新 安装完系统之后,需要更新一些补丁.Ctrl+Alt+T调出终端,执行一下代码: sudo apt-get update sudo apt-get upgrade 卸载libreO ...

  2. [测试题]gene

    Description Input Output Sample Input 3A+00A+A+ 00B+D+A- B-C+00C+ Sample Output bounded Hint 题解 //It ...

  3. [USACO07NOV]牛继电器Cow Relays

    题目描述 给出一张无向连通图,求S到E经过k条边的最短路. 输入输出样例 输入样例#1: 2 6 6 4 11 4 6 4 4 8 8 4 9 6 6 8 2 6 9 3 8 9 输出样例#1: 10 ...

  4. ●BZOJ 4310 跳蚤

    ●赘述题目 给出一个字符串,要求分成k个子串,然后求出每个子串的字典序最大的子串(我称它为子子串),要使这k个子子串中的字典序最大的那个串(即魔力串)最小.输出该魔力串. (本题个人感觉很好,比较综合 ...

  5. ●BZOJ 4665 小w的喜糖

    题链: http://www.lydsy.com/JudgeOnline/problem.php?id=4665 题解: 容斥,dp令 v[i] 表示原来拥有i类糖果的人数. (一个套路,首先把每个糖 ...

  6. 51Nod 1781 跑的比谁都快

    香港记者跑的比谁都快是众所周知的常识. 现在,香港记者站在一颗有 n 个点的树的根结点上(即1号点),编号为 i 的点拥有权值 a[i] ,数据保证每个点的编号都小于它任意孩子结点的别号. 我们假定这 ...

  7. hdu 5573Binary Tree

    Binary Tree Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Tota ...

  8. Codeforces Round #398 (div.2)简要题解

    这场cf时间特别好,周六下午,于是就打了打(谁叫我永远1800上不去div1) 比以前div2的题目更均衡了,没有太简单和太难的...好像B题难度高了很多,然后卡了很多人. 然后我最后做了四题,E题感 ...

  9. bzoj2560串珠子 状压dp+容斥(?)

    2560: 串珠子 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 515  Solved: 348[Submit][Status][Discuss] ...

  10. 假定某系统提供硬件的访管指令(例如形式:“svc n”),为了实现系统调用,系统设计者应做哪些工作?用户又如如何请求操作系统服务?

    工作: 1.  编写并调试好能实现各种功能的例行子程序. 2.  编写并调试好访管中断处理程序. 3.  构造例行子程序入口地址表. 在用户程序中,需要请求操作系统服务的地方安排一条系统调用.这样,当 ...