转自 https://mp.weixin.qq.com/s/XSfPs2hX2P7FPqRUyematA
论坛上很多小伙伴关心为什么闲鱼选择了 Flutter 而不选择其他跨端方案?站在质量的角度,高性能是一个很重的因素,我们使用 Flutter 重写了宝贝详情页之后,对比了 Flutter 和 Native 详情页的性能表现,结论是中高端机型上 Flutter 和 Native 不相上下,在低端机型上,Flutter 会比 Native 更加的流畅,其实闲鱼团队在使用 Flutter 做详情页过程中,没有更多地关注性能优化,为了更快地上线,也是优先功能的实现,不过测试结果出来之后,却出乎意料地优于原先的 Native 的实现 (具体的测试结果,属于敏感数据,要走披露流程,伤不起…)
但是这样很显然不能敷衍过去,仔细想了想,确实 Flutter 的定位并不是要替代 Native,他只想做一个极致的跨端解决方案,所以还是要回到跨端解决方案的赛道,给您从性能角度比一比,谁才是更好的跨端开发方案?
Flutter is Google’s mobile app SDK for crafting high-quality native interfaces on iOS and Android in record time. Flutter works with existing code, is used by developers and organizations around the world, and is free and open source.
We're working on a large-scale rearchitecture of React Native to make it more flexible and integrate better with native infrastructure in hybrid JavaScript/native apps.
怎么比较确实伤脑筋,自己也写了一个 Flutter 和 一个 RN 的 App,但是实在太丑陋,担心大家关注点都到我的烂代码上了,所以在 Github 上找到了一个跨端开发高手 Car Guo,用 Flutter 和 RN 分别实现的一个实际可用的 App,Car Guo 谦虚表示其实也写的比较粗糙,但是在我看来这个是具备真实使用场景的 App(Github 客户端 App,提供丰富的功能,旨在更好的日常管理和维护个人 Github),还是有代表性的
[Flutter] https://github.com/CarGuo/GSYGithubAppFlutter
[REACT NATIVE] https://github.com/CarGuo/GSYGithubApp
1、默认登录成功
2、“动态” 页,点击搜索按钮,搜索关键字 “Java”,正常速度浏览 3 页,等第 4 页加载完成后回退
3、点击 “趋势” 页 Tab,浏览 Feeds 到页面底部,点击最底部的 Item,进入 Item 后,浏览详情 + 浏览 3 页的动态后回退,到 “我的” Tab 页
4、查看 “我的” Feeds 到底部,点击右上角搜索按钮,搜索关键字 “C”,浏览 3 页后,等第 4 页加载完成后场景结束
1、Flutter 在低端和中端的 iOS 机型上,FPS 的表现都优于 RN
2、CPU 的使用上 Flutter 在低端机上表现略差于 RN,中端机型略优于 RN
3、值得注意的是内存上的表现 (上图红色箭头区域),Flutter 在低端机型上的起始内存和 RN 几乎一致,在中端机型上会多 30M 左右的内存 (分析为 Dart VM 的内存),可以想到这应该是 Flutter 针对低端和中端机型上内存策略是不一样的,可用内存少的机型,Dart VM 的初始内存少,运行时进行分配 (这样也可以理解为什么在低端机上带来了更多的 CPU 损耗),中端机器上预分配了更多的 VM 内存,这样在处理时会更加的游刃有余,减少 CPU 的介入,带来更流畅的体验.
可以看出,Flutter 团队在针对不同机型上处理更加的细腻,目的就是为了带来稳定流畅的体验。
1、Flutter 在高低端机的 CPU 上的表现都优于 RN,尤其在低端的小米 2s 上有着更优的表现
2、Android 端在原来 FPS 基础上增加了流畅度的指标,FPS 和流畅度的表现 Flutter 优于 RN(计算规则见附参考文章)
3、Android 端的内存也是值得关注的一点,在小米 2s 上起始内存 Flutter 明显比 RN 多 40M,RN 在测试过程中内存飞涨,Flutter 相比之下会更稳定,内存上 RN 侧的代码是需要调优的,同一套代码 Flutter 在 Android 和 iOS 上并没有很大的差异,但是 RN 的却要在单端调优,Flutter 在这项比拼上又更胜一筹。
比较奇怪的是三星 S8 上 Flutter 和 RN 的初始内存是一致的,猜测是 RN 也 Android 高端机型上也会预分配一些内存,具体细节还需要更进一步的研究。
看了之前的数据,做为裁判的我会把金牌颁给 Flutter,在测试过程中的体验和数据上来看 Flutter 都优于 RN,并且开发这个 App 的是一位 Android 的开发同学,Flutter 和 RN 对于他来说都是全新的技术栈,Car Guo 同学更倾向性地让大家得到一致性的使用体验,性能方面并没有投入太多的时间进行调优,由此看出 Flutter 在跨端开发上在同样投入的情况下,可以获得更佳的性能,更好的用户体验。
拿到了这些数据,也感受到 Flutter 带来福利,那 Flutter 为什么可以做到这么流畅呢?Flutter 是如何优化了渲染,Dart VM 的 Runtime 是怎么玩的?请大家继续关注后续解密文章,感兴趣的同学欢迎加入闲鱼,成为跨端解决方案的领军者。
Android 内存获取方式:
dumpsys meminfo packageName
Android CPU 通过 busybox 执行 top 命令获取
iOS CPU 获取方式:累计每个线程中的 CPU 利用率
for (j = 0; j < thread_count; j++)
{
ATCPUDO *cpuDO = [[ATCPUDO alloc] init];
char name[256];
pthread_t pt = pthread_from_mach_thread_np(thread_list[j]);
if (pt) {
name[0] = '\0';
__unused int rc = pthread_getname_np(pt, name, sizeof name);
cpuDO.threadid = thread_list[j];
cpuDO.identify = [NSString stringWithFormat:@"%s",name];
}
thread_info_count = THREAD_INFO_MAX;
kr = thread_info(thread_list[j], THREAD_BASIC_INFO,(thread_info_t)thinfo, &thread_info_count);
if (kr != KERN_SUCCESS) {
return nil;
}
basic_info_th = (thread_basic_info_t)thinfo;
if (!(basic_info_th->flags & TH_FLAGS_IDLE)) {
tot_sec = tot_sec + basic_info_th->user_time.seconds + basic_info_th->system_time.seconds;
tot_usec = tot_usec + basic_info_th->system_time.microseconds + basic_info_th->system_time.microseconds;
tot_cpu = tot_cpu + basic_info_th->cpu_usage / (float)TH_USAGE_SCALE * 100.0;
cpuDO.usage = basic_info_th->cpu_usage / (float)TH_USAGE_SCALE * 100.0;
if (container) {
[container addObject:cpuDO];
}
}
}
if ([[UIDevice currentDevice].systemVersion intValue] < 10) {
kern_return_t kr;
mach_msg_type_number_t info_count;
task_vm_info_data_t vm_info;
info_count = TASK_VM_INFO_COUNT;
kr = task_info(mach_task_self(), TASK_VM_INFO_PURGEABLE, (task_info_t)&vm_info,&info_count);
if (kr == KERN_SUCCESS) {
return (vm_size_t)(vm_info.internal + vm_info.compressed - vm_info.purgeable_volatile_pmap);
}
return 0;
}
task_vm_info_data_t vmInfo;
mach_msg_type_number_t count = TASK_VM_INFO_COUNT;
kern_return_t result = task_info(mach_task_self(), TASK_VM_INFO, (task_info_t) &vmInfo, &count);
if (result != KERN_SUCCESS)
return 0;
return (vm_size_t)vmInfo.phys_footprint;