0

I've been working on this for hours now. Can't find any reason. I can't post the entire fragment here but the following should make it clear.

@BindView(R.id.acService) AutoCompleteTextView autocompleteService;
@BindView(R.id.acAddress) AutoCompleteTextView autocompleteAddress;

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState)
{
    View view = inflater.inflate(R.layout.fragment_home, container, false);
    unbinder = ButterKnife.bind(this, view);

    initialize();
    loadSkillsData();

    return view;
}

private void initialize()
{
    context = getActivity();
    util = new Util(context);
    requestService = new RequestService();
    geoDataClient = Places.getGeoDataClient(context, null);

    autocompleteAdapter = new PlaceAutocompleteAdapter(context, geoDataClient, BOUNDS_ONTARIO, null);
    autocompleteAddress.setAdapter(autocompleteAdapter);

    mapFragment = (SupportMapFragment) getChildFragmentManager().findFragmentById(R.id.mapFragment);
    mapFragment.getMapAsync(this);
}

private void loadSkillsData()
{
    Realm realm = getRealm();
    UserModel user = realm.where(UserModel.class).findFirst();

    Retrofit retrofit = new Retrofit.Builder()
            .baseUrl(RestAPI.ENDPOINT)
            .addConverterFactory(MoshiConverterFactory.create())
            .build();
    RestAPI restApi = retrofit.create(RestAPI.class);
    Call<ResponseSkills> loginCall = restApi.getSkills(user.getServerUserId());
    loginCall.enqueue(new Callback<ResponseSkills>()
    {
        @Override
        public void onResponse(Call<ResponseSkills> call, final Response<ResponseSkills> response)
        {
            if (response.isSuccessful())
            {
                if (response.body().getStatus())
                {
                    skillList = response.body().getSkillList();
                    ArrayAdapter<SkillModel> skillAdapter = new ArrayAdapter<>(context, android.R.layout.simple_list_item_1, skillList);
                    autocompleteService.setAdapter(skillAdapter);
                }
                else
                {
                    switch (response.body().getError())
                    {
                        default:
                            Toasty.error(context, response.body().getError());
                            break;
                    }
                }
            } else
            {
                Toasty.error(context, getString(R.string.toast_experienced_a_problem)).show();
            }
        }

        @Override
        public void onFailure(Call<ResponseSkills> call, Throwable t)
        {
            Toasty.error(context, getString(R.string.toast_experienced_a_problem)).show();
            t.printStackTrace();
        }
    });
}

Layout: XML

On the line autocompleteService.setAdapter(skillAdapter);, I get a NPE saying that I'm calling setAdapter on a null object. Debugging tells me autocompleteService is indeed null at this point.

Why is this view null? Butterknife.bind is called way before this is. Why isn't the view initialized?

Here's the exact error:

java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.AutoCompleteTextView.setAdapter(android.widget.ListAdapter)' on a null object reference
                                                            at com.xyz.controllers.HomeFragment$3.onResponse(HomeFragment.java:183)
                                                            at retrofit2.ExecutorCallAdapterFactory$ExecutorCallbackCall$1$1.run(ExecutorCallAdapterFactory.java:70)
                                                            at android.os.Handler.handleCallback(Handler.java:739)
                                                            at android.os.Handler.dispatchMessage(Handler.java:95)
                                                            at android.os.Looper.loop(Looper.java:148)
                                                            at android.app.ActivityThread.main(ActivityThread.java:5417)
                                                            at java.lang.reflect.Method.invoke(Native Method)
                                                            at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
                                                            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
Asim
  • 6,962
  • 8
  • 38
  • 61
  • First make sure `R.id.acService` is part of `R.layout.fragment_home`. If its ok then Try https://stackoverflow.com/questions/34228734/android-butterknife-binding-in-fragment. – ADM Apr 06 '18 at 11:17
  • It is. Adding image in a second. – Asim Apr 06 '18 at 11:19
  • Try clean build . And see if solution above work for you . Because code looks ok to me . – ADM Apr 06 '18 at 11:22
  • Already tried that. This code was working perfectly fine 24 hours ago. I made some changes to other parts of the app (to models mostly) and this started happening. – Asim Apr 06 '18 at 11:26
  • @ADM I tried something. If I ignore butterknife and try to initialize the variable the usual way (getView.findViewById), the getView returns null. – Asim Apr 06 '18 at 11:37
  • If you use `getView()` inside `onCreateView` then it will return `null` i think you should use it inside `onViewCreated()` – ADM Apr 06 '18 at 11:41
  • have you added right xml file – Rahul Chaudhary Apr 06 '18 at 11:47
  • @RahulChaudhary Yes. – Asim Apr 06 '18 at 11:53
  • @ADM Already tried that. Same result. – Asim Apr 06 '18 at 11:53
  • did you try removing butterknife bind and assign does views by using findViewById() method? so the view is null again? – anzaidemirzoi Apr 06 '18 at 12:49

2 Answers2

0

Override onViewCreated and move your

    unbinder = ButterKnife.bind(this, view);
    initialize();
    loadSkillsData();

into there

reactivemobile
  • 505
  • 3
  • 9
  • @Override public void onViewCreated(View view, Bundle savedInstanceState) { unbinder = ButterKnife.bind(this, view); initialize(); loadSkillsData(); } – Asim Apr 06 '18 at 11:25
  • Same result with the above. – Asim Apr 06 '18 at 11:25
0

Use ViewTreeObserver.onGlobalLayoutListener to make sure the view is fully laid out before attempting to mutate it. Here is your loadSkillsData function using the global layout listener which should resolve your NPE:

private void loadSkillsData()
{
    Realm realm = getRealm();
    UserModel user = realm.where(UserModel.class).findFirst();

    Retrofit retrofit = new Retrofit.Builder()
            .baseUrl(RestAPI.ENDPOINT)
            .addConverterFactory(MoshiConverterFactory.create())
            .build();
    RestAPI restApi = retrofit.create(RestAPI.class);
    Call<ResponseSkills> loginCall = restApi.getSkills(user.getServerUserId());
    loginCall.enqueue(new Callback<ResponseSkills>()
    {
        @Override
        public void onResponse(Call<ResponseSkills> call, final Response<ResponseSkills> response)
        {
            if (response.isSuccessful())
            {
                if (response.body().getStatus())
                {
                    skillList = response.body().getSkillList();
                    ArrayAdapter<SkillModel> skillAdapter = new ArrayAdapter<>(context, android.R.layout.simple_list_item_1, skillList);
                    autocompleteService.viewTreeObserver.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
                        @Override
                        public void onGlobalLayout() {
                            autocompleteService.setAdapter(skillAdapter);
                        }
                    }
                }
                else
                {
                    switch (response.body().getError())
                    {
                        default:
                        Toasty.error(context, response.body().getError());
                        break;
                    }
                }
            } else
            {
                Toasty.error(context, getString(R.string.toast_experienced_a_problem)).show();
            }
        }

        @Override
        public void onFailure(Call<ResponseSkills> call, Throwable t)
        {
            Toasty.error(context, getString(R.string.toast_experienced_a_problem)).show();
            t.printStackTrace();
        }
    });
}

In addition to the problem with the NPE, you are also going to run into problems with the way you are using realm. Realm needs to be accessed on it's own thread, it will cause crashes in your app if you access it from the UI thread like you are doing in your code above.

Thomas Cook
  • 4,371
  • 2
  • 25
  • 42