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数据

相关推荐
千里马学框架5 天前
一起学 Android 14:ShellTransition 屏幕旋转过程深度剖析
android·智能手机·性能优化·framework·性能·屏幕旋转·rotation
美狐美颜SDK开放平台5 天前
开发直播APP时如何接入视频美颜SDK?开发流程与注意事项
android·人工智能·计算机视觉·音视频·直播美颜sdk
AFinalStone5 天前
Android7 SystemUI源码解析(七)Keyguard锁屏模块深度解析
android·systemui
致远ccc5 天前
Google Play 上架前如何测试 App?多国家 Android 环境测试
android·app测试·googleplay·多国家应用测试
ttyyttemo5 天前
Kotlin 协程中的 Job 结构化并发与取消
android
sun0077005 天前
tbox 4g/5g切换,导致wan ip 改变,导致车机旧网络不可用。需要重启车机才行
android
其实防守也摸鱼5 天前
内网穿透与反向代理:原理、工具与实战指南
android·大数据·运维·安全·网络安全·自动化·渗透
liangshanbo12155 天前
React 性能优化实战
性能优化·react
AFinalStone5 天前
Android7 SystemUI 源码解析(四)NavigationBar 导航栏与 SystemBars
android·systemui
JMchen5 天前
属性动画原理与高级动画实现
android·kotlin·canvas