2

I'm creating an app that display movie poster in a gridview. I'm using an API from themoviedb.org. The problem is I'm getting the follow error when I run my app and I'm struggling to fix it:

07-18 20:27:39.244    2751-2751/? E/AndroidRuntime﹕ FATAL EXCEPTION: main
    Process: com.projmobileapp.pmdbadd.pmdb, PID: 2751
    java.lang.RuntimeException: Unable to start activity ComponentInfo{com.projmobileapp.pmdbadd.pmdb/com.projmobileapp.pmdbadd.pmdb.MainActivity}: android.view.InflateException: Binary XML file line #1: Error inflating class fragment
            at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2693)
            at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2758)
            at android.app.ActivityThread.access$900(ActivityThread.java:177)
            at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1448)
            at android.os.Handler.dispatchMessage(Handler.java:102)
            at android.os.Looper.loop(Looper.java:145)
            at android.app.ActivityThread.main(ActivityThread.java:5942)
            at java.lang.reflect.Method.invoke(Native Method)
            at java.lang.reflect.Method.invoke(Method.java:372)
            at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1400)
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1195)
     Caused by: android.view.InflateException: Binary XML file line #1: Error inflating class fragment
            at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:770)
            at android.view.LayoutInflater.inflate(LayoutInflater.java:483)
            at android.view.LayoutInflater.inflate(LayoutInflater.java:415)
            at android.view.LayoutInflater.inflate(LayoutInflater.java:366)
            at android.support.v7.app.AppCompatDelegateImplV7.setContentView(AppCompatDelegateImplV7.java:249)
            at android.support.v7.app.AppCompatActivity.setContentView(AppCompatActivity.java:106)
            at com.projmobileapp.pmdbadd.pmdb.MainActivity.onCreate(MainActivity.java:15)
            at android.app.Activity.performCreate(Activity.java:6289)
            at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1119)
            at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2646)
            at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2758)
            at android.app.ActivityThread.access$900(ActivityThread.java:177)
            at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1448)
            at android.os.Handler.dispatchMessage(Handler.java:102)
            at android.os.Looper.loop(Looper.java:145)
            at android.app.ActivityThread.main(ActivityThread.java:5942)
            at java.lang.reflect.Method.invoke(Native Method)
            at java.lang.reflect.Method.invoke(Method.java:372)
            at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1400)
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1195)
     Caused by: java.lang.NullPointerException: Attempt to invoke interface method 'int java.util.List.size()' on a null object reference
            at android.widget.ArrayAdapter.getCount(ArrayAdapter.java:330)
            at android.widget.GridView.setAdapter(GridView.java:201)
            at com.projmobileapp.pmdbadd.pmdb.MainActivityFragment.onCreateView(MainActivityFragment.java:53)
            at android.support.v4.app.Fragment.performCreateView(Fragment.java:1789)
            at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:924)
            at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1116)
            at android.support.v4.app.FragmentManagerImpl.addFragment(FragmentManager.java:1218)
            at android.support.v4.app.FragmentManagerImpl.onCreateView(FragmentManager.java:2170)
            at android.support.v4.app.FragmentActivity.onCreateView(FragmentActivity.java:300)
            at android.support.v7.app.AppCompatDelegateImplV7.callActivityOnCreateView(AppCompatDelegateImplV7.java:838)
            at android.support.v7.app.AppCompatDelegateImplV11.callActivityOnCreateView(AppCompatDelegateImplV11.java:34)
            at android.support.v7.app.AppCompatDelegateImplV7.onCreateView(AppCompatDelegateImplV7.java:826)
            at android.support.v4.view.LayoutInflaterCompatHC$FactoryWrapperHC.onCreateView(LayoutInflaterCompatHC.java:44)
            at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:732)
            at android.view.LayoutInflater.inflate(LayoutInflater.java:483)
            at android.view.LayoutInflater.inflate(LayoutInflater.java:415)
            at android.view.LayoutInflater.inflate(LayoutInflater.java:366)
            at android.support.v7.app.AppCompatDelegateImplV7.setContentView(AppCompatDelegateImplV7.java:249)
            at android.support.v7.app.AppCompatActivity.setContentView(AppCompatActivity.java:106)
            at com.projmobileapp.pmdbadd.pmdb.MainActivity.onCreate(MainActivity.java:15)
            at android.app.Activity.performCreate(Activity.java:6289)
            at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1119)
            at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2646)
            at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2758)
            at android.app.ActivityThread.access$900(ActivityThread.java:177)
            at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1448)
            at android.os.Handler.dispatchMessage(Handler.java:102)
            at android.os.Looper.loop(Looper.java:145)
            at android.app.ActivityThread.main(ActivityThread.java:5942)
            at java.lang.reflect.Method.invoke(Native Method)
            at java.lang.reflect.Method.invoke(Method.java:372)
            at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1400)
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1195)
07-18 20:27:39.244    2702-2702/? W/ResourcesManager﹕ Asset path '/system/framework/twframework.jar' does not exist or contains no resources.

