androidnullpointerexceptioncrashroboguice

Unexpected lifecycle causing Fatal Exception?


I'm an iOS dev, with absolutely no experience with android, so forgive me if I have difficulty expressing my question:

I'm getting a Fatal Exception in an android app I'm tasked with maintaining.

Fatal Exception: java.lang.RuntimeException Unable to start activity ComponentInfo{…}: java.lang.NullPointerException

I can see from the crash report (attached below) that the crash is happening in one of my fragments in a method called "bindLocation"

Here is the line it's crashing on:

            mLocationHeaderTextView.setVisibility(View.VISIBLE);

So clearly the problem is that mLocationHeaderTextview is null. mLocationHeaderTextView is injected using roboguice as follows:

 @InjectView(R.id.filter_location_header)
TextView mLocationHeaderTextView;

Now, I think the error is caused by my textView not being 'injected'. After looking at the stack trace, I've determine that this is likely a result of onViewCreated() not getting called before bindLocation() is called.

This is where I'm getting lost, however. I'm very unfamiliar with android, so I'm not sure how it's possible that my onViewCreated method is getting skipped over. I've tried a million things on the device to try and reproduce this situation, but I am unable. No matter what user actions I can think to try, my onViewCreated method gets called and my variable is non-Null. Perhaps I'm misreading the stack trace. I'm hoping that my stack trace below provides enough information for y'all to help me, but if not, let me know what else I can tell you about the app. I cannot post my full source code (as it does not belong to me), but I will provide whatever information I can.

Thanks so much for your help!

enter image description here

From my FilterDrawerFragment:

 @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    View view = inflater.inflate(R.layout.fragment_filter_drawer, container, false);

    ButterKnife.inject(this, view);

    return view;
}

@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);

    mRulesHeaderReset.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            resetRules();
        }
    });

    if (getActivity() != null && mDrawerLayout == null) {
        mDrawerLayout = (DrawerLayout) getActivity().findViewById(R.id.filter_drawer_layout);

        if (mDrawerLayout != null) {
            mDrawerLayout.setDrawerListener(new DrawerLayout.DrawerListener() {
                @Override
                public void onDrawerSlide(View view, float v) {

                }

                @Override
                public void onDrawerOpened(View view) {
                    NavigationUtils.invalidateOptionsMenu(getActivity());
                }

                @Override
                public void onDrawerClosed(View view) {
                    notifyFiltersChanged();

                    NavigationUtils.invalidateOptionsMenu(getActivity());
                }

                @Override
                public void onDrawerStateChanged(int i) {

                }
            });

            // set a custom shadow that overlays the main content when the drawer opens
            mDrawerLayout.setDrawerShadow(R.drawable.drawer_shadow, GravityCompat.END);
        }
    }

    mlocationChangeButton.setOnClickListener(this);

    bindUI();
}

 private void bindUI() {
    if (isAdded() && mConfiguration != null) {

        // bind the location
        bindLocation();
        …
    }
}

private void bindLocation() {
    if (isAdded() && mConfiguration != null && mConfiguration.isLocationEnabled()) {

        // show the location related items
        mLocationHeaderTextView.setVisibility(View.VISIBLE);//Crash reported here.
        …
            }
        });

    }
}


/**
 * Users of this fragment must call this to update the filter configuration
 */
public void updateConfiguration(FilterConfiguration configuration) {
    if (configuration != null && !configuration.equals(mConfiguration)) {
        mConfiguration = configuration;

        bindUI();
    }
}

And updateConfiguration() is called from LocationFinderFragment:

 @Override
public void onViewCreated(View view, Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);

    if (getArguments() != null) {
          ………
    if (savedInstanceState == null) {
        mFilterDrawerFragment = FilterDrawerFragment.newInstance();
        getChildFragmentManager()
                .beginTransaction()
                .replace(R.id.filter_drawer, mFilterDrawerFragment)
                .commit();

    } else {
        mFilterDrawerFragment = (FilterDrawerFragment) getChildFragmentManager().findFragmentById(R.id.filter_drawer);
    }

    ………         
);
    …………………
    populateFilters();
}


private void populateFilters() {
    // these might be passed into the fragment
    if (mFacets != null) {
        FilterConfiguration config = new FilterConfiguration() {{
        ……………………
        }};

        mFilterDrawerFragment.updateConfiguration(config);

Solution

  • Now I think I see the problem.

    1. In roboguice views are injected in onViewCreated().
    2. onViewCreated() is called after onCreateView().
    3. bindLocation() is called in onCreateView().

    Hence, by this time views are not yet injected by roboguice.

    Please try calling bindLocation() in onViewCreated() after call to super.