Don't Store Data in the Application Object

來源:互聯網
上載者:User

標籤:

There is always some information that is needed in many places in your app. It can be a session token, the result of an expensive computation, etc. It is often tempting to avoid the overhead of passing objects between activities or keeping those in persistent storage.

A pattern that is sometimes suggested is to dump your data in the Application object with the idea that it will be available across all activities. This solution is simple, elegant and… totally wrong.

If you assume that your data will stay there, your application will eventually crash with a NullPointerException.

A Simple Test CaseThe Code

The Application object:

 1 2 3 4 5 6 7 8 910111213
// access modifiers omitted for brevityclass MyApplication extends Application {    String name;    String getName() {        return name;    }    void setName(String name) {        this.name = name;    }}

The first activity, where we store the name of the user in the application object:

 1 2 3 4 5 6 7 8 9101112131415
// access modifiers omitted for brevityclass 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));    }}

The second activity, where we shout the name of the user:

 1 2 3 4 5 6 7 8 910111213141516171819
// access modifiers omitted for brevityclass 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());    }}
The Scenario
  1. The user starts the app.
  2. In WhatIsYourNameActivity, you ask for the name of the user and you store it in MyApplication.
  3. In GreetLoudlyActivity, you fetch the user’s name from the MyApplication object and display it.
  4. The user leaves the app using the home button.
  5. A few hours later, Android silently kills the app to reclaim some memory.

    So far, so good!

    But here comes the crashy part…

  6. The user reopens the app.
  7. Android creates a new MyApplication instance and restores GreetLoudlyActivity.
  8. GreetLoudlyActivity fetches the user’s name, which is now null, and crashes with a NullPointerException.
Why Does it Crash?

In this sample, we crash because the Application object is brand new, so the name variable is null, leading to a NullPointerException when we call String#toUpperCase() on it.

Which brings us to the core of the problem: The application object will not stay in memory forever, it will get killed. Contrary to popular belief, the app won’t be restarted from scratch. Android will create a new Application object and start the activity where the user was before to give the illusion that the application was never killed in the first place.

Which means that if you expect some data to be in your application object just because your user is not supposed to be able to open activity B before activity A, you are in for a crashy surprise.

What Are The Alternatives

There is no magic solution here, you can do one of the following:

  • Explicitly pass the data to the Activity through the intent.
  • Use one of the many ways to persist the data to disk.
  • Always do a null-check and handle it manually.
How To Simulate The Application Being Killed

EDIT: As pointed out by Daniel Lew, an easier way to kill your app is to simply use the “Stop Process” feature in DDMS. This will work on any phone as long as your application is debuggable.**

To test this, you must use an emulator or a rooted phone.

  1. Exit your app using the home button.
  2. In a terminal:
 1 2 3 4 5 6 7 8 9101112131415
# find the process idadb 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 PIDadb shell kill -9 21997# the app is now killed
  1. Now return to the app using the task switcher.
    You are now on a new application instance.
The Bottom Line

Storing data in the application object is error prone and can crash your app. Prefer storing your global data on disk if it is really needed later or explicitly pass it to your activity in the intent’s extras.

Also remember that while this is true for the application object, it is also true for any singleton or public static field that you might have in your app.

 

我的最終解決方案是:在setName()的時候存到本地,在getName()的時候從本地取。

Don't Store Data in the Application Object

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.