Android最全UITableView性能优化分析总结,美团ui面试

尾声

开发是需要一定的基础的,我是08年开始进入Android这行的,在这期间经历了Android的鼎盛时期,和所谓的Android”凉了“。中间当然也有着,不可说的心酸,看着身边朋友,同事一个个转前端,换行业,其实当时我的心也有过犹豫,但是我还是坚持下来了,这次的疫情就是一个好的机会,大浪淘沙,优胜劣汰。再等等,说不定下一个黄金浪潮就被你等到了。

  • 330页 PDF Android核心笔记

  • 几十套阿里 、字节跳动、腾讯、华为、美团等公司2020年的面试题

  • PDF和思维脑图,包含知识脉络 + 诸多细节

  • Android进阶系统学习视频

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

问题:cell对多张图片用maskToBounds进行圆角处理的话,帧数下降很快,容易造成卡顿。

原因:一次mask发生了两次离屏渲染和一次主屏渲染。即使忽略昂贵的上下文切换,一次mask需要渲染三次才能在屏幕上显示,这已经是普通视图显示3陪耗时,若再加上下文环境切换,一次mask就是普通渲染的30倍以上耗时操作。问我这个30倍以上这个数据怎么的出来的?当我在cell的UIImageView的实例增加到150个,并去掉圆角的时候,帧数才跌至28帧每秒。虽然不是甚准确,但至少反映mask这个耗时是无mask操作的耗时的数十倍的。

Off-Screen Rendering(离屏渲染)

指的是GPU在当前屏幕缓冲区以外新开辟一个缓冲区进行渲染操作。由上面的一个结论视图和圆角的大小对帧率并没有什么卵影响,数量才是伤害的核心输出啊。可以知道离屏渲染耗时是发生在离屏这个动作上面,而不是渲染。为什么离屏这么耗时?原因主要有创建缓冲区和上下文切换。创建新的缓冲区代价都不算大,付出最大代价的是上下文切换。

上下文切换

上下文切换,不管是在GPU渲染过程中,还是一直所熟悉的进程切换,上下文切换在哪里都是一个相当耗时的操作。首先我要保存当前屏幕渲染环境,然后切换到一个新的绘制环境,申请绘制资源,初始化环境,然后开始一个绘制,绘制完毕后销毁这个绘制环境,如需要切换到On-Screen Rendering或者再开始一个新的离屏渲染重复之前的操作。 下图描述了一次mask的渲染操作。

构思:不使用或尽量少使用maskToBounds进行圆角处理

方案

1、缓存视图渲染内容


self.layer.shouldRasterize = YES;  

self.layer.rasterizationScale = [UIScreen mainScreen].scale;



这样大部分情况下可以马上挽救你的帧数在55帧每秒以上。shouldRasterize = YES可以使视图渲染内容被缓存起来,下次绘制的时候可以直接显示缓存,当然要在视图内容不改变的情况下。

2、预先生成圆角图片

采取预先生成圆角图片,并缓存起来这个方法才是比较好的手段。预处理圆角图片可以在后台处理,处理完毕后缓存起来,再在主线程显示,这就避免了不必要的离屏渲染了。

3、镂空圆形图片覆盖

此方法可以实现圆形头像效果,这个也是极为高效的方法。缺点就是对视图的背景有要求,单色背景效果就最为理想。

4、 Core Graphics API 绘制圆角

在willDisplayCell的方法里面我们把 cell的背景设置成透明,然后自定义 UIView 做为 cell 的 backgroundView,那如果需要选中状态的话,还需要再自定义一个 UIView 做为 cell 的 selectedBackgroundView,通过 Core Graphics API 来实现 UIView 图层圆角的绘制。

实操的时候,这里有几篇文章值得借鉴:

补充:UILabel实现圆角

1、不设置背景色(backgroundColor)类型:只设置borderWidth、borderColor的label,直接设置cornerRadius,不需要设置masksToBounds = YES,就可以实现圆角功能。

2、需要设置背景色(backgroundColor)类型:直接设置cornerRadius是不能正常显示圆角的,原因是:UILabel设置backgroundColor的行为,不再是设定layer的背景色而是为contents设置背景色。所以解决方式是我们不去设置label的backgroundColor,而是直接设置label.layer.backgroundColor,这样就可以实现单独设置cornerRadius,显示圆角的效果。代码:


    UILabel *tagLabel = [UILabel new];

    tagLabel.text = @"减";

    tagLabel.textColor = [UIColor whiteColor];

    tagLabel.font = [UIFont systemFontOfSize:12];

    tagLabel.layer.backgroundColor = [UIColor greenColor].CGColor;

    tagLabel.layer.cornerRadius = 2;



三、复杂层级处理

==========================================================================

问题:subview层级太复杂,给cell在绘制时添加很大的负担

