The answer from @hp.android works well if you are just working with bitmap backgrounds but, in my case, I had a BaseAdapter providing a set of ImageViews for a GridView. I modified the unbindDrawables() method as advised so that the condition is:
if (view instanceof ViewGroup && !(view instanceof AdapterView)) {
  ...
}
but the problem then is that the recursive method never processes the children of the AdapterView. To address this, I instead did the following:
if (view instanceof ViewGroup) {
  ViewGroup viewGroup = (ViewGroup) view;
  for (int i = 0; i < viewGroup.getChildCount(); i++)
    unbindDrawables(viewGroup.getChildAt(i));
  if (!(view instanceof AdapterView))
    viewGroup.removeAllViews();
}
so that the children of the AdapterView are still processed -- the method just doesn't attempt to remove all children (which is unsupported).
This doesn't quite fix the problem however since ImageViews manage a bitmap that is not their background. I therefore added the following. It's not ideal but it works:
if (view instanceof ImageView) {
  ImageView imageView = (ImageView) view;
  imageView.setImageBitmap(null);
}
Overall the unbindDrawables() method is then:
private void unbindDrawables(View view) {
  if (view.getBackground() != null)
    view.getBackground().setCallback(null);
  if (view instanceof ImageView) {
    ImageView imageView = (ImageView) view;
    imageView.setImageBitmap(null);
  } else if (view instanceof ViewGroup) {
    ViewGroup viewGroup = (ViewGroup) view;
    for (int i = 0; i < viewGroup.getChildCount(); i++)
    unbindDrawables(viewGroup.getChildAt(i));
    if (!(view instanceof AdapterView))
      viewGroup.removeAllViews();
  }
}
I'm hoping there is a more principled approach to freeing up such resources.