ImageView在setImageResource后发生了什么

源码解析

调用Resources.getDrawableForDensity 获取对应分辨率下的图片,这个时候如果我们在不同的分辨率文件下的图片不一样,在不同的手机分辨率下就会显示不同的图片。

java 复制代码
@Nullable
public Drawable getDrawableForDensity(@DrawableRes int id, int density, @Nullable Theme theme) {
    final TypedValue value = obtainTempTypedValue();
    try {
        final ResourcesImpl impl = mResourcesImpl;
        impl.getValueForDensity(id, density, value, true);
        return loadDrawable(value, id, density, theme);
    } finally {
        releaseTempTypedValue(value);
    }
}

@NonNull
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
Drawable loadDrawable(@NonNull TypedValue value, int id, int density, @Nullable Theme theme)
        throws NotFoundException {
    return mResourcesImpl.loadDrawable(this, value, id, density, theme);
}

如果不是对应类型的Drawable(ColorDrawable),就会调用 loadDrawableForCookie 去加载指定Id的图像文件。

java 复制代码
// ResourcesImpl#loadDrawable

// Next, check preloaded drawables. Preloaded drawables may contain
// unresolved theme attributes.
final Drawable.ConstantState cs;
if (isColorDrawable) {
    cs = sPreloadedColorDrawables.get(key);
} else {
    cs = sPreloadedDrawables[mConfiguration.getLayoutDirection()].get(key);
}

Drawable dr;
boolean needsNewDrawableAfterCache = false;
if (cs != null) {
    dr = cs.newDrawable(wrapper);
} else if (isColorDrawable) {
    dr = new ColorDrawable(value.data);
} else {
    dr = loadDrawableForCookie(wrapper, value, id, density);
}
java 复制代码
 /**
* Loads a drawable from XML or resources stream.
*
* @return Drawable, or null if Drawable cannot be decoded.
*/
@Nullable
private Drawable loadDrawableForCookie(@NonNull Resources wrapper, @NonNull TypedValue value,
        int id, int density) {
    if (value.string == null) {
        throw new NotFoundException("Resource "" + getResourceName(id) + "" ("
                + Integer.toHexString(id) + ") is not a Drawable (color or path): " + value);
    }
    ...

    final Drawable dr;

    LookupStack stack = mLookupStack.get();
    try {
        // Perform a linear search to check if we have already referenced this resource before.
        if (stack.contains(id)) {
            throw new Exception("Recursive reference in drawable");
        }
        stack.push(id);
        try {
            if (file.endsWith(".xml")) {
                // 加载XML图像文件
                final String typeName = getResourceTypeName(id);
                if (typeName != null && typeName.equals("color")) {
                    dr = loadColorOrXmlDrawable(wrapper, value, id, density, file);
                } else {
                    dr = loadXmlDrawable(wrapper, value, id, density, file);
                }
            } else {
                // 加载其他的图像文件,jpg\png
                final InputStream is = mAssets.openNonAsset(
                        value.assetCookie, file, AssetManager.ACCESS_STREAMING);
                final AssetInputStream ais = (AssetInputStream) is;
                dr = decodeImageDrawable(ais, wrapper, value);
            }
        } finally {
            stack.pop();
        }
    } catch (Exception | StackOverflowError e) {
        Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
        final NotFoundException rnf = new NotFoundException(
                "File " + file + " from drawable resource ID #0x" + Integer.toHexString(id));
        rnf.initCause(e);
        throw rnf;
    }
    Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);

    return dr;
}

/**
* Loads a Drawable from an encoded image stream, or null.
*
* This call will handle closing ais.
*/
@Nullable
private Drawable decodeImageDrawable(@NonNull AssetInputStream ais,
        @NonNull Resources wrapper, @NonNull TypedValue value) {
    ImageDecoder.Source src = new ImageDecoder.AssetInputStreamSource(ais,
                        wrapper, value);
    try {
        return ImageDecoder.decodeDrawable(src, (decoder, info, s) -> {
            decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE);
        });
    } catch (IOException ioe) {
        // This is okay. This may be something that ImageDecoder does not
        // support, like SVG.
        return null;
    }
}

在下面这个方法中对非XML格式的图像文件进行加载,会先调用native方法对图像文件进行解码,创建Bitmap对象,将数据加载到native中。

java 复制代码
@WorkerThread
@NonNull
private static Drawable decodeDrawableImpl(@NonNull Source src,
        @Nullable OnHeaderDecodedListener listener) throws IOException {
    try (ImageDecoder decoder = src.createImageDecoder(true /*preferAnimation*/)) {
        decoder.mSource = src;
        decoder.callHeaderDecoded(listener, src);

        try (ImageDecoderSourceTrace unused = new ImageDecoderSourceTrace(decoder)) {
            // ...
            // this call potentially manipulates the decoder so it must be performed prior to
            // decoding the bitmap and after decode set the density on the resulting bitmap
            final int srcDensity = decoder.computeDensity(src);
            
            // 动态图, gif
            if (decoder.mAnimated) {
                // AnimatedImageDrawable calls postProcessAndRelease only if
                // mPostProcessor exists.
                ImageDecoder postProcessPtr = decoder.mPostProcessor == null ? null : decoder;
                decoder.checkState(true);
                Drawable d = new AnimatedImageDrawable(decoder.mNativePtr,
                        postProcessPtr, decoder.mDesiredWidth,
                        decoder.mDesiredHeight, decoder.getColorSpacePtr(),
                        decoder.checkForExtended(), srcDensity,
                        src.computeDstDensity(), decoder.mCropRect,
                        decoder.mInputStream, decoder.mAssetFd);
                // d has taken ownership of these objects.
                decoder.mInputStream = null;
                decoder.mAssetFd = null;
                return d;
            }

            // 解码图片数据,加载bitmap
            Bitmap bm = decoder.decodeBitmapInternal();
            bm.setDensity(srcDensity);

            //...
            
            // 默认 BitmapDrawable
            return new BitmapDrawable(res, bm);
        }
    } finally {
        // Close the ImageDecoder#decode trace.
        Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
    }
}

问题

  1. BitmapDrawable中的bitmap 在什么时候会回收native中的数据?

从分析源码的情况,BitmapDrawable 并不会因为引用BD其他View的销毁而会被recycle。所以我们在设置Bitmap结束后,最好还是在整体视图被销毁后,主动调用recycle(),释放native数据

相关推荐
hedalei42 分钟前
android14 硬键盘ESC改BACK按键返回无效问题
android·android14·esc·back按键
hcgeng1 小时前
android 如何判定底部导航栏显示时 不是键盘显示
android·底部导航·导航高度
和煦的春风1 小时前
性能案例分析 | Waiting for GPU completion
android·linux
用户2018792831671 小时前
ConcurrentHashMap:用 “社区超市” 故事讲透并发的设计哲学
android
4Forsee1 小时前
【Android】View 交互的事件处理机制
android·交互
龙腾-虎跃1 小时前
Android Sip电话(PJSP)
android
zhangphil2 小时前
Android点击桌面图库应用启动流程trace分析
android
咖啡の猫2 小时前
Android开发-文本输入
android·gitee
No Silver Bullet3 小时前
Android Studio如何开启离线编译模式
android
vocal5 小时前
【Android 构建优化】R8 : 混淆,压缩与优化
android