0

一张图引发的App crash

 2 years ago
source link: http://www.androidchina.net/12490.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

一张图引发的App crash – Android开发中文站

你的位置:Android开发中文站 > 热点资讯 > 一张图引发的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这个值就需要根据手机系统的具体情况来分析。

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


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK