zoukankan      html  css  js  c++  java
  • 【安卓开发】为什么不能往Android的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
    3. 长按home按钮回到之前的app。
      你现在是出于一个新的application实例中了。

    总结

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

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

  • 相关阅读:
    点聚-weboffice 6.0 (二)
    点聚-weboffice 6.0 (一)
    Hibernate连接池设置
    ajax工作原理(转)
    LigerUI java SSH小例子
    file标签样式修改
    好久不来了,回来园子看看
    resharper 8.2
    无聊的要死
    无聊
  • 原文地址:https://www.cnblogs.com/xiaomaohai/p/6157994.html
Copyright © 2011-2022 走看看