【问题标题】:How is the number of views limited?观看次数如何限制?
【发布时间】:2013-03-06 05:59:50
【问题描述】:

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

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

但是这种方法有一个重大缺陷:它很重。

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

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

现在的问题:

  1. 内存消耗是否很大程度上取决于视图类?换句话说,ButtonTextViewImageView 之间有什么重大区别吗?我可以将点击处理程序附加到任何视图,因此它们在使用上不会有太大差异。

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

  3. 九块图像是否比普通图像重得多?更好的是:创建N 视图,每个视图都有一些背景图像,或者创建一个宽N 倍且具有重复背景的视图?

  4. 给定一些布局,首先应该优化什么:视图总数、嵌套级别或其他?

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

更新

感谢 User117,我上面提出的一些问题现在得到了解答。我使用了 Hierarchy Viewer 并优化了布局:与之前相比,现在的总视图数量减少了近两倍,嵌套也减少了。

但是应用程序仍然挂在一个大型论坛线程上。

更新 2

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

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

  1. 我的所有观点都已添加。在添加它们时,我可以看到稍微慢了一点。
  2. 几秒钟内几乎没有任何反应。在此期间,日志中生成了一些信息消息,它们是相同的:[global] Loaded time zone names for en_US in XXXXms,唯一的区别是毫秒数。
  3. 产生内存不足错误消息:[dalvikvm-heap] Out of memory on a N-byte allocation(实际大小不同)。冗长的错误报告开始。

这是什么意思?看起来渲染有自己的需求,可能相当大。

更新 3

我终于找到了核心问题。这是我的应用程序的屏幕截图,请参阅图片下方的说明。

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

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

在我当前的实现中,缩进由小的ImageView 构成,每个都包含一个正方形图像,显示空白区域、垂直线、T 形连接器或L 形角。所有这些视图在包含整个讨论树的大型 RelativeLayout 中相互对齐。

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

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

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

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

所以我得出的结论是我陷入了死胡同。有没有可能一次画出所有这些线?

【问题讨论】:

    标签: android performance android-layout android-view


    【解决方案1】:
    1. 不同的View 是不同的对象。有些只有draw() 重量轻的东西,有些可以容纳大的Bitmap Objects,以及handler Objects 等等。所以,是的,不同的View 会消耗不同数量的 RAM。

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

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

      1. 设置较大的父视图的背景,子视图透明时效果更好,背景会透出来。

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

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

    5. SDK 为此提供了Hierarchy Viewer 工具。它显示了正在运行的布局的完整树结构,还为布局的缓慢部分设置了危险信号。

    一个好的布局是:

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

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

    3. 很聪明,会循环使用视图而不是创建所有视图。

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

    更新:this question 的回答,展示了许多在 android 中用于树视图的方法。

    【讨论】:

    • 给定一个简单的功能:一个带有 onClick 操作的图形按钮,我可以采取多种方式:1) 插入带有背景图像的 Button,2) 插入 ImageView,3 ) 插入一个TextView,然后为该对象分配一个单击处理程序,无论使用哪个类。这些方式在性能上有什么区别吗?
    • @AlexanderDunaev 好吧,Button 将使用操作系统默认视觉效果,ImageView 将从您的应用程序资源中加载一些额外的内容,TextView 包含额外的代码测量/渲染文本,以及相同的附加处理程序代码。这些视图管理/重新绘制自身所用时间的个体差异将是可以忽略的。现在,如果您的布局中有大约 一千个,那么总体差异可能会很大。此外,ButtonImageButtonImageViewTextView 等也专为特定目的而设计,并且最适合用于该目的。
    • 我支持@User117 的回答,尤其是第 4 点)。不要在不可见的可能较长(分层)列表中创建视图。相反,通过将视图包含的数据“滚动”到视图中来更改可见视图的内容,就像 ListView 及其相关视图所做的那样。例如。在您的屏幕截图中,应该创建不超过 11 或 12 行,每行包含一个父视图、一个图像视图和一个带有一些自定义绘图(线条)的文本视图l。总而言之,在这种情况下不超过 36 个视图。
    • 我喜欢这个想法,但希望这样实现平滑滚动可能会非常复杂。而且我必须处理许多由布局和 ScrollView 管理的事情。
    • @AlexanderDunaev 没那么复杂,想一个简单的ListView,但有项目(仅扩展),根据节点深度向右缩进。
    猜你喜欢
    • 2013-03-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-27
    • 2015-09-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多