在小米上面运行时的void android.view.View.setDrawingCacheBackgroundColor(int)' on a null object reference错误

今天测试时,遇到一个问题:

进入同一个界面获取同一份数据并显示时,三星上面没问题,但是小米会崩,并报错void android.view.View.setDrawingCacheBackgroundColor(int)' on a null object reference。网上找到一些原因:

1) (mViewFlags & WILL_NOT_CACHE_DRAWING) == WILL_NOT_CACHE_DRAWING  这个值为true

2) (mViewFlags & DRAWING_CACHE_ENABLED) == DRAWING_CACHE_ENABLED 为false,buildDrawingCache没执行

3) buildDrawingCache执行失败

这些在源码中都可以看到,在获得缓存数据的时候,跟背景色(drawingCacheBackgroundColor),透明度isOpaque,use32BitCache这些有关系,看是细看这些东西都是表面的,是系统在buildDrawingCache的时候,根据View或都系统设置而来的;有些属性是不能更改的;这样一来当一个固定大小的View在不同的设备上生成的图片就可能有所不同,我同事这边存在的问题就是,设置View的固定大小为1360*768,而我的设备分辨率为1024*600,而源码里可以看到这样代码:

[html]  view plain copy print ?
  1. if (width <= 0 || height <= 0 ||  
  2.                     // Projected bitmap size in bytes  
  3.                    (width * height * (opaque && !use32BitCache ? 2 : 4) >  
  4.                            ViewConfiguration.get(mContext).getScaledMaximumDrawingCacheSize())) {  
  5.                destroyDrawingCache();  
  6.                mCachingFailed = true;  
  7.                return;  
  8.            }  

当我们在buildDrawingCache的时候,系统给了我们默认最大的DrawingCacheSize为屏幕宽*高*4;而我的View的CacheSize大小超过了某些设备默认值,就会导致获得为空;开始想着用反射的方法去改变这些属性,或者设置背景颜色来改变图片质量,这样一来CacheSize大小 就可能会变小,但是这样始终不能达到效果; 


最终解决方案:

查看系统buildDrawingCache方法可以看到:

