為什么不能往Android的Application對象里存儲數據

jopen 9年前發布 | 39K 次閱讀 Android Android開發 移動開發

原文出處: Developer Phil   譯文出處:Android Cool Posts   

在一個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 MyApplication extends Application {

    String name;

    String getName() {
        return name;
    }

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

第二個activity,我們調用第一個activity設置并存在application里面的用戶姓名:

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

    String name;

    String getName() {
        return name;
    }

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

測試場景

  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. 在終端里:
    // access modifiers omitted for brevity
    class MyApplication extends Application {
    
        String name;
    
        String getName() {
            return name;
        }
    
        void setName(String name) {
            this.name = name;
        }
    }
  3. 長按home按鈕回到之前的app。
    你現在是出于一個新的application實例中了。

總結

不要在application對象里面儲存數據,這容易出錯,導致你的app crash。
要么將你后面要用的數據保存到磁盤上面或者保存到intent得extra里面直接傳遞給activity 。

這些結論不但對application對象有用,對你app里面的單例對象(singleton)或者公共靜態變量(public static)同樣適用。

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!