视图数量有限吗?

时间:2013-03-06 05:59:50

标签: android performance android-layout android-view

我想弄清楚Android布局和视图的主要有效性问题。我现在正在做研究,但也许有人已经有了答案。

我有一个动态填充了视图的RelativeLayout。基本上,应用程序以XML格式加载论坛帖子,然后呈现讨论树,以便每条消息都显示在自己的视图组中。我决定不使用WebView,因为我想实现一些客户端功能,这些功能在自定义视图上比在HTML页面上更容易。

然而,这种方法存在一个重大缺陷:它很重。

第一个问题(我已经解决了)是nesting of views。现在这不是问题,我的布局几乎是平的,所以最大深度是10-12(从最顶级的PhoneWindow $ DecorView算起,实际深度取决于数据)。

现在我已达到下一个限制,该限制以某种方式与视图的资源消耗相关联(或由其引起)。加载数据后,应用程序挂起一段时间来构建布局(创建视图并用数据填充它们),挂起时间似乎与数据大小成线性关系;如果数据足够大,视图将永远不会出现(最终系统建议关闭应用程序,因为它没有响应)。

现在问题:

  1. 内存消耗是否显着依赖于视图类?换句话说,ButtonTextViewImageView之间是否存在重大差异?我可以将点击处理程序附加到任何视图,以便它们在使用方面没有多大差别。

  2. 背景图片会影响性能吗?如果在N视图中设置了相同的图片,是否会使布局N更重? (我知道这个问题可能看起来很愚蠢但无论如何。)

  3. 9张贴片图片是否比普通贴片图片重?什么是更好的:创建N个视图,其中每个视图都有一些背景图像,或者创建一个宽N倍且背景重复的视图?

  4. 考虑到一些布局,应该首先优化什么:总观看次数,嵌套级别或其他?

  5. 最有趣的。是否可以衡量或至少估计活动及其观点所消耗的资源?如果我做出一些改变,我怎么能看到我走的正确呢?

  6. 更新

    感谢User117,我上面提到的一些问题现已得到解答。我已经使用了层次结构查看器并优化了我的布局:与之前相比,现在整体视图数量减少了两倍,嵌套也减少了。

    然而,该应用程序仍然挂在大型论坛帖子上。

    更新2

    我已将调试器连接到我的设备,发现应用程序内存不足。

    但对我来说非常意外的是,在填充布局之后错误发生。顺序如下:

    1. 我的所有观点都已添加。我可以看到它们被添加时会略微减速。
    2. 几秒钟内几乎没有任何事情发生。在此期间,日志中会生成一些信息消息,它们是相同的:[global] Loaded time zone names for en_US in XXXXms,唯一的区别是毫秒数。
    3. 产生了内存不足错误消息:[dalvikvm-heap] Out of memory on a N-byte allocation(实际大小不同)。长错误报告开始。
    4. 这是什么意思?看起来渲染有其可能相当大的需求。

      更新3

      最后,我发现了核心问题。以下是我的应用程序的屏幕截图,请参阅图像下方的说明。

      A tree-like hierarchy of messages.

      每条消息都包含一个显示或隐藏回复的圆形按钮和按钮右侧的红色内容框。这非常简单,只需要6个视图,包括布局。

      问题是这些连接线的缩进显示哪个消息与哪个消息相关。

      在我目前的实现中,缩进是由小ImageView构建的,每个都包含一个方形图像,显示空白区域,垂直线条或类似T的连接器,或者L角。所有这些视图在包含整个讨论树的大RelativeLayout内彼此对齐。

      这适用于中小型树(最多几百条消息),但是当我尝试加载一棵大树(2K +消息)时,我得到上面更新2中解释的结果。

      显然,我有两个问题。我产生大量消耗内存的视图,这些视图是ImageView,需要更多内存才能渲染,因为它们渲染位图并因此创建图形缓存(根据User117给出的解释)注释)。

      我尝试禁用将图像加载到缩进视图中但没有效果。似乎补充说,大量的观点足以吃掉所有可用的内存。

      我的另一个想法是为包含所有管道和角落的每条消息创建缩进图像,因此每条消息将只有缩进视图而不是10或20.但这更加消耗:我已经消耗了在填充布局的过程中内存不足。 (我将图像缓存在地图中,因此没有创建具有相同图像序列的两个位图,这对我们没有帮助。)

      所以我得出的结论是,我走到了尽头。是否有可能一次性绘制所有这些线?

1 个答案:

答案 0 :(得分:8)

  1. 不同的View是不同的种对象。有些只有draw()轻量级的东西,有些可以容纳大型Bitmap对象,还有处理器对象等等。所以,是的,不同的View会消耗不同的RAM。

  2. 如果视图之间共享相同的Bitmap对象,则RAM中只有一个Object,每个View都有一个指向该对象的引用变量。但是,当View绘制时不是这样:在屏幕上的n个位置绘制相同的位图n次将消耗n倍的CPU并为每个视图生成n个不同的 bitmap_cache

  3. 9补丁图像的每一边实际上比原始图像大2个像素。它们作为文件并没有太大的不同。当它们被绘制时,两者都可以缩放并且将占用几乎相等的空间。唯一的区别是9-Patch的缩放比例不同。

    1. 当子视图是透明的并且背景将显示时,设置较大的父视图的背景会更好。

    2. 您可以保存小图像并设置平铺背景,以便它可以填充大面积区域。

  4. 首先要优化嵌套,因为所有视图在给定时间可能都不可见,假设在滚动布局中只能看到几个视图。然后,您可以减少使用的总观看次数。从ListView获取提示:鉴于用户一次只能看到一组子数据,它重新循环视图。并节省了大量资源。

  5. SDK为此提供了层次结构查看器工具。它显示了正在运行的布局的完整树结构,也为布局的缓慢部分放置了红色标记。

  6. 一个好的布局是:

    1. 易于测量(避免复杂的加权宽度,高度和对齐)。对于 示例而不是为每个孩子做layout_gravity="left",父母可以gravity="left"

    2. 深度较小,每个重叠视图会在绘制屏幕时添加另一个要合成的图层。每个嵌套的视图结构都要求进行链式布局调用。

    3. 聪明并重新循环视图而不是创建所有视图。

    4. 重复使用其部分:<merge><include>等标记。

    5. <强>更新 this question的答案,显示了android中树视图的许多方法。