java.lang.NullPointerException 由 public class MyApplication extends Application 引起

java.lang.NullPointerException caused by public class MyApplication extends Application

在检查我的 Android 应用程序崩溃报告时,我注意到 java.lang.NullPointerException 是崩溃的主要原因之一。这是我经常崩溃的一个例子:

    java.lang.RuntimeException:
      at android.app.ActivityThread.performLaunchActivity (ActivityThread.java:3364)
      at android.app.ActivityThread.handleLaunchActivity (ActivityThread.java:3548)
      at android.app.servertransaction.LaunchActivityItem.execute (LaunchActivityItem.java:86)
      at android.app.servertransaction.TransactionExecutor.executeCallbacks (TransactionExecutor.java:108)
      at android.app.servertransaction.TransactionExecutor.execute (TransactionExecutor.java:68)
      at android.app.ActivityThread$H.handleMessage (ActivityThread.java:2155)
      at android.os.Handler.dispatchMessage (Handler.java:109)
      at android.os.Looper.loop (Looper.java:207)
      at android.app.ActivityThread.main (ActivityThread.java:7539)
      at java.lang.reflect.Method.invoke (Native Method)
      at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:524)
      at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:958)
    Caused by: java.lang.NullPointerException:
      at com.myapp.ProductDetails.onCreate (ProductDetails.java:47)
      at android.app.Activity.performCreate (Activity.java:7441)
      at android.app.Activity.performCreate (Activity.java:7431)
      at android.app.Instrumentation.callActivityOnCreate (Instrumentation.java:1286)
      at android.app.ActivityThread.performLaunchActivity (ActivityThread.java:3343)

我去检查了我在 com.myapp.ProductDetails.onCreate (ProductDetails.java:47) 中的内容,结果是这样的:

ProductDetail productDetail = MyApplication.getInstance().pDetail;

我知道该行导致 java.lang.NullPointerException 并因此导致崩溃。问题是为什么?我正在使用 Application,基础 class 用于维护在 https://developer.android.com/reference/android/app/Application 中解释的全局应用程序状态。我正在申请 classing 申请:

import android.app.Application;
.....
public class MyApplication extends Application{
    static MyApplication _instance;
    public ProductDetail pDetail;
    .....
    @Override
    public void onCreate() {
        super.onCreate();
        _instance= this;
        .....
    }
    .....
    public static MyApplication getInstance(){
        return _instance;
    }
}

但是当我使用 ProductDetail productDetail = MyApplication.getInstance().pDetail; 时,有时我会得到 java.lang.NullPointerException。我要强调的是,该行有时会导致 java.lang.NullPointerException,但并非总是如此。我的理论是,我的应用程序发生的事情就是 https://github.com/codepath/android_guides/wiki/Understanding-the-Android-Application-Class:

中所解释的

However, you should never store mutable instance data inside the Application object because if you assume that your data will stay there, your application will inevitably crash at some point with a NullPointerException. The application object is not guaranteed to 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.

我试过用这个记录日志:

ACRA.getErrorReporter().putCustomData("ACRA CUSTOM_DATA begins","ACRA CUSTOM_DATA begins");ACRA.getErrorReporter().putCustomData("MyApplication.getInstance()", MyApplication.getInstance().toString());ACRA.getErrorReporter().putCustomData("MyApplication.getInstance().pDetail", MyApplication.getInstance().pDetail.toString());ProductDetail productDetail = MyApplication.getInstance().pDetail;ACRA.getErrorReporter().putCustomData("ACRA CUSTOM_DATA ends","ACRA CUSTOM_DATA ends");

在日志结果中,我只看到了这个:

CUSTOM_DATA=ACRA CUSTOM_DATA begins = ACRA CUSTOM_DATA begins
MyApplication.getInstance() = com.myapp.MyApplication@c190fea

我的解释是 MyApplication.getInstance().toString() 不会使应用程序崩溃,因为即使原来的 MyApplication 丢失了(这是我的假设,如果进程在某个时候被杀死)因为 MyApplication.getInstance().toString() 创建了一个新对象,因为原始对象丢失了(我的假设)。但是当我尝试执行 MyApplication.getInstance().pDetail.toString() 时,应用程序确实崩溃了,因为它已经丢失了成员变量 pDetail 的值,因为 MyApplication 将持有一个新对象,而不是原始对象已正确分配 MyApplication.getInstance().pDetail 值的一个。所以我的解释是,即使我希望 MyApplication.getInstance() 始终保持一个且只有一个对象,并且一直保持相同,直到我明确关闭应用程序,也许 MyApplication 对象丢失然后使用 MyApplication.getInstance() 创建一个新对象。那么 java.lang.NullPointerException 就有意义了。两个问题。 1. 你觉得这个解释合理吗?如果不是,为什么? 2. 你会建议我做什么来重写我的代码,以便我修复这个我偶尔遇到的 java.lang.NullPointerException?有趣的是,当我长时间最小化应用程序时,我已经看到这种情况发生在我身上,比方说几小时或几天,然后重新打开它。这是有道理的,因为进程可能被终止,当我将最小化的应用程序带回前台时,MyApplication.getInstance() 可能正在创建全新的进程或对象,而不是已创建的原始进程或对象,因此,MyApplication.getInstance().pDetail 已经失去了它的价值,然后我得到了 java.lang.NullPointerException。这个理论有道理吗?

更新 1:

ProductDetail 是我的 class,其中包含导致崩溃的行和 java.lang.NullPointerException:

public class ProductDetail extends FragmentActivity implements OnMapReadyCallback {
    .....
    static ProductDetail _instance;
    protected void onCreate(Bundle savedInstanceState) {
        .....
        ProductDetail productDetail = MyApplication.getInstance().pDetail; // This is the line that SOMETIMES causes the app to crash with a java.lang.NullPointerException.
        .....
    }
}

嗯,是的,您关于可以终止应用程序实例的假设是正确的。您 有时 发生的错误就是证明。正如这篇文章所说:

The OS may kill processes as necessary. All processes are divided into 5 levels of "killability" specified in the documentation.

So, for instance, if your app goes in the background due to the user answering to an incoming call, then depending on the state of the RAM, the OS may (or may not) kill your process (destroying the Application instance in the process).

您的应用程序实例作为存储 class 用例不可靠。例如,让我们举个例子:

  • 您的应用程序进入后台,因为您的用户已转到主应用程序抽屉,或者您的应用程序消耗了不必要的内存或电池。
  • 文档说因为您的应用程序在后台运行您的服务,或者有时应用程序状态为“已取消”。

这就是为什么您没有看到那么多在应用程序实例中存储数据的方法。大多数时候,您会使用 SharedPreferences or you would write it to a local text, custom file. This article has way more information in it: Using the Android Application class to persist data

关于 http://www.developerphil.com/dont-store-data-in-the-application-object/ 根本原因的精彩解释。这是根据那篇文章的解决方案:

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

  1. Explicitly pass the data to the Activity through the intent.
  2. Use one of the many ways to persist the data to disk.
  3. Always do a null-check and handle it manually.