[java]  view plain copy print ?
  1. public void buildDrawingCache(boolean autoScale) {  
  2.         if ((mPrivateFlags & DRAWING_CACHE_VALID) == 0 || (autoScale ?  
  3.                 mDrawingCache == null : mUnscaledDrawingCache == null)) {  
  4.             mCachingFailed = false;  
  5.   
  6.             if (ViewDebug.TRACE_HIERARCHY) {  
  7.                 ViewDebug.trace(this, ViewDebug.HierarchyTraceType.BUILD_CACHE);  
  8.             }  
  9.   
  10.             int width = mRight - mLeft;  
  11.             int height = mBottom - mTop;  
  12.   
  13.             final AttachInfo attachInfo = mAttachInfo;  
  14.             final boolean scalingRequired = attachInfo != null && attachInfo.mScalingRequired;  
  15.   
  16.             if (autoScale && scalingRequired) {  
  17.                 width = (int) ((width * attachInfo.mApplicationScale) + 0.5f);  
  18.                 height = (int) ((height * attachInfo.mApplicationScale) + 0.5f);  
  19.             }  
  20.   
  21.             final int drawingCacheBackgroundColor = mDrawingCacheBackgroundColor;  
  22.             final boolean opaque = drawingCacheBackgroundColor != 0 || isOpaque();  
  23.             final boolean use32BitCache = attachInfo != null && attachInfo.mUse32BitDrawingCache;  
  24.   
  25.             if (width <= 0 || height <= 0 ||  
  26.                      // Projected bitmap size in bytes  
  27.                     (width * height * (opaque && !use32BitCache ? 2 : 4) >  
  28.                             ViewConfiguration.get(mContext).getScaledMaximumDrawingCacheSize())) {  
  29.                 destroyDrawingCache();  
  30.                 mCachingFailed = true;  
  31.                 return;  
  32.             }  
  33.   
  34.             boolean clear = true;  
  35.             Bitmap bitmap = autoScale ? mDrawingCache : mUnscaledDrawingCache;  
  36.   
  37.             if (bitmap == null || bitmap.getWidth() != width || bitmap.getHeight() != height) {  
  38.                 Bitmap.Config quality;  
  39.                 if (!opaque) {  
  40.                     // Never pick ARGB_4444 because it looks awful  
  41.                     // Keep the DRAWING_CACHE_QUALITY_LOW flag just in case  
  42.                     switch (mViewFlags & DRAWING_CACHE_QUALITY_MASK) {  
  43.                         case DRAWING_CACHE_QUALITY_AUTO:  
  44.                             quality = Bitmap.Config.ARGB_8888;  
  45.                             break;  
  46.                         case DRAWING_CACHE_QUALITY_LOW:  
  47.                             quality = Bitmap.Config.ARGB_8888;  
  48.                             break;  
  49.                         case DRAWING_CACHE_QUALITY_HIGH:  
  50.                             quality = Bitmap.Config.ARGB_8888;  
  51.                             break;  
  52.                         default:  
  53.                             quality = Bitmap.Config.ARGB_8888;  
  54.                             break;  
  55.                     }  
  56.                 } else {  
  57.                     // Optimization for translucent windows  
  58.                     // If the window is translucent, use a 32 bits bitmap to benefit from memcpy()  
  59.                     quality = use32BitCache ? Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565;  
  60.                 }  
  61.   
  62.                 // Try to cleanup memory  
  63.                 if (bitmap != null) bitmap.recycle();  
  64.   
  65.                 try {  
  66.                     bitmap = Bitmap.createBitmap(width, height, quality);  
  67.                     bitmap.setDensity(getResources().getDisplayMetrics().densityDpi);  
  68.                     if (autoScale) {  
  69.                         mDrawingCache = bitmap;  
  70.                     } else {  
  71.                         mUnscaledDrawingCache = bitmap;  
  72.                     }  
  73.                     if (opaque && use32BitCache) bitmap.setHasAlpha(false);  
  74.                 } catch (OutOfMemoryError e) {  
  75.                     // If there is not enough memory to create the bitmap cache, just  
  76.                     // ignore the issue as bitmap caches are not required to draw the  
  77.                     // view hierarchy  
  78.                     if (autoScale) {  
  79.                         mDrawingCache = null;  
  80.                     } else {  
  81.                         mUnscaledDrawingCache = null;  
  82.                     }  
  83.                     mCachingFailed = true;  
  84.                     return;  
  85.                 }  
  86.   
  87.                 clear = drawingCacheBackgroundColor != 0;  
  88.             }  
  89.   
  90.             Canvas canvas;  
  91.             if (attachInfo != null) {  
  92.                 canvas = attachInfo.mCanvas;  
  93.                 if (canvas == null) {  
  94.                     canvas = new Canvas();  
  95.                 }  
  96.                 canvas.setBitmap(bitmap);  
  97.                 // Temporarily clobber the cached Canvas in case one of our children  
  98.                 // is also using a drawing cache. Without this, the children would  
  99.                 // steal the canvas by attaching their own bitmap to it and bad, bad  
  100.                 // thing would happen (invisible views, corrupted drawings, etc.)  
  101.                 attachInfo.mCanvas = null;  
  102.             } else {  
  103.                 // This case should hopefully never or seldom happen  
  104.                 canvas = new Canvas(bitmap);  
  105.             }  
  106.   
  107.             if (clear) {  
  108.                 bitmap.eraseColor(drawingCacheBackgroundColor);  
  109.             }  
  110.   
  111.             computeScroll();  
  112.             final int restoreCount = canvas.save();  
  113.   
  114.             if (autoScale && scalingRequired) {  
  115.                 final float scale = attachInfo.mApplicationScale;  
  116.                 canvas.scale(scale, scale);  
  117.             }  
  118.   
  119.             canvas.translate(-mScrollX, -mScrollY);  
  120.   
  121.             mPrivateFlags |= DRAWN;  
  122.             if (mAttachInfo == null || !mAttachInfo.mHardwareAccelerated ||  
  123.                     mLayerType != LAYER_TYPE_NONE) {  
  124.                 mPrivateFlags |= DRAWING_CACHE_VALID;  
  125.             }  
  126.   
  127.             // Fast path for layouts with no backgrounds  
  128.             if ((mPrivateFlags & SKIP_DRAW) == SKIP_DRAW) {  
  129.                 if (ViewDebug.TRACE_HIERARCHY) {  
  130.                     ViewDebug.trace(this, ViewDebug.HierarchyTraceType.DRAW);  
  131.                 }  
  132.                 mPrivateFlags &= ~DIRTY_MASK;  
  133.                 dispatchDraw(canvas);  
  134.             } else {  
  135.                 draw(canvas);  
  136.             }  
  137.   
  138.             canvas.restoreToCount(restoreCount);  
  139.             canvas.setBitmap(null);  
  140.   
  141.             if (attachInfo != null) {  
  142.                 // Restore the cached Canvas for our siblings  
  143.                 attachInfo.mCanvas = canvas;  
  144.             }  
  145.         }  
  146.     }  
  147.   
  148.     /** 
  149.      * Create a snapshot of the view into a bitmap.  We should probably make 
  150.      * some form of this public, but should think about the API. 
  151.      */  
  152.     Bitmap createSnapshot(Bitmap.Config quality, int backgroundColor, boolean skipChildren) {  
  153.         int width = mRight - mLeft;  
  154.         int height = mBottom - mTop;  
  155.   
  156.         final AttachInfo attachInfo = mAttachInfo;  
  157.         final float scale = attachInfo != null ? attachInfo.mApplicationScale : 1.0f;  
  158.         width = (int) ((width * scale) + 0.5f);  
  159.         height = (int) ((height * scale) + 0.5f);  
  160.   
  161.         Bitmap bitmap = Bitmap.createBitmap(width > 0 ? width : 1, height > 0 ? height : 1, quality);  
  162.         if (bitmap == null) {  
  163.             throw new OutOfMemoryError();  
  164.         }  
  165.   
  166.         Resources resources = getResources();  
  167.         if (resources != null) {  
  168.             bitmap.setDensity(resources.getDisplayMetrics().densityDpi);  
  169.         }  
  170.   
  171.         Canvas canvas;  
  172.         if (attachInfo != null) {  
  173.             canvas = attachInfo.mCanvas;  
  174.             if (canvas == null) {  
  175.                 canvas = new Canvas();  
  176.             }  
  177.             canvas.setBitmap(bitmap);  
  178.             // Temporarily clobber the cached Canvas in case one of our children  
  179.             // is also using a drawing cache. Without this, the children would  
  180.             // steal the canvas by attaching their own bitmap to it and bad, bad  
  181.             // things would happen (invisible views, corrupted drawings, etc.)  
  182.             attachInfo.mCanvas = null;  
  183.         } else {  
  184.             // This case should hopefully never or seldom happen  
  185.             canvas = new Canvas(bitmap);  
  186.         }  
  187.   
  188.         if ((backgroundColor & 0xff000000) != 0) {  
  189.             bitmap.eraseColor(backgroundColor);  
  190.         }  
  191.   
  192.         computeScroll();  
  193.         final int restoreCount = canvas.save();  
  194.         canvas.scale(scale, scale);  
  195.         canvas.translate(-mScrollX, -mScrollY);  
  196.   
  197.         // Temporarily remove the dirty mask  
  198.         int flags = mPrivateFlags;  
  199.         mPrivateFlags &= ~DIRTY_MASK;  
  200.   
  201.         // Fast path for layouts with no backgrounds  
  202.         if ((mPrivateFlags & SKIP_DRAW) == SKIP_DRAW) {  
  203.             dispatchDraw(canvas);  
  204.         } else {  
  205.             draw(canvas);  
  206.         }  
  207.   
  208.         mPrivateFlags = flags;  
  209.   
  210.         canvas.restoreToCount(restoreCount);  
  211.         canvas.setBitmap(null);  
  212.   
  213.         if (attachInfo != null) {  
  214.             // Restore the cached Canvas for our siblings  
  215.             attachInfo.mCanvas = canvas;  
  216.         }  
  217.   
  218.         return bitmap;  
  219.     }  

