While examining my Android app crash reports, I noticed that the java.lang.NullPointerException
was one of top causes of crashes. This is an example of one of my frequent crashes:
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)
I went to examine what I had in com.myapp.ProductDetails.onCreate (ProductDetails.java:47)
and it was this:
ProductDetail productDetail = MyApplication.getInstance().pDetail;
I know that line is causing the java.lang.NullPointerException
and consequently the crash. The question is why? I am using Application
, the base class for maintaining global application state explained at https://developer.android.com/reference/android/app/Application. I am subclassing Application:
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;
}
}
But when I use ProductDetail productDetail = MyApplication.getInstance().pDetail;
, SOMETIMES I get the java.lang.NullPointerException
. I am emphasizing that the line causes the java.lang.NullPointerException
SOMETIMES, not all the time. My theory is that what is happening to my app is what is explained at 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 aNullPointerException
. 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 newApplication
object and start the activity where the user was before to give the illusion that the application was never killed in the first place.
I tried logging using this:
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");
In the log results, I only see this:
CUSTOM_DATA=ACRA CUSTOM_DATA begins = ACRA CUSTOM_DATA begins
MyApplication.getInstance() = com.myapp.MyApplication@c190fea
The way I interpret it is that MyApplication.getInstance().toString()
does not make the app to crash because even though the original MyApplication
was lost (that is my assumption that somehow that object gets lost maybe if the process is killed at some point) because MyApplication.getInstance().toString()
creates a new object, since the original was lost (my assumption). But when I try to execute MyApplication.getInstance().pDetail.toString()
, then the app does crash because it had already lost the value of the member variable pDetail
, since MyApplication
would be holding a new object, not the original one that had a value for MyApplication.getInstance().pDetail
properly assigned. So my interpretation is that even though I wanted MyApplication.getInstance()
to always hold one and only one object and the same all the time until I explicitly close the app, maybe the MyApplication
object gets lost and then using MyApplication.getInstance()
creates a new object. Then the java.lang.NullPointerException
would make sense. Two questions. 1. Does this explanation sound plausible to you? If not, why? 2. What would you recommend to do to rewrite my code so that I fix this java.lang.NullPointerException
that I occasionally get? Interesting to note that I have seen this happen to me when I minimize the app for a long time, let's say hours or days and then reopen it. This would make sense because maybe the process is killed and when I bring the minimized app back to the foreground, MyApplication.getInstance()
could be creating whole new process or object, not the original one that had been created and as a consequence, MyApplication.getInstance().pDetail
would have already lost its value and then I get the java.lang.NullPointerException
. Does this theory make sense?
UPDATE 1:
ProductDetail
is a class that I have, which contains the line that causes the crash and the 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.
.....
}
}