本篇文章已授权微信公众号 guolin_blog(郭霖)独家发布
在针对WebView使用getDrawingCache()进行当前屏幕截图时,发现返回的是NULL,同时在控制台看到logcat提示了一条异常信息,意思就是当前视图大小已经超过了缓存容量。内容如下:
02-01 14:21:33.512 3461-3461/com.example.sample.job W/View: View too large to fit into drawing cache, needs 9338145 bytes, only 3686400 available
Android的截图手段有四五种了,尤其是在5.0以后,google提供了更好了截图方式,在这里:https://github.com/weizongwei5/AndroidScreenShot_SysApi。
getDrawingCache()是其中一种截图手段,使用方便,主要针对应用内截图,用法如下:
view.setDrawingCacheEnabled(true);
view.buildDrawingCache();//这句话可加可不加,因为getDrawingCache()执行的主体就是buildDrawingCache()
Bitmap bitmap = Bitmap.createBitmap(view.getDrawingCache(), 0, 0, view.getMeasuredWidth(), view.getMeasuredHeight() - view.getPaddingBottom());
view.setDrawingCacheEnabled(false);
view.destroyDrawingCache();
return bitmap;
getDrawingCache()自身实际上是执行getDrawingCache(false),buildDrawingCache()自身实际上是执行buildDrawingCache(false),而getDrawingCache(false)操作的实体又是buildDrawingCache(false),即get操作的执行顺序也是先build再get,两个方法最终都会执行buildDrawingCacheImpl(false)来实现drawingcache的写入getDrawingCache()只是多了一步获取drawingcache对象的操作,这里的入参false都指的是自动缩放标记,用以指导是否自动适配当前屏幕的大小的,android默认是false。你也可以调用set方法把它改成true,但是会导致你的页面缓存值更大,下面是我尝试设置为true时得到的反馈信息:
buildDrawingCache(true):
02-02 17:38:11.614 5825-5825/com.example.sample.job W/View: View too large to fit into drawing cache, needs 10022400 bytes, only 3686400 available
buildDrawingCache(false):
02-02 17:44:12.725 10069-10069/com.example.sample.job W/View: View too large to fit into drawing cache, needs 9803520 bytes, only 3686400 available
具体的实现我们可以看下android源码:
public void buildDrawingCache() {
buildDrawingCache(false);
}
public Bitmap getDrawingCache() {
return getDrawingCache(false);
}
public Bitmap getDrawingCache(boolean autoScale) {
if ((mViewFlags & WILL_NOT_CACHE_DRAWING) == WILL_NOT_CACHE_DRAWING) {
return null;
}
if ((mViewFlags & DRAWING_CACHE_ENABLED) == DRAWING_CACHE_ENABLED) {
buildDrawingCache(autoScale);
}
return autoScale ? mDrawingCache : mUnscaledDrawingCache;
}
/* If you call {@link #buildDrawingCache()} manually without calling
* {@link #setDrawingCacheEnabled(boolean) setDrawingCacheEnabled(true)}, you
* should cleanup the cache by calling {@link #destroyDrawingCache()} afterwards.
* You should avoid calling this method when hardware acceleration is enabled. If
* you do not need the drawing cache bitmap, calling this method will increase memory
* usage and cause the view to be rendered in software once, thus negatively impacting
* performance.
*/
public void buildDrawingCache(boolean autoScale) {
if ((mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == 0 || (autoScale ?
mDrawingCache == null : mUnscaledDrawingCache == null)) {
if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
Trace.traceBegin(Trace.TRACE_TAG_VIEW,
"buildDrawingCache/SW Layer for " + getClass().getSimpleName());
}
try {
buildDrawingCacheImpl(autoScale);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
}
}
private void buildDrawingCacheImpl(boolean autoScale) {
mCachingFailed = false;
int width = mRight - mLeft;
int height = mBottom - mTop;
final AttachInfo attachInfo = mAttachInfo;
final boolean scalingRequired = attachInfo != null && attachInfo.mScalingRequired;
if (autoScale && scalingRequired) {
width = (int) ((width * attachInfo.mApplicationScale) + 0.5f);
height = (int) ((height * attachInfo.mApplicationScale) + 0.5f);
}
final int drawingCacheBackgroundColor = mDrawingCacheBackgroundColor;
final boolean opaque = drawingCacheBackgroundColor != 0 || isOpaque();
final boolean use32BitCache = attachInfo != null && attachInfo.mUse32BitDrawingCache;
final long projectedBitmapSize = width * height * (opaque && !use32BitCache ? 2 : 4);
final long drawingCacheSize =
ViewConfiguration.get(mContext).getScaledMaximumDrawingCacheSize();
if (width <= 0 || height <= 0 || projectedBitmapSize > drawingCacheSize) {
if (width > 0 && height > 0) {
Log.w(VIEW_LOG_TAG, getClass().getSimpleName() + " not displayed because it is"
+ " too large to fit into a software layer (or drawing cache), needs "
+ projectedBitmapSize + " bytes, only "
+ drawingCacheSize + " available");
}
destroyDrawingCache();
mCachingFailed = true;
return;
}
..检测drawingCache原有数据操作..
try {
bitmap = Bitmap.createBitmap(mResources.getDisplayMetrics(),
width, height, quality);
bitmap.setDensity(getResources().getDisplayMetrics().densityDpi);
if (autoScale) {
mDrawingCache = bitmap;
} else {
mUnscaledDrawingCache = bitmap;
}
if (opaque && use32BitCache) bitmap.setHasAlpha(false);
} catch (OutOfMemoryError e) {
// If there is not enough memory to create the bitmap cache, just
// ignore the issue as bitmap caches are not required to draw the
// view hierarchy
if (autoScale) {
mDrawingCache = null;
} else {
mUnscaledDrawingCache = null;
}
mCachingFailed = true;
return;
}
..执行Bitmap写入autoScale ? mDrawingCache : mUnscaledDrawingCache操作..
}
/**
* The distance in pixels from the top edge of this view's parent
* to the top edge of this view.
* {@hide}
*/
@ViewDebug.ExportedProperty(category = "layout")
protected int mTop;
/**
* The distance in pixels from the top edge of this view's parent
* to the bottom edge of this view.
* {@hide}
*/
@ViewDebug.ExportedProperty(category = "layout")
protected int mBottom;
/**
* Return the height of your view.
*
* @return The height of your view, in pixels.
*/
@ViewDebug.ExportedProperty(category = "layout")
public final int getHeight() {
return mBottom - mTop;
}
Bitmap bitmap = Bitmap.createBitmap(view.getWidth(), view.getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
if (Build.VERSION.SDK_INT >= 11) {
view.measure(View.MeasureSpec.makeMeasureSpec(view.getWidth(), View.MeasureSpec.EXACTLY), View.MeasureSpec.makeMeasureSpec(view.getHeight(), View.MeasureSpec.EXACTLY));
view.layout((int) view.getX(), (int) view.getY(), (int) view.getX() + view.getMeasuredWidth(), (int) view.getY() + view.getMeasuredHeight());
} else {
view.measure(View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED), View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight());
}
view.draw(canvas);
return bitmap;
by the way,祝大家阖家幸福,春节快乐。