Android Drawable缓存

本文介绍了Android中通过getResources().getDrawable获取Drawable对象的过程,核心逻辑位于ResourcesImpl.java。Drawable对象可能是Drawable或BitmapDrawable,BitmapDrawable的Bitmap对象会被缓存。若要使两个Drawable互不影响,需调用mutate方法。ResourcesImpl.java通过文件后缀决定返回Drawable或BitmapDrawable,xml后缀会根据标签生成不同Drawable实例。在Activity和View退出时,由于Bitmap是弱引用,不需要手动解绑,可由GC回收。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Android获取Drawable对象方式为getResources().getDrawable(@DrawableResId int res), 只可能返回***Drawable、BitmapDrawable对象, 当然id不存在会抛异常。

public abstract class Drawable {
 ...
 }

常用Drawable
在/framework/base/graphics/java/android/graphics目录下有很多Drawable派生类。
在这里插入图片描述

下面讲解下返回对象是如何产生的, 核心逻辑在ResourcesImpl.java

   private final DrawableCache mDrawableCache = new DrawableCache();  //缓存的是ConstantState抽象类的对象,而不是Drawable对象;  弱引用,不影响GC
    private final DrawableCache mColorDrawableCache = new DrawableCache(); //弱引用,不影响GC
    
    Drawable loadDrawable(@NonNull Resources wrapper, @NonNull TypedValue value, int id,
            int density, @Nullable Resources.Theme theme)
            throws NotFoundException {
                 final boolean useCache = density == 0 || value.density == mMetrics.densityDpi;
                 //getDrawableForDensity(id, 0, theme);density为0, 所以useCache默认为true,  
                 //app取自己的drawable时mPreloading为false, 即下面的if条件为真
                if (!mPreloading && useCache) {
                     final Drawable cachedDrawable = caches.getInstance(key, wrapper, theme);
                     if (cachedDrawable != null) {
                        cachedDrawable.setChangingConfigurations(value.changingConfigurations);
                    return cachedDrawable;
                }
            }

不要被上面的caches.getInstance误导, 它创建了新的实例(newDrawble方法). 但是复用了ConstantState, 如果是BitmapDrawable的话就复用了Bitmap对象。

  public Drawable getInstance(long key, Resources resources, Resources.Theme theme) {
        final Drawable.ConstantState entry = get(key, theme);
        if (entry != null) {
            return entry.newDrawable(resources, theme);
        }
        return null;
    }
    
   public Drawable newDrawable() {
            return new BitmapDrawable(this, null);
        }

做个练习:

BitmapDrawable firstDrawable = (BitmapDrawable) getResources()
                .getDrawable(R.drawable.school_picture);
BitmapDrawable secondDrawable = (BitmapDrawable) getResources()
                .getDrawable(R.drawable.school_picture);

2个drawable实例指向的是同一个Bitmap对象(其实是Bitmap
State), 在操作其中一个drawble的时候, 另一个实例也会受影响。
在这里插入图片描述

如果想2个drawable互不影响, 即使用不同的ConstantState实例。 可以调用BitmapDrawable.java的mutate方法:

    @Override
    public Drawable mutate() {
        if (!mMutated && super.mutate() == this) {
            mBitmapState = new BitmapState(mBitmapState);
            mMutated = true;
        }
        return this;
    }

即使用新的ConstantState实例替换mBitmapState参数值。

ResouresesImpl.java是通过文件后缀判断Drawable类型的, 如果后缀是xml则返回ColorDrawable对象, 否则返回BitmapDrawable对象。

private Drawable loadDrawableForCookie(@NonNull Resources wrapper, @NonNull TypedValue value,
            int id, int density) {
            ...
            if (file.endsWith(".xml")) {
                    final XmlResourceParser rp = loadXmlResourceParser(
                            file, id, value.assetCookie, "drawable");
                    dr = Drawable.createFromXmlForDensity(wrapper, rp, density, null);
                    rp.close();
                } else {
                    final InputStream is = mAssets.openNonAsset(
                            value.assetCookie, file, AssetManager.ACCESS_STREAMING);
                    AssetInputStream ais = (AssetInputStream) is;
                    dr = decodeImageDrawable(ais, wrapper, value);   //返回BitmapDrawable
                }

    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;
        }
    }
    public static Drawable decodeDrawable(@NonNull Source src,
            @Nullable OnHeaderDecodedListener listener) throws IOException {
        Bitmap bitmap = decodeBitmap(src, listener);
        **return new BitmapDrawable(src.getResources(), bitmap);**
    }

