zoukankan      html  css  js  c++  java
  • 为什么不能往Android的Application对象里存储数据

    在一个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 。
    • 使用官方推荐的几种方式将数据持久化到磁盘上。
    • 在使用数据的时候总是要对变量的值进行非空检查。
    • 进行本地数据保护,对apk应用的网络缓存、本地存储数据进行深度保护。

    如果模拟App被Kill掉

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

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

    1. 使用home按钮退出app。
    2. 在终端里:
      # 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)同样适用。

  • 相关阅读:
    LeetCode 83. Remove Duplicates from Sorted List (从有序链表中去除重复项)
    LeetCode 21. Merge Two Sorted Lists (合并两个有序链表)
    LeetCode 720. Longest Word in Dictionary (字典里最长的单词)
    LeetCode 690. Employee Importance (职员的重要值)
    LeetCode 645. Set Mismatch (集合不匹配)
    LeetCode 500. Keyboard Row (键盘行)
    LeetCode 463. Island Perimeter (岛的周长)
    115.Distinct Subsequences
    55.Jump Game
    124.Binary Tree Maximum Path Sum
  • 原文地址:https://www.cnblogs.com/Niger123/p/4401725.html
Copyright © 2011-2022 走看看