Here's my code

package com.projmobileapp.pmdbadd.pmdb;

import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.GridView;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;

public class MainActivityFragment extends Fragment {

    private ArrayList<Movie> flavorList;


    public MainActivityFragment() {
    }

    MovieAdapter mMovieAdapter;


    public void onSaveInstanceState(Bundle outState) {
        outState.putParcelableArrayList("flavors", flavorList);
        super.onSaveInstanceState(outState);
    }


    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {

        View rootView = inflater.inflate(R.layout.fragment_main, container, false);

        mMovieAdapter = new MovieAdapter(getActivity(), flavorList);
        GridView listView = (GridView) rootView.findViewById(R.id.gridview_movie);
        listView.setAdapter(mMovieAdapter);
        updateMovie();

    return rootView;

    }


    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setHasOptionsMenu(true);
    }

    @Override
    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
        // Inflate the menu; this adds items to the action bar if it is present.
        inflater.inflate(R.menu.mainactivityfragment, menu);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        int id = item.getItemId();
        if (id == R.id.action_refresh) {
            updateMovie();
            return true;
        }
        return super.onOptionsItemSelected(item);
    }

    private void updateMovie() {
        FetchMovieTask movieTask = new FetchMovieTask();
        movieTask.execute();
    }

    class FetchMovieTask extends AsyncTask<Void, Void, List<Movie>> {
        private final String LOG_TAG = FetchMovieTask.class.getSimpleName();

        @Override
        protected List<Movie> doInBackground(Void... params) {
            HttpURLConnection urlConnection = null;
            BufferedReader reader = null;

            // Will contain the raw JSON response as a string.
            String movieJsonStr = null;

            try {
                URL url = new URL("http://api.themoviedb.org/3/discover/movie?sort_by=popularity.desc&api_key=InsertAPIKey");

                urlConnection = (HttpURLConnection) url.openConnection();
                urlConnection.setRequestMethod("GET");
                urlConnection.connect();

                // Read the input stream into a String
                InputStream inputStream = urlConnection.getInputStream();
                StringBuffer buffer = new StringBuffer();
                if (inputStream == null) {
                    // Nothing to do.
                    return null;
                }
                reader = new BufferedReader(new InputStreamReader(inputStream));

                String line;
                while ((line = reader.readLine()) != null) {

                    buffer.append(line + "\n");
                }

                if (buffer.length() == 0) {
                    // Stream was empty.  No point in parsing.
                    return null;
                }
                movieJsonStr = buffer.toString();

            } catch (IOException e) {
                Log.e(LOG_TAG, "Error ", e);
                return null;
            } finally {
                if (urlConnection != null) {
                    urlConnection.disconnect();
                }
                if (reader != null) {
                    try {
                        reader.close();
                    } catch (final IOException e) {
                        Log.e(LOG_TAG, "Error closing stream", e);
                    }
                }
            }
            try {
                return getMovieDataFromJson(movieJsonStr);
            } catch (JSONException j) {
                Log.e(LOG_TAG, "JSON Error", j);
            }
            return null;
        }

        private List<Movie> getMovieDataFromJson(String forecastJsonStr)
                throws JSONException {
            List<Movie> movies = new ArrayList<>();
            JSONObject movieJson = new JSONObject(forecastJsonStr);
            JSONArray movieArray = movieJson.getJSONArray("results");
            for (int i = 0; i < movieArray.length(); i++) {
                JSONObject movie = movieArray.getJSONObject(i);
                Movie movie1 = new Movie(movieJson.getString("original_title"), movieJson.getDouble("vote_average"), movieJson.getString("release_date"), movieJson.getString("overview"), movieJson.getString("poster_path"));
                movies.add(movie1);

            }
            return movies;
        }





        @Override
        protected void onPostExecute(List<Movie> movies) {
            if (movies != null) {
                mMovieAdapter.clear();
                for(Movie dayForecastStr : movies) {
                    mMovieAdapter.add(dayForecastStr);
                }
                // New data is back from the server.  Hooray!
            }
        }
    }


}

Could someone please advise

