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

相关推荐
用户20187928316738 分钟前
Binder 同应用内(本地)通信是否存在 1MB 大小限制?
android
一条上岸小咸鱼1 小时前
Kotlin 基本数据类型(四):String
android·前端·kotlin
侑虎科技2 小时前
游戏AI行为决策——HTN(分层任务网络)
性能优化
Onion_992 小时前
学习下Github上的Android CICD吧
android·github
来来走走2 小时前
Flutter Form组件的基本使用
android·flutter
顾林海3 小时前
Android MMKV 深度解析:原理、实践与源码剖析
android·面试·源码阅读
雨白3 小时前
TCP/IP 核心概念详解:从网络分层到连接管理
android
代码的余温4 小时前
SQL性能优化全攻略
数据库·mysql·性能优化
Wgllss4 小时前
雷电雨效果:Kotlin+Compose+协程+Flow 实现天气UI
android·架构·android jetpack
摘星编程6 小时前
私有化部署全攻略:开源模型本地化改造的性能与安全评测
性能优化·私有化部署·开源模型·安全防护·企业级ai