原因:层级太复杂,需要做大量透明处理。

构思:手动绘制视图提升流畅性。将一些相对重复性很大的视图选择使用CoreText和CoreGraphic技术直接绘制在一个视图上,减少视图的层级。

实操细节分析

1、手动绘制方法,不是直接子类化 UITableViewCell,然后覆盖 drawRect: 方法,这样你会得到一个大黑块!因为 cell 中不是只有一个 content view。如果不了解 cell 的层次结构,可以用 Reveal 去看下。

2、绘制 cell 不建议使用 UIView,而是用 CALayer。 UIView 的绘制是建立在 CoreGraphic 上的,使用的是 CPU。CALayer 使用的是 Core Animation,CPU,GPU 通吃,由系统决定使用哪个。View的绘制使用的是自下向上的一层一层的绘制,然后渲染。Layer处理的是 Texure,利用 GPU 的 Texture Cache 和独立的浮点数计算单元加速纹理的处理。

3、GPU不喜欢透明,所以所有的绘图一定要弄成不透明,对于圆角和阴影这些的可以截个伪透明的小图然后绘制上去。在layer的回调里一定也只做绘图,不做计算!

实操借鉴CoreText实现图文混排系列,进一步学习中。

四、图片尺寸处理优化

============================================================================

问题: 图片通过contentMode处理显示,对tableview滚动速度同样会造成影响。

原因:图片变形需要对图片做 transform ,每次压缩图片都要对图片乘以一个变换矩阵,如果显示的图片很多,这个计算量是不同忽视的。

构思方案

1、从网络下载图片后先根据需要显示的图片大小切/压缩成合适大小的图,每次只显示处理过大小的图片,当查看大图时在显示大图。

2、服务器直接返回预处理好的小图和大图以及对应的尺寸最好。

实操

1、图片剪切压缩集成。

2、后台配合。

实操借鉴

1、UIImage剪裁、压缩、拉伸等处理

2、iOS 图片压缩方法

五、图片加载时间段优化

=============================================================================

问题:当TableView快速滚动时,仍然会出现加载跟不上占用系统资源,导致卡顿。

原因: 当TableView的cell中图片很多时,快速滚动页面,使用SDWebImage加载图片虽然是异步过程,但是仍然十分占用系统资源。

构思:快速滚动或滚动时,图片异步加载处理。

方案

1、TableView滚动的时候不去加载未加载过的图片,停止滚动后再从网络加载(做好监听判断处理)。

2、TableView快速滚动的时候(做好监听判断处理)不加载未加载过的图片,当滚动速度小于某个区间值(不管是拖动中还是没有拖动,不管是加速还是减速中)的时候再从网络加载未加载过的图片。

:已经加载过得图片,无论什么时候都加载该图片(因为SDWebImage会将加载过得图片缓存下来,再次加载的时候从缓存中取,这样就不用开辟线程下载图片了)

方案分析

尾声

在我的博客上很多朋友都在给我留言,需要一些系统的面试高频题目。之前说过我的复习范围无非是个人技术博客还有整理的笔记,考虑到笔记是手写版不利于保存,所以打算重新整理并放到网上,时间原因这里先列出面试问题,题解详见:


展示学习笔记

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 12
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要在UITableView的section中添加数据,你需要先创建一个包含所需数据的数组。然后,在UITableViewDataSource协议中实现以下方法: 1. numberOfSections(in tableView: UITableView) -> Int:返回表格中的section数。 2. tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int:返回指定section中的行数。 3. tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell:返回指定indexPath的UITableViewCell实例。 例如,假设你有一个包含多个section的UITableView,每个section都包含一个字符串数组。以下是一个示例代码: ``` class ViewController: UIViewController, UITableViewDataSource { var data: [[String]] = [["item 1", "item 2"], ["item 3", "item 4", "item 5"]] @IBOutlet weak var tableView: UITableView! override func viewDidLoad() { super.viewDidLoad() tableView.dataSource = self } // MARK: - UITableViewDataSource func numberOfSections(in tableView: UITableView) -> Int { return data.count } func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return data[section].count } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) cell.textLabel?.text = data[indexPath.section][indexPath.row] return cell } } ``` 在这个例子中,我们创建了一个包含两个section的UITableView。每个section都有一个字符串数组,我们将其存储在data数组中。在numberOfSections方法中,我们返回data数组的数量,即section的数量。在tableView(_:numberOfRowsInSection:)方法中,我们返回特定section中的行数。最后,在tableView(_:cellForRowAt:)方法中,我们获取特定indexPath的字符串并将其显示在UITableViewCell中。 注意,在上述示例代码中,我们将UITableViewCell标识符设置为“Cell”,你需要确保在Storyboard或xib文件中对应的UITableViewCell的标识符也设置为“Cell”。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值