后缀是xml时,执行了Drawable.createFromXmlForDensity方法, 最终会执行到DrawableInflator.java的inflateFromTag方法, 返回上面graphics文件夹下面的类。。

    private Drawable inflateFromTag(@NonNull String name) {
        switch (name) {
            case "selector":
                return new StateListDrawable();
            case "animated-selector":
                return new AnimatedStateListDrawable();
            case "level-list":
                return new LevelListDrawable();
            case "layer-list":
                return new LayerDrawable();
            case "transition":
                return new TransitionDrawable();
            case "ripple":
                return new RippleDrawable();
            case "adaptive-icon":
                return new AdaptiveIconDrawable();
            case "color":
                return new ColorDrawable();
            case "shape":
                return new GradientDrawable();
            case "vector":
                return new VectorDrawable();
            case "animated-vector":
                return new AnimatedVectorDrawable();
            case "scale":
                return new ScaleDrawable();
            case "clip":
                return new ClipDrawable();
            case "rotate":
                return new RotateDrawable();
            case "animated-rotate":
                return new AnimatedRotateDrawable();
            case "animation-list":
                return new AnimationDrawable();
            case "inset":
                return new InsetDrawable();
            case "bitmap":
                return new BitmapDrawable();
            case "nine-patch":
                return new NinePatchDrawable();
            case "animated-image":
                return new AnimatedImageDrawable();
            default:
                return null;
        }
    }

小结:
1、 getResources().getDrawable只可能返回Drawable(根据xml里的tag生成不同实例)、BitmapDrawable或者抛异常;
2、 在ResourcesImpl.java里缓存了ConstantState弱引用, 对应BitmapDrawable对象来说缓存了Bitmap引用。
3、通过调用drawable的mutate方法可以创建新的ConstantState对象。
4、ResourcesImpl.java通过文件名后缀返回不同的drawable对象, 后缀是xml时根据文件里的tag名称返回
Drawable对象,其它情况返回BitmapDrawable对象;
5、 在Activity、View退出时不需要解绑Drawable, 即ImageView的setImageDrawable(null)。 因为ResourcesImpl缓存的是Bitmap弱引用,可以被GC回收;

