0

I've faced with such problem. In my app I have an Activity (MobileActivity) which contains some views (Spinners, TextViews and Button). When user clicks on the button, I need to open a Dialog (ConfirmDialog). In this Dialog, I need to show some data from MobileActivity's views (see code below). And sometimes in Play Console, I see crash reports with NullPointerException (I marked that row in my code). Where is the problem?

Thank you!

I supposed, that the problem can be in Activity's lifecycle methods. I tried next actions:

  • Started my app on emulator and opened the DialogFragment
  • Pressed Home button
  • In adb, killed my process (adb kill )
  • Reopened my app from background apps

It didn't crash. So, the problem isn't in back|foreground?

Code from MobileActivity, which will open my DialogFragment

ConfirmDialog newFragment = new ConfirmDialog();
newFragment.show(getFragmentManager(), "Confirmation"); 

Code From ConfirmDialog

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
        // Get the layout inflater
        LayoutInflater inflater = getActivity().getLayoutInflater();
            View v = null;
        Spinner s;
            v = inflater.inflate(R.layout.confirm_mobile_layout, null);
        builder.setView(v);
        //next row marked in stacktrace as problem row
        //Spinner with Id=acn_debit_mobile is placed on MobileActivity
        s = getActivity().findViewById(R.id.acn_debit_mobile);         
        Spinner b = getActivity().findViewById(R.id.biller_mobile);
            //other code
            return builder.create();
 }

And the stacktrace from Play Console

java.lang.RuntimeException: 
 at android.app.ActivityThread.performLaunchActivity (ActivityThread.java:2814)
 at android.app.ActivityThread.handleLaunchActivity (ActivityThread.java:2892)
 at android.app.ActivityThread.handleRelaunchActivity (ActivityThread.java:4763)
 at android.app.ActivityThread.-wrap18 (Unknown Source)
 at android.app.ActivityThread$H.handleMessage (ActivityThread.java:1621)
 at android.os.Handler.dispatchMessage (Handler.java:106)
 at android.os.Looper.loop (Looper.java:171)
 at android.app.ActivityThread.main (ActivityThread.java:6635)
 at java.lang.reflect.Method.invoke (Native Method)
 at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:547)
 at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:823)

Caused by: java.lang.NullPointerException: 
 at ua.grant.smart.ConfirmDialog.onCreateDialog (ConfirmDialog.java:121)
 at android.app.DialogFragment.onGetLayoutInflater (DialogFragment.java:406)
 at android.app.Fragment.performGetLayoutInflater (Fragment.java:1325)
 at android.app.FragmentManagerImpl.moveToState (FragmentManager.java:1279)
 at android.app.FragmentManagerImpl.moveFragmentToExpectedState (FragmentManager.java:1562)
 at android.app.FragmentManagerImpl.moveToState (FragmentManager.java:1623)
 at android.app.FragmentManagerImpl.dispatchMoveToState (FragmentManager.java:3032)
 at android.app.FragmentManagerImpl.dispatchActivityCreated (FragmentManager.java:2984)
 at android.app.FragmentController.dispatchActivityCreated (FragmentController.java:178)
 at android.app.Activity.performCreate (Activity.java:7090)
 at android.app.Activity.performCreate (Activity.java:7075)
 at android.app.Instrumentation.callActivityOnCreate (Instrumentation.java:1215)
 at android.app.ActivityThread.performLaunchActivity (ActivityThread.java:2767)

I added onAttach and onDetach methods to store a reference to my host Activity (code below), and replaced getActivity() to mActivity in all places in my ConfirmDialog class. Will it solve this problem?

mActivity Activity;

@Override
public void onAttach(Activity activity) {
    super.onAttach(activity);
    mActivity = activity;
}

@Override
public void onDetach() {
    super.onDetach();
    mActivity = null;
}
user3533397
  • 191
  • 1
  • 2
  • 14

1 Answers1

1

Keeping activity instance and destroy it whenever the fragment is detached is a good approach to avoid NullPointerException

mActivity Activity;

@Override
public void onAttach(Activity activity) {
    super.onAttach(activity);
    mActivity = activity;
}

@Override
public void onDetach() {
    super.onDetach();
    mActivity = null;
}

But there are 2 things you might need to review.

  1. Why don't you use the same approach to your fragment, where is mActivity in your onCreateDialog. Btw, please check null for mActivity before doing any transactions.

enter image description here

  1. Using yourDialogFragment.show() explicitly run this function:

enter image description here

ft.commit() schedules a commit of this transaction. The commit does not happen immediately. It will be scheduled as work on the main thread to be done the next time that thread is ready. So we can't ensure anything, it may cause IllegalStateException for instance.

How about: commitAllowingStateLoss(). But read the document carefully before using it. Take a look at this.

  1. Why don't you inflate view at onCreateView? (You don't have to do it in onCreateDialog)

enter image description here

phatnhse
  • 3,870
  • 2
  • 20
  • 29
  • Thanks for your answer! Sure, I'll use mActivity instead of getActivity(), but can I be sure that it will help me to avoid NullPointerException in my situation? About IllegalStateException: I faced with this problem in other place. Also looks strange: error appears only in one phone (during last month). And I also couldn't reproduce this problem. – user3533397 Dec 26 '18 at 16:14
  • Why should I inflate view in onCreateView, not in onCreateDialog? I took this code from developer.android.com, so I was sure that it is correct) – user3533397 Dec 26 '18 at 16:16
  • For the log that you attached, check null for each getActivity() might help you to avoid NullPointerException – phatnhse Dec 26 '18 at 16:17
  • Yeah, both work, but onCreateView give you inflater by default. You dont have to `getLayoutInflater` from `Activity`. https://stackoverflow.com/questions/13257038/custom-layout-for-dialogfragment-oncreateview-vs-oncreatedialog – phatnhse Dec 26 '18 at 16:17
  • you can override both indeed, but the problem comes when you try to inflate the view after having already creating the dialog view. – phatnhse Dec 26 '18 at 16:19
  • Sorry, but I didn't understand about getActivity(). Why can it reutrn null? It's like magic for me, I couldn't reproduce that error. I'll use mActivity, but I just want to understand, where was the problem.. – user3533397 Dec 26 '18 at 16:21
  • `yourFragment.show()` is asynchronous -- the DialogFragment isn't actually immediately displayed -- it's sent to the end of the message queue. Let say if your activity is destroy or not available when `onCreateDialog` is called. Lets simulate it by put the function show() on a `Handler` callback where `Thread.sleep(5000)` was called and `finish` your activity before that timeout. – phatnhse Dec 26 '18 at 16:26
  • Does it fit your question? – phatnhse Dec 26 '18 at 16:32
  • I'll try your advice with Handler. Now I don't understand, how my activity cann be killed between button's onClick and Dialog's onCreateDialog.. – user3533397 Dec 26 '18 at 16:40
  • For example, at 00:00:01, we click the button, then we need 20ms to init Spinner. At 00:00:15ms, we press home button, then activity is not available anymore, then getActivity() becomes null... The case like this is rarely happening... But it could... – phatnhse Dec 26 '18 at 16:48
  • But I cheched this situation (pressing Home, and even killing process). It doesn't produce NullPointerException.. – user3533397 Dec 26 '18 at 16:55
  • It's not easy to reproduce that case man ^^ Using handler is a good choice – phatnhse Dec 26 '18 at 17:26
  • Ok, thank you for your help, I'll try to understand Handlers)) Now I tried your advice, and when the Thread was sleeping, I've killed process. When app was opened from background, it didn't crash.. – user3533397 Dec 26 '18 at 17:35