title: unable to populate gridview from api call
body: <p>I have combed stackoverflow for solutions to this, I've tried tons of snippets, and haven't been able to figure out the problem.</p>
  
  <p>So I am following an android Udacity course and have reached a point where they ask me to populate a gridview with movie posters from an api call to The Movie DB.
  I've seen many others here on stackoverflow doing the same project however the main difference (and possibly why their solutions haven't worked for me) I have is that I am trying to do this with all the major classes in separate files, rather than have everything in the main activity or something like that.</p>
  
  <p>I've tried all the solutions that worked for them, such as move the gridview adapter declaration and setadapter to the end of onPostExecute in asynctask, and modified my ImageAdapter class over and over again, but I can't get it to work.</p>
  
  <p>My app does the API call just fine, Logs the recieved JSON and all the poster links, as well as copy the links into an arraylist correctly; but the gridview does not populate. </p>
  
  <p>here is my code:</p>
  
  <p><strong>MainActivity.java</strong></p>
  
  <pre><code>public class MainActivity extends AppCompatActivity {
  
      @Override
      protected void onCreate(Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
          setContentView(R.layout.activity_main);
          Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
          setSupportActionBar(toolbar);
          if (savedInstanceState == null) {
              getSupportFragmentManager().beginTransaction()
                      .add(R.id.container, new GalleryFragment())
                      .commit();
          }
  
  
      }
  
      @Override
      public boolean onCreateOptionsMenu(Menu menu) {
          // Inflate the menu; this adds items to the action bar if it is present.
          getMenuInflater().inflate(R.menu.menu_main, menu);
          return true;
      }
  
      @Override
      public boolean onOptionsItemSelected(MenuItem item) {
          // Handle action bar item clicks here. The action bar will
          // automatically handle clicks on the Home/Up button, so long
          // as you specify a parent activity in AndroidManifest.xml.
          int id = item.getItemId();
  
          //noinspection SimplifiableIfStatement
          if (id == R.id.action_settings) {
              return true;
          }
  
          return super.onOptionsItemSelected(item);
      }
  }
  </code></pre>
  
  <p><strong>ImageAdapter.java</strong></p>
  
  <pre><code>public class ImageAdapter extends BaseAdapter {
  
      private final String LOG_TAG = ImageAdapter.class.getSimpleName();
  
      private List&lt;?&gt; mImageList = new ArrayList&lt;&gt;();
      private LayoutInflater inflater;
  
      public ImageAdapter(LayoutInflater i, List&lt;?&gt; images) {
  
  
          this.mImageList = images;
          this.inflater = i;
      }
  
      @Override
      public Object getItem(int position) {
          return mImageList.get(position);
      }
  
      @Override
      public int getCount() {
          return mImageList.size();
      }
  
      @Override
      public long getItemId(int position) {
          return position;
      }
  
      public View getView(int position, View convertView, ViewGroup parent) {
          ImageView imageView;
          if (convertView == null){
              imageView = new ImageView(inflater.getContext());
          } else {
            imageView = (ImageView) convertView;
          }
  
          Log.v(LOG_TAG,"New one: " + mImageList.get(position).toString());
  
          Picasso
                  .with(inflater.getContext())
                  .load(mImageList.get(position).toString())
                  .into(imageView);
          imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
          imageView.setAdjustViewBounds(true);
  
          return imageView;
      }
  }
  </code></pre>
  
  <p><strong>GalleryFragment.java</strong></p>
  
  <pre><code>public class GalleryFragment extends Fragment {
  
      public static GridView gridView;
      public static ImageAdapter imageAdapter;
  
      public GalleryFragment() {
          // Required empty public constructor
      }
  
      @Override
      public void onStart() {
          super.onStart();
          FetchMovies data = new FetchMovies(getActivity());
          data.execute();
      }
  
      @Override
      public void onCreate(Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
  
          setHasOptionsMenu(true);
      }
  
      @Override
      public View onCreateView(LayoutInflater inflater, ViewGroup container,
                               Bundle savedInstanceState) {
  
          View rootView = inflater.inflate(R.layout.fragment_gallery, container, false);
  
          gridView = (GridView) rootView.findViewById(R.id.gridview);
  
          return rootView;
      }
  }
  </code></pre>
  
  <p><strong>FetchMovies.java</strong></p>
  
  <pre><code>public class FetchMovies extends AsyncTask &lt;Void, Void, String[]&gt; {
  
      private final String LOG_TAG = FetchMovies.class.getSimpleName();
  
      private Context context;
      List&lt;Uri&gt; posterURLs = new ArrayList&lt;&gt;();
      String[] title, overview, popularity, rating, releaseDate;
  
      public FetchMovies(Context c){
          this.context = c;
      }
  
  
  
  
  
      /**
       * Prepare image URL for presentation.
       */
      private String formatURL(String relativeURL) {
          String imageBaseURL = "http://image.tmdb.org/t/p/";
          String size = "w185";
          relativeURL = relativeURL.substring(1);
          Uri uri = Uri.parse(imageBaseURL).buildUpon()
                  .appendPath(size)
                  .appendPath(relativeURL).build();
          return uri.toString();
      }
  
  
      private String[] getMovieDataFromJson(String movieJsonStr)
              throws JSONException {
  
          // These are the names of the JSON objects that need to be extracted.
          final String RESULT_LIST = "results";
          final String TITLE = "original_title";
          final String POSTER_URL = "poster_path";
          final String OVERVIEW = "overview";
          final String POPULARITY = "popularity";
          final String RATING = "vote_average";
          final String RELEASE_DATE = "release_date";
  
          JSONObject allMovieData = new JSONObject(movieJsonStr);
          JSONArray resultsArray = allMovieData.getJSONArray(RESULT_LIST);
  
          String[] posterPaths = new String[resultsArray.length()];
          title = new String[resultsArray.length()];
          overview = new String[resultsArray.length()];
          popularity = new String[resultsArray.length()];
          rating = new String[resultsArray.length()];
          releaseDate = new String[resultsArray.length()];
  
          for(int i = 0; i &lt; resultsArray.length(); i++) {
  
              // Get the JSON object representing one movie's details
              JSONObject eachMovie = resultsArray.getJSONObject(i);
  
              title[i] = eachMovie.getString(TITLE);
              String relativeURL = eachMovie.getString(POSTER_URL);
              posterPaths[i] = formatURL(relativeURL);
              overview[i] = eachMovie.getString(OVERVIEW);
              popularity[i] = eachMovie.getString(POPULARITY);
              rating[i] = eachMovie.getString(RATING);
              releaseDate[i] = eachMovie.getString(RELEASE_DATE);
              Log.v("poster path", posterPaths[i]);
          }
  
          return posterPaths;
  
      }
  
  
      @Override
      protected String[] doInBackground(Void... params) {
          HttpURLConnection urlConnection = null;
          BufferedReader reader = null;
  
          //For building the URL for the weather query from OpenWeatherMap
          final String BASE_URL = "http://api.themoviedb.org/3/discover/movie?";
          final String SORT_PARAM = "sort_by";
          final String API_PARAM = "api_key";
  
          String sort_by = "popularity.desc",
                  apiKey = "MY API KEY IS PLACED HERE";
  
          // Will contain the raw JSON response as a string.
          String movieJsonStr = null;
  
          try {
              // Construct the URL for the OpenWeatherMap query
              // Possible parameters are available at OWM's forecast API page, at
              // http://openweathermap.org/API#forecast
              Uri queryUri = Uri.parse(BASE_URL).buildUpon()
                      //.appendQueryParameter(SORT_PARAM,params[0])
                      .appendQueryParameter(SORT_PARAM, sort_by)
                      .appendQueryParameter(API_PARAM,apiKey).build();
  
              URL queryUrl = new URL(queryUri.toString());
              // Create the request to TheMovieDB, and open the connection
              urlConnection = (HttpURLConnection) queryUrl.openConnection();
              urlConnection.setRequestMethod("GET");
              urlConnection.connect();
  
              // Read the input stream into a String
              InputStream inputStream = urlConnection.getInputStream();
              StringBuffer buffer = new StringBuffer();
              if (inputStream == null) {
                  Log.v(LOG_TAG, "Couldn't open input stream.");
                  // Nothing to do.
                  return null;
              }
              reader = new BufferedReader(new InputStreamReader(inputStream));
  
              String line;
              while ((line = reader.readLine()) != null) {
                  // Since it's JSON, adding a newline isn't necessary (it won't affect parsing)
                  // But it does make debugging a *lot* easier if you print out the completed
                  // buffer for debugging.
                  buffer.append(line + "\n");
              }
  
              if (buffer.length() == 0) {
                  Log.v(LOG_TAG, "Input stream was empty.");
                  // Stream was empty.  No point in parsing.
                  return null;
              }
  
              //if all's well, parse the required data and return it to the system
              //(which then calls the onPostExecute() method with this data).
              movieJsonStr = buffer.toString();
              Log.v(LOG_TAG,movieJsonStr);
              return getMovieDataFromJson(movieJsonStr);
              //return getWeatherDataFromJson(forecastJsonStr, numDays);
  
          } catch (IOException e) {
              Log.e(LOG_TAG, "Error: Couldn't get movie data. ", e);
              // If the code didn't successfully get the weather data, there's no point in attempting
              // to parse it.
              return null;
          } catch (JSONException e) {
              Log.e(LOG_TAG, "Error in parsing: ", e);
              //If there is an error in parsing the JSON data, there's nothing to display.
              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);
                  }
              }
          }
  
      }
  
      @Override
      protected void onPostExecute(String[] strings) {
          super.onPostExecute(strings);
  
          for (int i=0; i&lt;strings.length;i++){
              Uri uri = Uri.parse(strings[i]);
              posterURLs.add(uri);
          }
  
          LayoutInflater inflater = LayoutInflater.from(context);
          GalleryFragment.imageAdapter = new ImageAdapter(inflater,posterURLs);
  
          GalleryFragment.gridView.setAdapter(GalleryFragment.imageAdapter);
  
  
      }
  
  
  }
  </code></pre>
  
  <p>here are my layout files:</p>
  
  <p><strong>fragment_gallery.xml</strong></p>
  
  <pre><code>&lt;FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
      xmlns:tools="http://schemas.android.com/tools"
      android:layout_width="match_parent"
      android:layout_height="match_parent"
      tools:context="com.example.zakna.popularmovies.GalleryFragment"&gt;
  
  
      &lt;GridView
          android:layout_width="wrap_content"
          android:layout_height="wrap_content"
          android:id="@+id/gridview"
          android:layout_gravity="start|top"
          android:numColumns="auto_fit"
          android:clickable="false" /&gt;
  
  &lt;/FrameLayout&gt;
  </code></pre>
  
  <p><strong>gallery_grid_item.xml</strong></p>
  
  <pre><code>&lt;ImageView xmlns:android="http://schemas.android.com/apk/res/android"
      android:layout_width="match_parent"
      android:layout_height="match_parent"
      android:id="@+id/movie_poster"&gt;
  
  &lt;/ImageView&gt;
  </code></pre>
  
  <p><strong>activity_main.xml</strong></p>
  
  <pre><code>&lt;android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
      xmlns:app="http://schemas.android.com/apk/res-auto"
      xmlns:tools="http://schemas.android.com/tools"
      android:layout_width="match_parent"
      android:layout_height="match_parent"
      android:fitsSystemWindows="true"
      tools:context="com.example.zakna.popularmovies.MainActivity"&gt;
  
      &lt;android.support.design.widget.AppBarLayout
          android:layout_width="match_parent"
          android:layout_height="wrap_content"
          android:theme="@style/AppTheme.AppBarOverlay"&gt;
  
          &lt;android.support.v7.widget.Toolbar
              android:id="@+id/toolbar"
              android:layout_width="match_parent"
              android:layout_height="?attr/actionBarSize"
              android:background="?attr/colorPrimary"
              app:popupTheme="@style/AppTheme.PopupOverlay" /&gt;
  
      &lt;/android.support.design.widget.AppBarLayout&gt;
  
      &lt;include layout="@layout/content_main" /&gt;
  
  &lt;/android.support.design.widget.CoordinatorLayout&gt;
  </code></pre>
  
  <p><strong>content_main.xml</strong></p>
  
  <pre><code>&lt;FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
      xmlns:tools="http://schemas.android.com/tools" android:id="@+id/container"
      android:layout_width="match_parent" android:layout_height="match_parent"
      tools:context=".MainActivity" tools:ignore="MergeRootFrame" /&gt;
  </code></pre>
  
  <p><strong>logcat output:</strong></p>
  
  <pre><code> W/System: ClassLoader referenced unknown path: /data/app/com.example.zakna.popularmovies-1/lib/arm64
   W/System: ClassLoader referenced unknown path: /data/app/com.example.zakna.popularmovies-1/lib/arm64
   W/art: Before Android 4.1, method android.graphics.PorterDuffColorFilter android.support.graphics.drawable.VectorDrawableCompat.updateTintFilter(android.graphics.PorterDuffColorFilter, android.content.res.ColorStateList, android.graphics.PorterDuff$Mode) would have incorrectly overridden the package-private method in android.graphics.drawable.Drawable
   I/DpmTcmClient: RegisterTcmMonitor from: com.android.okhttp.TcmIdleTimerMonitor
   D/AppTracker: App Event: start
   D/OpenGLRenderer: Use EGL_SWAP_BEHAVIOR_PRESERVED: true
   I/Adreno: QUALCOMM build                   : 183c040, Iff84fb1103
                                                                           Build Date                       : 03/18/16
                                                                           OpenGL ES Shader Compiler Version: XE031.06.00.02
                                                                           Local Branch                     : 
                                                                           Remote Branch                    : refs/tags/AU_LINUX_ANDROID_LA.BF64.1.2.2_RB4.06.00.01.180.031
                                                                           Remote Branch                    : NONE
                                                                           Reconstruct Branch               : NOTHING
   I/OpenGLRenderer: Initialized EGL, version 1.4
   D/AbstractTracker: Event success
   V/FetchMovies: SNIPPED JSON FILE
   V/poster path: http://image.tmdb.org/t/p/w185/e1mjopzAS2KNsvpbpahQ1a6SkSn.jpg
   V/poster path: http://image.tmdb.org/t/p/w185/lFSSLTlFozwpaGlO31OoUeirBgQ.jpg
   V/poster path: http://image.tmdb.org/t/p/w185/vOipe2myi26UDwP978hsYOrnUWC.jpg
   V/poster path: http://image.tmdb.org/t/p/w185/cGOPbv9wA5gEejkUN892JrveARt.jpg
   V/poster path: http://image.tmdb.org/t/p/w185/kqjL17yufvn9OVLyXYpvtyrFfak.jpg
   V/poster path: http://image.tmdb.org/t/p/w185/5N20rQURev5CNDcMjHVUZhpoCNC.jpg
   V/poster path: http://image.tmdb.org/t/p/w185/h28t2JNNGrZx0fIuAw8aHQFhIxR.jpg
   V/poster path: http://image.tmdb.org/t/p/w185/uANwnNNlX4DfUoA6UurzyoAAxTj.jpg
   V/poster path: http://image.tmdb.org/t/p/w185/4qnJ1hsMADxzwnOmnwjZTNp0rKT.jpg
   V/poster path: http://image.tmdb.org/t/p/w185/gj282Pniaa78ZJfbaixyLXnXEDI.jpg
   V/poster path: http://image.tmdb.org/t/p/w185/jjBgi2r5cRt36xF6iNUEhzscEcb.jpg
   V/poster path: http://image.tmdb.org/t/p/w185/vNCeqxbKyDHL9LUza03V2Im16wB.jpg
   V/poster path: http://image.tmdb.org/t/p/w185/5JU9ytZJyR3zmClGmVm9q4Geqbd.jpg
   V/poster path: http://image.tmdb.org/t/p/w185/ghL4ub6vwbYShlqCFHpoIRwx2sm.jpg
   V/poster path: http://image.tmdb.org/t/p/w185/dCgm7efXDmiABSdWDHBDBx2jwmn.jpg
   V/poster path: http://image.tmdb.org/t/p/w185/inVq3FRqcYIRl2la8iZikYYxFNR.jpg
   V/poster path: http://image.tmdb.org/t/p/w185/nBNZadXqJSdt05SHLqgT0HuC5Gm.jpg
   V/poster path: http://image.tmdb.org/t/p/w185/sM33SANp9z6rXW8Itn7NnG1GOEs.jpg
   V/poster path: http://image.tmdb.org/t/p/w185/aBBQSC8ZECGn6Wh92gKDOakSC8p.jpg
   V/poster path: http://image.tmdb.org/t/p/w185/t2mZzQXjpQxmqtJOPpe8Dr2YpMl.jpg
   V/ImageAdapter: New one: http://image.tmdb.org/t/p/w185/e1mjopzAS2KNsvpbpahQ1a6SkSn.jpg
   W/Settings: Setting airplane_mode_on has moved from android.provider.Settings.System to android.provider.Settings.Global, returning read-only value.
   W/IInputConnectionWrapper: getExtractedText on inactive InputConnection
   W/IInputConnectionWrapper: getTextBeforeCursor on inactive InputConnection
   W/IInputConnectionWrapper: getSelectedText on inactive InputConnection
  W/IInputConnectionWrapper: getTextAfterCursor on inactive InputConnection
   D/AppTracker: App Event: stop
   D/AbstractTracker: Event success
  </code></pre>
  
  <p>my Log also shows that my ImageAdapter class is only called once. Shouldn't it be called many times as it fills the gridview?
  At this point I'm pretty convinced that the problem has something to do with the layout files, maybe I'm not calling the right one in the right place or something like that, but I can't figure it out. help would be greatly appreciated, Thank you!!</p>