View not displayed because it is too large to fit into a software layer (or drawing cache), needs 28
Posted 楠之枫雪
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了View not displayed because it is too large to fit into a software layer (or drawing cache), needs 28相关的知识,希望对你有一定的参考价值。
描述
在自定义view中,我设置了自定义view的宽度大小如下:
<myView
android: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的大小够大,就一定会进去这个限制判断中,说明开启硬件加速情况下,是不会调用这个方法的,去看下什么时候调用这个方法,查到是到这个调用的:
@Deprecated
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);
}
}
}
通过日志打印,发现开启硬件加速情况下确实不会回调这个方法。看下这个方法的说明:
/**
* <p>Forces the drawing cache to be built if the drawing cache is invalid.</p>
*
* <p>If you call {@link #buildDrawingCache()} manually without calling
* {@link #setDrawingCacheEnabled(boolean) setDrawingCacheEnabled(true)}, you
* should cleanup the cache by calling {@link #destroyDrawingCache()} afterwards.</p>
*
* <p>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.</p>
*
* <p>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.</p>
*
* @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.
*/
@CallSuper
public void draw(Canvas canvas) {
它可以把view及其内容绘制到指定canvas中,那不使用缓存也可以快速来绘制出view的视图。
转载注明本文来源:
https://blog.csdn.net/u014614038/article/details/117533941
以上是关于View not displayed because it is too large to fit into a software layer (or drawing cache), needs 28的主要内容,如果未能解决你的问题,请参考以下文章
was not registered for synchronization because synchronization is not active错误
Method not loaded because @ConditionalOnClass
ssh无法登录,提示Pseudo-terminal will not be allocated because stdin is not a terminal.
Coefficients: (1 not defined because of singularities)
Pseudo-terminal will not be allocated because stdin is not a terminal.