what is different between theses two context codes?

Viewed 93

I'm using recyclderview in fragment. What is different between code 1 and code 2? When i first used code 2 with code 3, it occured an error.

@NonNull
    @Override
    public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        LayoutInflater inflater = LayoutInflater.from(parent.getContext()); // code 1

//        LayoutInflater inflater = (LayoutInflater)  context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); // code 2

        context = parent.getContext(); //code 3
        View itemView = inflater.inflate(R.layout.gallery_item, parent, false);
       
        return new ViewHolder(itemView);
    }

ERROR

java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.Object android.content.Context.getSystemService(java.lang.String)' on a null object reference
        at com.example.bstcproject.adapter.GalleryAdapter.onCreateViewHolder(GalleryAdapter.java:43)
        at com.example.bstcproject.adapter.GalleryAdapter.onCreateViewHolder(GalleryAdapter.java:23)

I left code 3 and i used code 1 instead of code 2. It worked well.

I think it related to Context. But i don't know what is different between code 1 and code 2.

Don't both code 1 and code 2 have Context? What is different?

3 Answers

In Android, LayoutInflater is used to convert an xml file (activity_main.xml, fragment_login.xml, gallery_item.xml, etc.) into its coressponding View object.

XML file -> LayoutInflater -> View object in Java

To retrieve an instance of LayoutInflater, we have several options:

Back to your question

Code 1

LayoutInflater inflater = LayoutInflater.from(parent.getContext());

parent: The parent of the current item, in this case, parent is the RecyclerView

RecyclerView is a sub-class of View class, each View will have its own context, in this case, getContext() return the activity where you display the RecyclerView.

Code 2

LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);

In Android, when initializing an instance of an Adapter, we usually pass a Context instance via a constructor, like this.

class MyAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
    private Context context;
    private List<Data> data;

    public MyAdapter(Context context, List<Data> data) {
        this.context = context;
        this.data = data;
    }

    @NonNull
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        View itemView = inflater.inflate(R.layout.gallery_item, parent, false);
        return new ViewHolder(itemView);
    }
}

In your case, because you do not assign any value to the context, that why the compiler throws that error.

Code 3

context = parent.getContext();

In this case, you don't need to pass a Context via a constructor, just use the context from parent.

class MyAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
    private List<Data> data;

    public MyAdapter(List<Data> data) {
        this.data = data;
    }

    @NonNull
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        Context context = parent.getContext();
        LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        View itemView = inflater.inflate(R.layout.gallery_item, parent, false);
        return new ViewHolder(itemView);
    }
}

Inside onBindViewHolder(), you have 3 options to retrieve an instance of LayoutInflater:

@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
    Context context = parent.getContext();

    // Option 1
    LayoutInflater inflater = LayoutInflater.from(context);

    // Option 2
    LayoutInflater inflater2 = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);

    // Option 3
    LayoutInflater inflater3 = ((Activity) context).getLayoutInflater();
    
    View itemView = inflater.inflate(R.layout.gallery_item, parent, false);
    return new ViewHolder(itemView);
}

I always choose option 1 because it's short.

Code '1' and code '2' is almost the same but native implementation throws an error when inflater wasn't found. We can open LayoutInfalter.from method implementation and check it:

/**
 * Obtains the LayoutInflater from the given context.
 */
 public static LayoutInflater from(Context context) {
     LayoutInflater LayoutInflater =
             (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
     if (LayoutInflater == null) {
         throw new AssertionError("LayoutInflater not found.");
     }
     return LayoutInflater;
 }

Evidently you need to call this:

LayoutInflater inflater = (LayoutInflater)  parent.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);

instead of this:

LayoutInflater inflater = (LayoutInflater)  context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);

As Context may not have been initialised before it's first used. It's good practice to keep scope of variables as tight as possible, so if you don't need to store the Context from onCreateViewHolder in an instance variable, then don't.

Related