生成DrawingCache的过程貌似就是利用获得View的Canvas然后画到bitmap上,直接返回对应 的bitmap,这样一来,就是我们用getDrawingCache获得的bitmap;跟我们直接将View画到bitmap貌似区别 不是很大,受启发;如下:

自己生成Bitmap;

[java]  view plain copy print ?
  1. public static Bitmap loadBitmapFromView(View v, boolean isParemt) {  
  2.         if (v == null) {  
  3.             return null;  
  4.         }  
  5.         Bitmap screenshot;  
  6.         screenshot = Bitmap.createBitmap(v.getWidth(), v.getHeight(), HDConstantSet.BITMAP_QUALITY);  
  7.         Canvas c = new Canvas(screenshot);  
  8.         v.draw(c);  
  9.         return screenshot;  
  10.     }  

这样也就将View生成了我们需要的bitmap了,但是有些情况下:比如ViewPager在用getDrawingCache和我自己生成的Bitmap时候,会有区别,ViewPager第一屏是正常的,滑动到第二屏幕的时候,我手动生成的Bitmap不见了,而系统getDrawingCache方法生成 的Bitmap是可见的,郁闷,,,详细看了一下系统buildDrawingCache访求,发现在Canvas绘制Bitmap之后,多了一个步骤:

[java]  view plain copy print ?
  1. computeScroll();  
  2.        final int restoreCount = canvas.save();  
  3.        canvas.scale(scale, scale);  
  4.        canvas.translate(-mScrollX, -mScrollY);  

很明显,系统Canvas,对默认位置进行了移动,即启发:我们在用哥滑动View获得它的Bitmap时候,获得的是整个View的区域(包括隐藏的),如果想得到当前区域,需要重新定位到当前可显示的区域;自己的代码修改:


[java]  view plain copy print ?
  1. public static Bitmap loadBitmapFromView(View v, boolean isParemt) {  
  2.         if (v == null) {  
  3.             return null;  
  4.         }  
  5.         Bitmap screenshot;  
  6.         screenshot = Bitmap.createBitmap(v.getWidth(), v.getHeight(), HDConstantSet.BITMAP_QUALITY);  
  7.         Canvas c = new Canvas(screenshot);  
  8.         c.translate(-v.getScrollX(), -v.getScrollY());  
  9.         v.draw(c);  
  10.         return screenshot;  
  11.     }  

完美解决用自己生成 的Bitmap替换系统的getDrawingCache()方法;

当然系统getDrawingCache()考虑的因素很多,这一些我们也可以自己直接定义,比如透明磁,大小 ,图片质量等;最重要就是

[java]  view plain copy print ?
  1. c.translate(-v.getScrollX(), -v.getScrollY());  
这行一代码需要理解 ;



你可能感兴趣的:(Android,基础)