App crash caused by a picture

foreword

        The picture resources provided by the UE are generally used directly except for compression. However, once UE provided a small picture of 1*1 size, it caused a UI crash of the app. I would like to share the reasons and solutions for this problem below.

Problem Description

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

The size required by the system is positive, but the size provided by the system is 0, that is, the size of 0 causes the problem. This requires analysis and positioning in combination with stack information.

Cause location

        The stack information is as follows. Only the stack of the Android system API is shown here, and the place where the business code is called is omitted, but this information is enough to locate the cause.

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)
......
复制代码

It can be seen from line 2 of the stack information that the problem is finally caused by the setTargetSize method under the ImageDecoder class. Open the source code of Android here to see the specific implementation of the setTargetSize method, as follows.

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;
}
复制代码

It can be seen that when width=0 or height=0, the setTargetSize method will throw an exception. So why are width and height equal to 0? Continue to look down according to the stack information. The source code implementation of the computeDensity method is as follows.

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;
}
复制代码

The width and height of setTargetSize are passed in through the computeDensity method. mWidth and mHeight are the dimensions of the image, both of which are 1.

That is, the calculation formulas of width and height are (int)(1 * scale + 0.5f). Here you will find that the culprit that causes the width and height to be 0 is scale.        

The calculation formula of scale is (float)dstDensity/srcDensity. According to the description of dstDensity and srcDensity in the system API, dstDensity is the screen pixel density, and srcDensity is the density parameter passed in by the APP, depending on which image file is placed.

image.pngBecause the picture is placed in the xxhdpi folder, that is, the srcDensity is 480.        

The value of dstDensity needs to be analyzed according to the specific situation of the mobile phone system.        

假如一台手机的分辨率为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。问题解决。

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

Guess you like

Origin juejin.im/post/7087930757796069413