一张图引发的App crash

前言

        UE提供的图片资源,一般除了做下压缩外,都是直接使用的。不过有一次UE提供了一张1*1尺寸的小图片后,却引发了app一个UI crash。下面想分享下该问题的原因及解决方案。

问题描述

java.lang.llegalArgumentException: Dimensions must be positive! provided (0, 0)

系统要求的尺寸是正的,但提供系统的尺寸却是0,即尺寸是0导致了问题的出现。这需要结合堆栈信息进行分析、定位。

原因定位

        堆栈信息如下。这里只展示了Android系统API的堆栈,业务代码调用的地方被省略了,不过这些信息已经足够定位原因。

java.lang.llegalArgumentException: Dimensions must be positive! provided (0, 0)
at android.graphics.ImageDecoder.setTargetSize(lmageDecoder.java:1033)
at android.graphics.lmageDecoder.computeDensity(lmageDecoder.java:1823)
at android.graphics.ImageDecoder.decodeDrawablelmpl(lmageDecoder. java:1670)
at android.graphics.ImageDecoder.decodeDrawable(lmageDecoder.java:1645)
at android.content.res.Resourceslmpl.decodelmageDrawable(Resourceslmpl.java:766)
at android.content.res.Resourceslmpl.loadDrawableForCookie(Resourceslmpl.java:839)
at android.content.res.Resourceslmpl.loadDrawable(Resourceslmpl.java:631)
at android.content.res.Resources.loadDrawable(Resources.java:897)
at android.content.res.TypedArray.getDrawableForDensity(TypedArray.java:955)
at android.content.res.TypedArray.getDrawable(TypedArray.java:930)
at android.widget.ImageView.(lmageView.java:189)at android.widget.ImageView.(lmageView.java:172)
......
复制代码

由堆栈信息的第2行可见,最终引发问题的是ImageDecoder类下的setTargetSize方法。这里打开Android的源码看看setTargetSize方法的具体实现,如下。

public void setTargetSize(@Px @IntRange(from = 1) int width,                           
                          @Px @IntRange(from = 1) int height) {    
       if (width <= 0 || height <= 0) {      
             throw new IllegalArgumentException("Dimensions must be positive! " + "provided (" + width + ", " + height + ")");                
       }                            
         mDesiredWidth = width;                
         mDesiredHeight = height;
}
复制代码

可见,当width=0或height=0,setTargetSize方法会抛出异常。那么为什么width、height会等于0呢?根据堆栈信息继续往下看。computeDensity方法的源码实现如下。

private int computeDensity(@NonNull Source src) {            
        if (this.requestedResize()) {                      
            return Bitmap.DENSITY_NONE;            
        }                    
        
        final int srcDensity = src.getDensity();            
        if (srcDensity == Bitmap.DENSITY_NONE) {                      
            return srcDensity;            
        }                      

        if (mIsNinePatch && mPostProcessor == null) {                       
            return srcDensity;             
        }                      

        Resources res = src.getResources();             
        if (res != null && res.getDisplayMetrics().noncompatDensityDpi == srcDensity) {             return srcDensity;             
        }                      

        final int dstDensity = src.computeDstDensity();             
        if (srcDensity == dstDensity) {                       
            return srcDensity;             
        }                      

        if (srcDensity < dstDensity && sApiLevel >= Build.VERSION_CODES.P) {                    
           return srcDensity;             
        }                      

        float scale = (float) dstDensity / srcDensity;             
        int scaledWidth = (int) (mWidth * scale + 0.5f);             
        int scaledHeight = (int) (mHeight * scale + 0.5f);             
        this.setTargetSize(scaledWidth, scaledHeight);             
        return dstDensity;
}
复制代码

setTargetSize的width、height就是通过computeDensity方法传入。mWidth、mHeight是图片的尺寸,两个都为1。

即宽、高的计算公式都为(int)(1 * scale + 0.5f)。到这里会发现,导致宽、高为0的罪魁祸首是scale。        

scale的计算公式为(float)dstDensity/srcDensity。根据系统API对dstDensity、srcDensity的描述可知,dstDensity为屏幕像素密度,srcDensity为APP传入的密度参数、取决于放入了哪一个图片文件。

image.png 因为该图片放进了xxhdpi文件夹中,即srcDensity为480。        

dstDensity这个值就需要根据手机系统的具体情况来分析。        

扫描二维码关注公众号,回复: 13851293 查看本文章

假如一台手机的分辨率为19201080,4.5英寸。dstDensity的值就为(19201920 + 1080*1080)开根号、再除以4.5,约等于489.5。此时scale=489.5 / 480 = 1,计算得到的宽、高也会大于0,这种情况是不会抛出异常的。        

但假如是在宽度较大的平板,尺寸是13.5,那么得到的dstDensity约等于163。在这种情况下,根据宽、高的计算公式:(int)(1 * (163 / 480) + 0.5f) = 0。这时候系统就会抛出异常,导致APP crash。

解决方式

原因定位后,有两种解决方案:

  • 方案1:找UE换一张尺寸不低于3*3的图片。
  • 方案2:删除该1*1的图片。

和UE沟通后,该图片下个版本不会再使用,删除不会产生影响,于是采用了方案2。问题解决。

欢迎关注公众号度熊君,一起分享交流。

猜你喜欢

转载自juejin.im/post/7087930757796069413