<think>我们正在处理一个Android开发问题:如何对drawable资源进行高斯模糊处理。 根据引用[4],OpenCV提供了高斯模糊的内置函数GaussianBlur,我们可以使用它来处理图像。 同时,引用[5]提到了一种通过操作像素数组来实现模糊的方法,但使用OpenCV可能更为方便和高效。 步骤: 1. 将drawable资源转换为Bitmap。 2. 使用OpenCV的GaussianBlur函数对Bitmap进行模糊处理。 3. 将处理后的Bitmap设置到ImageView上。 注意:需要确保OpenCV库已经正确集成到项目中。 根据引用[3],我们需要在onCreate方法中初始化OpenCV,并在初始化成功后执行图像处理。 另外,引用[2]展示了如何在布局中添加一个ImageView来显示模糊后的图像。 因此,我们可以按照以下步骤实现: 步骤1:添加OpenCV依赖 在app的build.gradle中添加OpenCV的依赖(具体版本根据实际情况选择): ```gradle dependencies { implementation 'com.quickbirdstudios:opencv:4.5.3.0' } ``` 或者通过其他方式集成OpenCV。 步骤2:在布局文件中添加ImageView(如引用[2]所示) ```xml <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <ImageView android:id="@+id/blurred_image" android:layout_width="match_parent" android:layout_height="match_parent" /> </LinearLayout> ``` 步骤3:在MainActivity中初始化OpenCV并处理图像 根据引用[3]和[4],我们可以这样写: 首先,将drawable资源转换为Bitmap: ```java Bitmap originalBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.your_drawable); ``` 然后,将Bitmap转换为Mat(OpenCV的矩阵格式),进行高斯模糊,再转换回Bitmap。 最后,将处理后的Bitmap设置到ImageView上。 具体代码实现如下: ```java import org.opencv.android.OpenCVLoader; import org.opencv.android.Utils; import org.opencv.core.Mat; import org.opencv.core.Size; import org.opencv.imgproc.Imgproc; public class MainActivity extends AppCompatActivity { private ImageView blurredImageView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); blurredImageView = findViewById(R.id.blurred_image); // 初始化OpenCV if (!OpenCVLoader.initDebug()) { // 初始化失败的处理 Log.e("MainActivity", "OpenCV initialization failed"); } else { // 初始化成功,进行图像处理 processImage(); } } private void processImage() { // 步骤1:将drawable资源转换为Bitmap Bitmap originalBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.your_image); // 步骤2:将Bitmap转换为Mat Mat src = new Mat(); Utils.bitmapToMat(originalBitmap, src); // 步骤3:应用高斯模糊 Mat dst = new Mat(); // 参数:输入图像,输出图像,高斯核大小(必须是正奇数),x方向标准差,y方向标准差 Imgproc.GaussianBlur(src, dst, new Size(25, 25), 0); // 步骤4:将Mat转换回Bitmap Bitmap blurredBitmap = Bitmap.createBitmap(dst.cols(), dst.rows(), Bitmap.Config.ARGB_8888); Utils.matToBitmap(dst, blurredBitmap); // 步骤5:显示模糊后的图像 blurredImageView.setImageBitmap(blurredBitmap); } } ``` 注意:高斯核的大小(如25,25)和标准差(这里设为0,OpenCV会根据核大小自动计算)可以根据需要调整。核越大,图像越模糊。 另外,如果不想使用OpenCV,也可以考虑使用RenderScript(但注意RenderScript在API 31后被废弃)或者其他纯Java实现的模糊算法,但OpenCV提供了更专业的图像处理功能。 根据引用[1],我们注意到Android R原生支持窗口模糊,但这里我们处理的是drawable资源,所以不适用。 因此,我们使用OpenCV来实现对drawable的高斯模糊。 相关问题: 1. 如何调整高斯模糊的模糊程度? 2. 除了OpenCV,还有哪些方法可以在Android中实现高斯模糊? 3. 高斯模糊处理大图像时性能如何优化? 4. 如何将高斯模糊处理后的图像保存为文件? 5. 高斯模糊处理时如何避免内存溢出(OOM)?</think>### 在Android中对Drawable进行高斯模糊处理 在Android中实现Drawable的高斯模糊处理,可通过以下步骤完成(基于OpenCV实现): #### 1. **添加OpenCV依赖** 在`build.gradle`中添加最新OpenCV库依赖: ```gradle dependencies { implementation 'com.quickbirdstudios:opencv:4.5.3.0' // 使用最新版本 } ``` #### 2. **初始化OpenCV** 在`MainActivity`中初始化OpenCV: ```java @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); if (!OpenCVLoader.initDebug()) { Log.e("OpenCV", "初始化失败"); } else { processDrawableBlur(); // 初始化成功后执行模糊 } } ``` #### 3. **Drawable模糊处理核心方法** ```java private void processDrawableBlur() { // 获取原始Drawable(示例使用资源ID) Drawable originalDrawable = getResources().getDrawable(R.drawable.your_image); Bitmap originalBitmap = ((BitmapDrawable) originalDrawable).getBitmap(); // 转换为OpenCV的Mat对象 Mat srcMat = new Mat(); Utils.bitmapToMat(originalBitmap, srcMat); // 执行高斯模糊 Mat blurredMat = new Mat(); Imgproc.GaussianBlur( srcMat, blurredMat, new Size(25, 25), // 模糊半径(奇数) 0 // 标准差(0表示自动计算) ); // 转换回Bitmap Bitmap blurredBitmap = Bitmap.createBitmap( blurredMat.cols(), blurredMat.rows(), Bitmap.Config.ARGB_8888 ); Utils.matToBitmap(blurredMat, blurredBitmap); // 显示结果 ImageView imageView = findViewById(R.id.blurred_image); imageView.setImageBitmap(blurredBitmap); } ``` #### 4. **布局文件(res/layout/activity_main.xml)** ```xml <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <ImageView android:id="@+id/blurred_image" android:layout_width="match_parent" android:layout_height="match_parent"/> </LinearLayout> ``` ### 关键参数说明 1. **模糊半径(Size对象)**: - 值越大越模糊(必须为正奇数) - 示例:`new Size(15,15)` 中等模糊,`new Size(45,45)` 重度模糊 2. **标准差(sigmaX/sigmaY)**: - 设为0时,OpenCV会根据核大小自动计算 - 可通过调整此值控制模糊效果分布 ### 性能优化建议 1. **降低分辨率**:对大图先缩放再模糊,最后还原尺寸 2. **子线程处理**:在`AsyncTask`或协程中执行模糊操作 3. **缓存结果**:相同资源只处理一次 4. **RenderScript替代方案**(API>17): ```java RenderScript rs = RenderScript.create(context); ScriptIntrinsicBlur blurScript = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs)); blurScript.setRadius(25f); // 模糊半径 ``` ### 注意事项 - **OpenCV初始化**:确保`initDebug()`成功后再调用图像处理 - **Bitmap转换**:注意ARGB_8888格式保证透明度支持 - **内存管理**:及时回收Mat对象 ```java srcMat.release(); blurredMat.release(); ``` --- ### 相关问题 1. 如何动态调整高斯模糊的模糊程度? 2. 处理大尺寸图片时如何避免内存溢出(OOM)? 3. 除了OpenCV,还有哪些高效的Android模糊方案? 4. 如何实现局部区域模糊效果? 5. 高斯模糊与其他模糊算法(如均值模糊)有何性能差异? [^1]: OpenCV高斯模糊函数原理与Android集成 [^2]: Android图像处理布局实现 [^3]: OpenCV初始化流程 [^4]: 高斯模糊参数详解 [^5]: Drawable与Bitmap转换技术
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值