YUA*_*ZHU 10 android listview bitmap recycle android-recyclerview
正如谷歌所说,当位图不在Android 3.0下使用时,我们必须手动调用Bitmap.recycle(),因为内存保留在本机堆中.
因此,我们可以为Bitmap提供引用计数,并在使用ListView时检查是否需要在ImageView的onDetachedFromWindow()中回收位图.这是来自Google的演示项目Bitmapfun(ImageFetcher)的解决方案.
但是当使用RecyclerView时,convertview通常会被分离并附加.onDetachedFromWindow()可能会回收位图,所以当它再次附加到父级时,位图就会被回收.
如何处理这个问题?使用RecyclerView时,在Android 3.0下回收位图的正确方法是什么?
这是Googls的Demo BitmapFun(ImageFetcher)的解决方案 - 扩展ImageView:
@Override
protected void onDetachedFromWindow() {
// This has been detached from Window, so clear the drawable
setImageDrawable(null);
super.onDetachedFromWindow();
}
@Override
public void setImageDrawable(Drawable drawable) {
// Keep hold of previous Drawable
final Drawable previousDrawable = getDrawable();
// Call super to set new Drawable
super.setImageDrawable(drawable);
// Notify new Drawable that it is being displayed
notifyDrawable(drawable, true);
// Notify old Drawable so it is no longer being displayed
notifyDrawable(previousDrawable, false);
}
private static void notifyDrawable(Drawable drawable, final boolean isDisplayed) {
if (drawable instanceof RecyclingBitmapDrawable) {
// The drawable is a CountingBitmapDrawable, so notify it
((RecyclingBitmapDrawable) drawable).setIsDisplayed(isDisplayed);
} else if (drawable instanceof LayerDrawable) {
// The drawable is a LayerDrawable, so recurse on each layer
LayerDrawable layerDrawable = (LayerDrawable) drawable;
for (int i = 0, z = layerDrawable.getNumberOfLayers(); i < z; i++) {
notifyDrawable(layerDrawable.getDrawable(i), isDisplayed);
}
}
}
Run Code Online (Sandbox Code Playgroud)
当从内存缓存中删除LruCache时:
@Override
protected void entryRemoved(boolean evicted, String key,
BitmapDrawable oldValue, BitmapDrawable newValue) {
if (RecyclingBitmapDrawable.class.isInstance(oldValue)) {
// The removed entry is a recycling drawable, so notify it
// that it has been removed from the memory cache
((RecyclingBitmapDrawable) oldValue).setIsCached(false);
Run Code Online (Sandbox Code Playgroud)
RecyclingBitmapDrawable是:
public class RecyclingBitmapDrawable extends BitmapDrawable {
static final String TAG = "CountingBitmapDrawable";
private int mCacheRefCount = 0;
private int mDisplayRefCount = 0;
private boolean mHasBeenDisplayed;
public RecyclingBitmapDrawable(Resources res, Bitmap bitmap) {
super(res, bitmap);
}
/**
* Notify the drawable that the displayed state has changed. Internally a
* count is kept so that the drawable knows when it is no longer being
* displayed.
*
* @param isDisplayed - Whether the drawable is being displayed or not
*/
public void setIsDisplayed(boolean isDisplayed) {
//BEGIN_INCLUDE(set_is_displayed)
synchronized (this) {
if (isDisplayed) {
mDisplayRefCount++;
mHasBeenDisplayed = true;
} else {
mDisplayRefCount--;
}
}
// Check to see if recycle() can be called
checkState();
//END_INCLUDE(set_is_displayed)
}
/**
* Notify the drawable that the cache state has changed. Internally a count
* is kept so that the drawable knows when it is no longer being cached.
*
* @param isCached - Whether the drawable is being cached or not
*/
public void setIsCached(boolean isCached) {
//BEGIN_INCLUDE(set_is_cached)
synchronized (this) {
if (isCached) {
mCacheRefCount++;
} else {
mCacheRefCount--;
}
}
// Check to see if recycle() can be called
checkState();
//END_INCLUDE(set_is_cached)
}
private synchronized void checkState() {
//BEGIN_INCLUDE(check_state)
// If the drawable cache and display ref counts = 0, and this drawable
// has been displayed, then recycle
if (mCacheRefCount <= 0 && mDisplayRefCount <= 0 && mHasBeenDisplayed
&& hasValidBitmap()) {
if (BuildConfig.DEBUG) {
Log.d(TAG, "No longer being used or cached so recycling. "
+ toString());
}
getBitmap().recycle();
}
//END_INCLUDE(check_state)
}
private synchronized boolean hasValidBitmap() {
Bitmap bitmap = getBitmap();
return bitmap != null && !bitmap.isRecycled();
}
Run Code Online (Sandbox Code Playgroud)
}
重要的一点是在ImageView的onDetachedFromWindow:setImageDrawable(null)意味着清晰的drawable,它可以使当前drawble的ref count = 0并回收它!当使用ListView时,此解决方案很有效,因为仅当ListView的根活动被销毁时才会发生onDetachedFromWindow.
但是使用Recyclering是不同的,这个重用ConvertView的机制与ListView不同.ImageView可能经常分离,而且它不是回收Bitmap的正确时间!
您可以使用滑翔来实现这一点。Glide 会高效地加载图像,您可以在需要时请求它清除资源。
onViewRecycled()为此,您必须使用recyclerview 适配器的方法。clear()您将使用这样的 glide 方法清除资源,
override fun onViewRecycled(holder: VHCustom) {
super.onViewRecycled(holder)
Glide.with(ctx).clear(holder.imageView)
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1658 次 |
| 最近记录: |