D. Ace
  • 398
  • 4
  • 9
  • 25
  • in onCreateView(), flavorList is null, but it shouldn't be. Fix that – adnan_e Jul 18 '15 at 19:43
  • First of all remove the MainActivityFragment() {} constructor. You dont mess with Fragment and activity constructors in android. Just delete or comment it out. – Samrat Dutta Jul 18 '15 at 19:44
  • I believe that you are supposedly saving the list out into the bundle on saveInstanceState for rotation, you're not actually reading it back in onCreate or onCreateView in case the bundle is not null. Also, I'm not even sure if `Movie` is actually a parcelable (use http://parcelabler.com ), but even if it were, this wouldn't work as you need to use `writeTypedList` and `readTypedList` and NOT `ParcelableArrayList`. – EpicPandaForce Jul 18 '15 at 19:46
  • And you should be getting `flavorList` from somewhere. You should at least replace it with Collections.emptyList(); – EpicPandaForce Jul 18 '15 at 19:48
  • 1
    @SamratDutta All subclasses of Fragment NEED an empty constructor. See the [docs](http://developer.android.com/reference/android/app/Fragment.html) – adnan_e Jul 18 '15 at 19:56
  • Okay. yes. now I have read. That was my mistake then. I did not have any idea about that. Thanks for the info. I learnt something new. :) – Samrat Dutta Jul 18 '15 at 19:58
  • 1
    @AdnanElezovic: "All subclasses of Fragment NEED an empty constructor" -- yes, and therefore they usually do not implement one, getting instead the automatically-generated public zero-argument constructor. Having any constructor implemented on a `Fragment` subclass is a code smell. Having a constructor that fails to chain to a superclass constructor is an outright mistake, as now any code in the `Fragment` constructor will not get executed. At the moment, there is no code there, but that could easily change in future versions of Android (or `support-v4` for the backport). – CommonsWare Jul 18 '15 at 20:14

3 Answers3

4
mMovieAdapter = new MovieAdapter(getActivity(), flavorList);

At this point in the code, flavorList is null. Do not create the MovieAdapter and call setAdapter() on the ListView until you have your movies. Or, initialize flavorList to be an empty ArrayList<Movie> before creating the adapter.

Also, please remove your broken MainActivityFragment constructor, or chain to the superclass constructor from inside of it.

CommonsWare
  • 986,068
  • 189
  • 2,389
  • 2,491
  • Apparently, all classes that extend `Fragment` require an empty constructor. So, that is not broken afterall. – Samrat Dutta Jul 18 '15 at 20:00
  • @SamratDutta: Oh, yes it is. Never implement a constructor without chaining to the superclass unless you wrote the entire class tree all the way to `Object`. Moreover, by simply deleting this broken constructor, the OP would get the automatically-generated public zero-argument constructor, which does chain to the superclass constructor. – CommonsWare Jul 18 '15 at 20:11
  • @CommonsWare If you check out the given template for a fragment (when you create a new fragment in android studio), you will see it comes with a empty constructor with no super() call, and a comment block above /* Required emply constructor */. – adnan_e Jul 18 '15 at 20:16
  • @AdnanElezovic: And I believe that I already filed a bug for that broken bit of the template. If not, I'll do so shortly. – CommonsWare Jul 18 '15 at 20:17
  • 1
    @CommonsWare That was exactly my initial reaction. And that is what i commented in the original post. That not writting anything means having a default constructor with a call to the super class constructor. But then someone brought it to my attention that the empty constructor is required. I did a google search, and a stackoverflow answer suggested the same. That is when I made this comment. Anyway, i am clearly not well informed in this issue. So probably you are right. I need to do some research. – Samrat Dutta Jul 18 '15 at 20:18
  • FWIW, here is the issue that I just filed related to the fragment constructor: https://code.google.com/p/android/issues/detail?id=180496 – CommonsWare Jul 18 '15 at 20:27
4

I believe that you are supposedly saving the list out into the bundle on saveInstanceState for rotation, you're not actually reading it back in onCreate or onCreateView in case the bundle is not null. Also, I'm not even sure if Movie is actually a parcelable (use http://parcelabler.com ), but even if it were, this wouldn't work as you need to use writeTypedList and readTypedList and NOT writeParcelableArrayList (that returns List<Parcelable> on read, and that is NOT what you want).

But the current problem is that you should not give a null to the adapter in this line:

mMovieAdapter = new MovieAdapter(getActivity(), flavorList);

Initialize the adapter and set it only when you actually have data, or initialize it with an empty list.

Really though, use writeTypedList and readTypedList, otherwise you'll run into a lot of issues later on with state saving.

EpicPandaForce
  • 79,669
  • 27
  • 256
  • 428
  • hi thanks for the reply, would you be able to give an example of how I can use writeTypedList and readTypedList in this case? – D. Ace Jul 18 '15 at 20:53
1

For anyone that is using GsonFormatter plugin to retrieve information from a RESTful API, make sure that you do a clean Format of the Gson Formatter (Remove generated code in the class and redo it)..

Hongbin Wang
  • 1,186
  • 2
  • 14
  • 34
SJW
  • 65
  • 1
  • 10