title: How exactly does GridView recycle/reuse view in Android?
body: <p>I think I do understand the mechanism of reusing views for ListView, but for GridView, the way it recycles the views are not in expectation.</p>
  
  <p>in my sample ,i just simply recycle the view if convertView is not null in <code>getView()</code> from Adapter.</p>
  
  <p>Also I log the position of the view.</p>
  
  <p><img src="https://i.stack.imgur.com/RQtyw.png" alt="enter image description here"></p>
  
  <p>but the strange thing is after I check the logcat, I found even if i did not scroll at all, the view from position 0(the first vi!ew) is not null(<strong>at the very first time no recycled view so convertView for positon 0 should be null right?</strong>).</p>
  
  <p><img src="https://i.stack.imgur.com/2tjjE.jpg" alt="enter image description here"></p>
  
  <p>even stranger thing is , after it logs all 54 items (as you can see the whole screen can hold 54 views), it logs serveral position 0 's recycled view</p>
  
  <p><img src="https://i.stack.imgur.com/6GgfY.jpg" alt="enter image description here"></p>
  
  <p>so it totally confuses me the way GridView recycle views. In my opinion it should be something like ListView, if I scroll down, the top views go off from the screen, then they go back to the bottom of the list one by one, but seems like in GridView its not.</p>
  
  <p><strong>I even use HashMap to calculate the recycle times of each View(by String to Integer Entry, Key is the <code>toString()</code> value of a view), i found the recycle times are not distributed equally, while some of the view has 30 times of recycle while some of them have just once.....</strong></p>
  
  <hr>
  
  <p><strong>Edit(add the recycle codes and also the screenshot for exception)</strong></p>
  
  <p><img src="https://i.stack.imgur.com/gPBXx.png" alt="enter image description here"></p>
  
  <p><img src="https://i.stack.imgur.com/nWwWQ.png" alt="enter image description here"></p>
  
  <pre><code>(Edit) even though I tried using `setImageDrawable(null)` it still throws exception, as noticed you can see the exception will be thrown no matter I called it before or after the recycle
  </code></pre>
  
  <p><img src="https://i.stack.imgur.com/ZYeWJ.png" alt="enter image description here">
  <img src="https://i.stack.imgur.com/FrBIk.png" alt="enter image description here"></p>
  
  <p>I think the point to this issue is Android is not handling recycled view as I expected, so my guess is even if a convertView is not null, it does not prove it is going off the screen, as you answered before, android will use the position 0 item to do initial measurement, so it may be the root cause.</p>
  
  <pre><code>**Edit Again**
  </code></pre>
  
  <p><strong>Adapter code:</strong></p>
  
  <pre><code> package com.example.photoswalldemo;
  
      import android.content.Context;
      import android.graphics.Bitmap;
      import android.graphics.BitmapFactory;
      import android.os.AsyncTask;
      import android.util.LruCache;
      import android.view.LayoutInflater;
      import android.view.View;
      import android.view.ViewGroup;
      import android.widget.AbsListView;
      import android.widget.AbsListView.OnScrollListener;
      import android.widget.ArrayAdapter;
      import android.widget.GridView;
      import android.widget.ImageView;
  
      import com.android.volley.RequestQueue;
      import com.android.volley.Response;
      import com.android.volley.VolleyError;
      import com.android.volley.toolbox.ImageRequest;
      import com.android.volley.toolbox.Volley;
  
      import java.net.HttpURLConnection;
      import java.net.URL;
      import java.util.HashSet;
      import java.util.Set;
  
      /**
       * GridView的适配器，负责异步从网络上下载图片展示在照片墙上。
       * 
       * @author guolin
       */
      public class PhotoWallAdapter extends ArrayAdapter&lt;String&gt; implements OnScrollListener {
  
          /**
           * 记录所有正在下载或等待下载的任务。
           */
          private Set&lt;BitmapWorkerTask&gt; taskCollection;
  
          /**
           * 图片缓存技术的核心类，用于缓存所有下载好的图片，在程序内存达到设定值时会将最少最近使用的图片移除掉。
           */
          private LruCache&lt;String, Bitmap&gt; mMemoryCache;
  
          /**
           * GridView的实例
           */
          private GridView mPhotoWall;
  
          /**
           * 第一张可见图片的下标
           */
          private int mFirstVisibleItem;
  
          /**
           * 一屏有多少张图片可见
           */
          private int mVisibleItemCount;
  
          /**
           * 记录是否刚打开程序，用于解决进入程序不滚动屏幕，不会下载图片的问题。
           */
          private boolean isFirstEnter = true;
  
  
          private RequestQueue queue= Volley.newRequestQueue(getContext());
  
  
  
          public PhotoWallAdapter(Context context, int textViewResourceId, String[] objects,
                  GridView photoWall) {
              super(context, textViewResourceId, objects);
              mPhotoWall = photoWall;
              taskCollection = new HashSet&lt;BitmapWorkerTask&gt;();
              // 获取应用程序最大可用内存
              int maxMemory = (int) Runtime.getRuntime().maxMemory();
              int cacheSize = maxMemory / 8;
              // 设置图片缓存大小为程序最大可用内存的1/8
              mMemoryCache = new LruCache&lt;String, Bitmap&gt;(cacheSize) {
                  @Override
                  protected int sizeOf(String key, Bitmap bitmap) {
                      return bitmap.getByteCount();
                  }
              };
              mPhotoWall.setOnScrollListener(this);
          }
  
          @Override
          public View getView(int position, View convertView, ViewGroup parent) {
             // Log.e("Monitor View","This View is on position "+position+" and its "+convertView);
              final String url = getItem(position);
              View view;
              if (convertView == null) {
                  view = LayoutInflater.from(getContext()).inflate(R.layout.photo_layout, null);
              } else {
  
                  View tmp=convertView;
  
      //            ImageView tmp1=(ImageView)tmp.findViewById(R.id.photo);
      //            if(tmp1.getDrawable()!=null){
      //                //Toast.makeText(getContext(),"it has Drawable!,",Toast.LENGTH_LONG).show();
      //                Log.e("Monitor View", "This View is on position " + position + " and its view " + convertView+" and this drawable is "+tmp1.getDrawable());
      //
      //                Bitmap bitmap=((BitmapDrawable)tmp1.getDrawable()).getBitmap();
      //                tmp1.setImageDrawable(null);
      //                tmp1.setImageBitmap(null);
      //                bitmap.recycle();
      //                bitmap=null;
      //                //tmp1.setImageDrawable(null);
  
  
              //    }
  
  
                  view = convertView;
  
  
              }
              final ImageView photo = (ImageView) view.findViewById(R.id.photo);
              // 给ImageView设置一个Tag，保证异步加载图片时不会乱序
              photo.setTag(url);
              setImageView(url, photo);
              return view;
          }
  
          /**
           * 给ImageView设置图片。首先从LruCache中取出图片的缓存，设置到ImageView上。如果LruCache中没有该图片的缓存，
           * 就给ImageView设置一张默认图片。
           * 
           * @param imageUrl
           *            图片的URL地址，用于作为LruCache的键。
           * @param imageView
           *            用于显示图片的控件。
           */
          private void setImageView(String imageUrl, ImageView imageView) {
              Bitmap bitmap = getBitmapFromMemoryCache(imageUrl);
              if (bitmap != null) {
                  imageView.setImageBitmap(bitmap);
              } else {
                  imageView.setImageResource(R.drawable.empty_photo);
              }
          }
  
          /**
           * 将一张图片存储到LruCache中。
           * 
           * @param key
           *            LruCache的键，这里传入图片的URL地址。
           * @param bitmap
           *            LruCache的键，这里传入从网络上下载的Bitmap对象。
           */
          public void addBitmapToMemoryCache(String key, Bitmap bitmap) {
              if (getBitmapFromMemoryCache(key) == null) {
                  mMemoryCache.put(key, bitmap);
              }
          }
  
          /**
           * 从LruCache中获取一张图片，如果不存在就返回null。
           * 
           * @param key
           *            LruCache的键，这里传入图片的URL地址。
           * @return 对应传入键的Bitmap对象，或者null。
           */
          public Bitmap getBitmapFromMemoryCache(String key) {
              return mMemoryCache.get(key);
          }
  
          @Override
          public void onScrollStateChanged(AbsListView view, int scrollState) {
              // 仅当GridView静止时才去下载图片，GridView滑动时取消所有正在下载的任务
              if (scrollState == SCROLL_STATE_IDLE) {
                  loadBitmaps(mFirstVisibleItem, mVisibleItemCount);
              } else {
                  cancelAllTasks();
              }
          }
  
          @Override
          public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
                  int totalItemCount) {
              mFirstVisibleItem = firstVisibleItem;
              mVisibleItemCount = visibleItemCount;
              // 下载的任务应该由onScrollStateChanged里调用，但首次进入程序时onScrollStateChanged并不会调用，
              // 因此在这里为首次进入程序开启下载任务。
              if (isFirstEnter &amp;&amp; visibleItemCount &gt; 0) {
                  loadBitmaps(firstVisibleItem, visibleItemCount);
                  isFirstEnter = false;
              }
          }
  
          /**
           * 加载Bitmap对象。此方法会在LruCache中检查所有屏幕中可见的ImageView的Bitmap对象，
           * 如果发现任何一个ImageView的Bitmap对象不在缓存中，就会开启异步线程去下载图片。
           * 
           * @param firstVisibleItem
           *            第一个可见的ImageView的下标
           * @param visibleItemCount
           *            屏幕中总共可见的元素数
           */
          private void loadBitmaps(int firstVisibleItem, int visibleItemCount) {
              try {
                  for (int i = firstVisibleItem; i &lt; firstVisibleItem + visibleItemCount; i++) {
                      String imageUrl = Images.imageThumbUrls[i];
                      Bitmap bitmap = getBitmapFromMemoryCache(imageUrl);
                      if (bitmap == null) {
                          //BitmapWorkerTask task = new BitmapWorkerTask();
                          //taskCollection.add(task);
                          //task.execute(imageUrl);
  
                          //new method , use volley
  
                          useVolley(imageUrl);
                      } else {
                          ImageView imageView = (ImageView) mPhotoWall.findViewWithTag(imageUrl);
                          if (imageView != null &amp;&amp; bitmap != null) {
                              imageView.setImageBitmap(bitmap);
                          }
                      }
                  }
              } catch (Exception e) {
                  e.printStackTrace();
              }
          }
  
          /**
           * 取消所有正在下载或等待下载的任务。
           */
          public void cancelAllTasks() {
              if (taskCollection != null) {
                  for (BitmapWorkerTask task : taskCollection) {
                      task.cancel(false);
                  }
              }
          }
  
          /**
           * 异步下载图片的任务。
           * 
           * @author guolin
           */
          class BitmapWorkerTask extends AsyncTask&lt;String, Void, Bitmap&gt; {
  
              /**
               * 图片的URL地址
               */
              private String imageUrl;
  
              @Override
              protected Bitmap doInBackground(String... params) {
                  imageUrl = params[0];
                  // 在后台开始下载图片
                  Bitmap bitmap = downloadBitmap(params[0]);
                  if (bitmap != null) {
                      // 图片下载完成后缓存到LrcCache中
                      addBitmapToMemoryCache(params[0], bitmap);
                  }
                  return bitmap;
              }
  
              @Override
              protected void onPostExecute(Bitmap bitmap) {
                  super.onPostExecute(bitmap);
                  // 根据Tag找到相应的ImageView控件，将下载好的图片显示出来。
                  ImageView imageView = (ImageView) mPhotoWall.findViewWithTag(imageUrl);
                  if (imageView != null &amp;&amp; bitmap != null) {
                      imageView.setImageBitmap(bitmap);
                  }
                  taskCollection.remove(this);
              }
  
              /**
               * 建立HTTP请求，并获取Bitmap对象。
               * 
               * @param imageUrl
               *            图片的URL地址
               * @return 解析后的Bitmap对象
               */
              private Bitmap downloadBitmap(String imageUrl) {
                  Bitmap bitmap = null;
                  HttpURLConnection con = null;
                  try {
                      URL url = new URL(imageUrl);
                      con = (HttpURLConnection) url.openConnection();
                      con.setConnectTimeout(5 * 1000);
                      con.setReadTimeout(10 * 1000);
                      con.setDoInput(true);
                      con.setDoOutput(true);
                      bitmap = BitmapFactory.decodeStream(con.getInputStream());
                  } catch (Exception e) {
                      e.printStackTrace();
                  } finally {
                      if (con != null) {
                          con.disconnect();
                      }
                  }
                  return bitmap;
              }
  
          }
  
  
  
          private void useVolley(String url){
  
              final String tmpurl=url;
  
             // ImageView tmpImageView=(ImageView)mPhotoWall.findViewWithTag(tmpurl);
              ImageRequest imageRequest=new ImageRequest(url,new Response.Listener&lt;Bitmap&gt;() {
                  @Override
                  public void onResponse(Bitmap response) {
  
                      ImageView tmpImageView=(ImageView)mPhotoWall.findViewWithTag(tmpurl);
  
                      tmpImageView.setImageBitmap(response);
  
                      mMemoryCache.put(tmpurl,response);
                  }
              },0, 0, Bitmap.Config.RGB_565, new Response.ErrorListener() {
                  @Override
                  public void onErrorResponse(VolleyError error) {
  
                      ImageView tmpImageView=(ImageView)mPhotoWall.findViewWithTag(tmpurl);
                      //imageView.setImageResource(R.drawable.default_image);
                      tmpImageView.setImageResource(R.drawable.empty_photo);
                  }
              });
  
  
  
              this.queue.add(imageRequest);
  
          }
  
      }
  </code></pre>
  
  <p><strong>Main Activity Code:</strong></p>
  
  <pre><code>public class MainActivity extends Activity {
  
      /**
       * 用于展示照片墙的GridView
       */
      private GridView mPhotoWall;
  
      /**
       * GridView的适配器
       */
      private PhotoWallAdapter adapter;
  
      @Override
      protected void onCreate(Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
          setContentView(R.layout.activity_main);
          mPhotoWall = (GridView) findViewById(R.id.photo_wall);
          adapter = new PhotoWallAdapter(this, 0, Images.imageThumbUrls, mPhotoWall);
          mPhotoWall.setAdapter(adapter);
      }
  
      @Override
      protected void onDestroy() {
          super.onDestroy();
          // 退出程序时结束所有的下载任务
          adapter.cancelAllTasks();
      }
  
  }
  </code></pre>