10

I have been suffering this issue for months and months (but now I am performance tuning). However, I now desperately need to know why my adapter feels it is necessary to run bindView up to 4 times on a record.

I have a custom cursor adapter that populates a gridview.

Some debug to show what's going on:

03-08 14:46:47.980: I/AdapterCursorGrid(20724): newView()
03-08 14:46:48.470: I/AdapterCursorGrid(20724): bindView()
03-08 14:46:48.570: I/AdapterCursorGrid(20724): --------------------------
03-08 14:46:48.570: I/AdapterCursorGrid(20724): bindView() Record Id: 1
03-08 14:46:48.570: I/AdapterCursorGrid(20724): bindView() Cursor Position: 0
03-08 14:46:48.570: I/AdapterCursorGrid(20724): bindView() View Type: 0
03-08 14:46:48.570: I/AdapterCursorGrid(20724): --------------------------
03-08 14:46:48.600: D/AdapterCursorGrid(20724): bindView() Avatar empty...
03-08 14:46:48.690: D/AdapterCursorGrid(20724): bindView() Picture creation...
03-08 14:46:49.490: I/AdapterCursorGrid(20724): bindView()
03-08 14:46:49.501: I/AdapterCursorGrid(20724): --------------------------
03-08 14:46:49.501: I/AdapterCursorGrid(20724): bindView() Record Id: 1
03-08 14:46:49.501: I/AdapterCursorGrid(20724): bindView() Cursor Position: 0
03-08 14:46:49.501: I/AdapterCursorGrid(20724): bindView() View Type: 0
03-08 14:46:49.501: I/AdapterCursorGrid(20724): --------------------------
03-08 14:46:49.521: D/AdapterCursorGrid(20724): bindView() Avatar empty...
03-08 14:46:49.521: D/AdapterCursorGrid(20724): bindView() Picture creation...
03-08 14:46:50.320: I/AdapterCursorGrid(20724): newView()
03-08 14:46:51.170: I/AdapterCursorGrid(20724): bindView()
03-08 14:46:51.180: I/AdapterCursorGrid(20724): --------------------------
03-08 14:46:51.180: I/AdapterCursorGrid(20724): bindView() Record Id: 1
03-08 14:46:51.180: I/AdapterCursorGrid(20724): bindView() Cursor Position: 0
03-08 14:46:51.190: I/AdapterCursorGrid(20724): bindView() View Type: 0
03-08 14:46:51.190: I/AdapterCursorGrid(20724): --------------------------
03-08 14:46:51.190: D/AdapterCursorGrid(20724): bindView() Avatar empty...
03-08 14:46:51.200: D/AdapterCursorGrid(20724): bindView() Picture creation...
03-08 14:46:51.870: I/AdapterCursorGrid(20724): bindView()
03-08 14:46:51.896: I/AdapterCursorGrid(20724): --------------------------
03-08 14:46:51.896: I/AdapterCursorGrid(20724): bindView() Record Id: 1
03-08 14:46:51.900: I/AdapterCursorGrid(20724): bindView() Cursor Position: 0
03-08 14:46:51.900: I/AdapterCursorGrid(20724): bindView() View Type: 0
03-08 14:46:51.900: I/AdapterCursorGrid(20724): --------------------------
03-08 14:46:51.900: D/AdapterCursorGrid(20724): bindView() Avatar empty...
03-08 14:46:51.900: D/AdapterCursorGrid(20724): bindView() Picture creation...

The "Avatar empty..." and "Picture creation..." is simply debug that tells me it is processing and updating 2 particular ImageViews.

Why o why is bindView running so many times? What are the reasons for this and what can I do to resolve this?

Logically speaking I expect bindView to run once (and once each time the adapter is updated), am I wrong in thinking this?

HGPB
  • 4,346
  • 8
  • 50
  • 86
  • Whatever "Picture creation" is should *not* be on the main application thread. `bindView()` needs to return in less than 1ms on a `GridView`, regardless of whether you feel that it is being called too many times for other reasons. **Spending ~600ms in "Picture creation" is very bad**. Even if `bindView()` were called only once per cell, it would be called MxN times (M rows, N columns) when the `GridView` is initially populated, meaning that *your UI is frozen for several seconds* at the outset. – CommonsWare Mar 08 '13 at 15:42
  • @CommonsWare Yes, i'll achieve this with an aync task. – HGPB Mar 08 '13 at 16:12

3 Answers3

15

The operating system may call bindView multiple times so that it can measure and lay out the list correctly. This is not a bug so much as the way it has to be. This, along with smoothness of scrolling, is why bindView implementations need to be as efficient as possible. There are some nice tips and tricks you can use detailed on the Android Developers Page.

CaseyB
  • 24,780
  • 14
  • 77
  • 112
  • 1
    Shall I assume this is relevant for a `gridView`? – HGPB Mar 08 '13 at 16:13
  • Yes. Android may call this as many times as it takes to measure and layout the container properly. – CaseyB Mar 08 '13 at 16:14
  • I currently implement the ViewHolder pattern. I will update the image processing in an async task and then I will review `bindView` again. The troubling part is the idea that my async task will be kicked off 4 times in its current state. Any other suggestions as to why it is being called 4 times? The `GridView` is set to fill_parent for both height and width. Scratching head. – HGPB Mar 08 '13 at 17:22
  • The bottom line is that it doesn't really matter why it's being called any number of times. That is not something that you can change as it's built into the operating system. All you can do is make it as smooth as possible. This means using a `ViewHolder`, using an `AsyncTask` to load images off of the UI thread and caching those images so you don't need to hit the network to load the same image over and over. – CaseyB Mar 08 '13 at 17:57
  • I agree! This is what I have now achieved. Thank you. – HGPB Mar 08 '13 at 20:45
12

I also found that bindView was being called many more times than expected. My ListView height was set to wrap_content. After changing it to match_parent the number of calls reduced drastically.

Credit for this solution goes to the answer for this question Custom CursorAdapater's bindView called 77 times...have I done something wrong?

Community
  • 1
  • 1
aaronmarino
  • 3,723
  • 1
  • 23
  • 36
  • why hasn't this answer been upvoted yet? This is an awesome solution. – faizal Sep 12 '14 at 12:33
  • Well it's not technically a solution to the question asked. I just wanted to highlight that if you're seeing bindView being called an inordinate amount of times, it may be because height is being mistakenly set to wrap_content. – aaronmarino Jul 22 '15 at 14:34
  • Thanks, you saved my day!! This answer should be the accepted answer :) – Riccardo Leschiutta Jul 28 '17 at 13:13
0

One thing I discovered with ImageView is that changing the image will cause the ImageView to request a layout unless the new and old image are exactly the same size.

Internally the ImageView uses getIntrinsicWidth() and getIntrinsicHeight() to work out whether or not to request a layout.

Try something like this (although it would make more sense to send the current width/height to your async load and resize the bitmap before returning from the background):

public void replaceBitmap(ImageView iv, Bitmap bitmap) {
    Drawable current = iv.getDrawable();
    if (bitmap.getWidth() != current.getIntrinsicHeight()
            || bitmap.getHeight() != current.getIntrinsicHeight()) {
        bitmap = Bitmap.createScaledBitmap(bitmap,
            current.getIntrinsicWidth(), current.getIntrinsicHeight(), true);
    }
    iv.setImageBitmap(bitmap);
}
Dave Cole
  • 558
  • 4
  • 14