View not displayed because it is too large to fit into a software layer (or drawing cache), needs 28

描述

在自定义view中,我设置了自定义view的宽度大小如下:

<myViewandroid:layout_width="match_parent"android:layout_height="5000px"android:visibility="visible" />

结果view不能绘制,直接不会回调onDraw方法,提示如下:
View not displayed because it is too large to fit into a software layer (or drawing cache), needs 28800000 bytes, only 14745600 available
意思就是绘制的图层大小超过了最大可用值

原因分析

我的虚拟机的分辨率为:1440x2560。software layer最大值可用通过以下获取到,:

 long size=ViewConfiguration.get(getContext()).getScaledMaximumDrawingCacheSize()

返回得到:14745600。通过查看源码发现,这个计算方法为:

mMaximumDrawingCacheSize = 4 * maxWindowBounds.width() * maxWindowBounds.height();

而且我设置的大小是1440x5000,可以计算出:

28800000 =4 * 1440 *5000

明显得到了提示的数据。后面发现随意的View不会出现这个问题,经过排查,居然是设置这个导致的:

 setLayerType(View.LAYER_TYPE_SOFTWARE, null)

当我把这个注释掉就正常了
没法了,下面通过源码排查下出现的原因:
找到限制大小的方法:

  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;}...}

明显可以看出,只要设置的view的大小够大,就一定会进去这个限制判断中,说明开启硬件加速情况下,是不会调用这个方法的,去看下什么时候调用这个方法,查到是到这个调用的:

 @Deprecatedpublic 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);}}}

通过日志打印,发现开启硬件加速情况下确实不会回调这个方法。看下这个方法的说明:

/*** 

Forces the drawing cache to be built if the drawing cache is invalid.

**

If you call {@link #buildDrawingCache()} manually without calling* {@link #setDrawingCacheEnabled(boolean) setDrawingCacheEnabled(true)}, you* should cleanup the cache by calling {@link #destroyDrawingCache()} afterwards.

**

Note about auto scaling in compatibility mode: When auto scaling is not enabled,* this method will create a bitmap of the same size as this view. Because this bitmap* will be drawn scaled by the parent ViewGroup, the result on screen might show* scaling artifacts. To avoid such artifacts, you should call this method by setting* the auto scaling to true. Doing so, however, will generate a bitmap of a different* size than the view. This implies that your application must be able to handle this* size.

**

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.

** @see #getDrawingCache()* @see #destroyDrawingCache()** @deprecated The view drawing cache was largely made obsolete with the introduction of* hardware-accelerated rendering in API 11. With hardware-acceleration, intermediate cache* layers are largely unnecessary and can easily result in a net loss in performance due to the* cost of creating and updating the layer. In the rare cases where caching layers are useful,* such as for alpha animations, {@link #setLayerType(int, Paint)} handles this with hardware* rendering. For software-rendered snapshots of a small part of the View hierarchy or* individual Views it is recommended to create a {@link Canvas} from either a {@link Bitmap} or* {@link android.graphics.Picture} and call {@link #draw(Canvas)} on the View. However these* software-rendered usages are discouraged and have compatibility issues with hardware-only* rendering features such as {@link android.graphics.Bitmap.Config#HARDWARE Config.HARDWARE}* bitmaps, real-time shadows, and outline clipping. For screenshots of the UI for feedback* reports or unit testing the {@link PixelCopy} API is recommended.*/

重点这句话:
With hardware-acceleration, intermediate cache * layers are largely unnecessary and can easily result in a net loss in performance due to the * cost of creating and updating the layer
由于硬件加速下,中间缓存层是不必要的,反而导致一定的开销。软件加速情况下,需要缓存来进行优化。

结论

自定义绘制view时,如果开启软件加速,会开启缓存,缓存层大小如果超了最大值会抛出异常,导致无法绘制view。缓存bitmap大小计算为:

projectedBitmapSize = width * height * (opaque && !use32BitCache ? 2 : 4)

而最大值计算大小为:

mMaximumDrawingCacheSize = 4 * maxWindowBounds.width() * maxWindowBounds.height()

明显与view大小、窗体大小有关。一般来说,view大小的乘积不大于屏幕分辨率大小的乘积是安全的,注意这里说的是一般来说。当开启硬件加速时,使用的是硬件缓存(HardwareLayer),就没有这个限制,而默认情况下NONE,不使用缓存,也是没有这个限制的,记得下开启软件加速,就会比较危险了。要记得有两种缓存软件缓存(bitmap)、硬件缓存(HardwareLayer),but,那就引出了另外一个问题,软件加速下缓存层的机制是怎么样的?

参考下这个文章:[Android]DrawingCache到底干了什么?
简单来说就是:通过drawingCache获取到view的视图,比如获取view的缩略图进行显示。通过查看源码,还发现了在view里面一个很有意思的方法:

/*** Manually render this view (and all of its children) to the given Canvas.* The view must have already done a full layout before this function is* called.  When implementing a view, implement* {@link #onDraw(android.graphics.Canvas)} instead of overriding this method.* If you do need to override this method, call the superclass version.** @param canvas The Canvas to which the View is rendered.*/@CallSuperpublic void draw(Canvas canvas) {

它可以把view及其内容绘制到指定canvas中,那不使用缓存也可以快速来绘制出view的视图。

转载注明本文来源:https://blog.csdn.net/u014614038/article/details/117533941


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部