IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> Flutter的原理及美团的实践(中),移动开发工程师核心竞争力 -> 正文阅读

[移动开发]Flutter的原理及美团的实践(中),移动开发工程师核心竞争力

loadData: (callback) async {

Map<String, dynamic> data = await post(“home/groups”);

if (data == null) {

callback(false);

return;

}

_data = AllCategoryResponse.fromJson(data);

if (_data == null || _data.code != 0) {

callback(false);

return;

}

callback(true);

}),

SO库兼容性

Flutter官方只提供了四种CPU架构的SO库:armeabi-v7a、arm64-v8a、x86和x86-64,其中x86系列只支持Debug模式,但是外卖使用的大量SDK都只提供了armeabi架构的库。

虽然我们可以通过修改引擎src根目录和third_party/dart目录下build/config/arm.gnithird_party/skia目录下的BUILD.gn等配置文件来编译出armeabi版本的Flutter引擎,但是实际上市面上绝大部分设备都已经支持armeabi-v7a,其提供的硬件加速浮点运算指令可以大大提高Flutter的运行速度,在灰度阶段我们可以主动屏蔽掉不支持armeabi-v7a的设备,直接使用armeabi-v7a版本的引擎。

做到这点我们首先需要修改Flutter提供的引擎,在Flutter安装目录下的bin/cache/artifacts/engine下有Flutter下载的所有平台的引擎:

我们只需要修改android-arm、android-arm-profile和android-arm-release下的flutter.jar,将其中的lib/armeabi-v7a/libflutter.so移动到lib/armeabi/libflutter.so即可:

cd $FLUTTER_ROOT/bin/cache/artifacts/engine

for arch in android-arm android-arm-profile android-arm-release; do

pushd $arch

cp flutter.jar flutter-armeabi-v7a.jar # 备份

unzip flutter.jar lib/armeabi-v7a/libflutter.so

mv lib/armeabi-v7a lib/armeabi

zip -d flutter.jar lib/armeabi-v7a/libflutter.so

zip flutter.jar lib/armeabi/libflutter.so

popd

done

这样在打包后Flutter的SO库就会打到APK的lib/armeabi目录中。在运行时如果设备不支持armeabi-v7a可能会崩溃,所以我们需要主动识别并屏蔽掉这类设备,在Android上判断设备是否支持armeabi-v7a也很简单:

public static boolean isARMv7Compatible() {

try {

if (SDK_INT >= LOLLIPOP) {

for (String abi : Build.SUPPORTED_32_BIT_ABIS) {

if (abi.equals(“armeabi-v7a”)) {

return true;

}

}

} else {

if (CPU_ABI.equals(“armeabi-v7a”) || CPU_ABI.equals(“arm64-v8a”)) {

return true;

}

}

} catch (Throwable e) {

L.wtf(e);

}

return false;

}

灰度和自动降级策略

Horn是一个美团内部的跨平台配置下发SDK,使用Horn可以很方便地指定灰度开关:

在条件配置页面定义一系列条件,然后在参数配置页面添加新的字段flutter即可:

因为在客户端做了ABI兜底策略,所以这里定义的ABI规则并没有启用。

Flutter目前仍然处于Beta阶段,灰度过程中难免发生崩溃现象,观察到崩溃后再针对机型或者设备ID来做降级虽然可以尽量降低影响,但是我们可以做到更迅速。外卖的Crash采集SDK同时也支持JNI Crash的收集,我们专门为Flutter注册了崩溃监听器,一旦采集到Flutter相关的JNI Crash就立即停止该设备的Flutter功能,启动Flutter之前会先判断FLUTTER_NATIVE_CRASH_FLAG文件是否存在,如果存在则表示该设备发生过Flutter相关的崩溃,很有可能是不兼容导致的问题,当前版本周期内在该设备上就不再使用Flutter功能。

除了崩溃以外,Flutter页面中的Dart代码也可能发生异常,例如服务器下发数据格式错误导致解析失败等等,Dart也提供了全局的异常捕获功能:

import ‘package:wm_app/plugins/wm_metrics.dart’;

void main() {

runZoned(() => runApp(WaimaiApp()), onError: (Object obj, StackTrace stack) {

uploadException(“KaTeX parse error: Undefined control sequence: \n at position 4: obj\?n?stack”);

});

}

这样我们就可以实现全方位的异常监控和完善的降级策略,最大程度减少灰度时可能对用户带来的影响。

分析崩溃堆栈和异常数据

Flutter的引擎部分全部使用C/C++实现,为了减少包大小,所有的SO库在发布时都会去除符号表信息。和其他的JNI崩溃堆栈一样,我们上报的堆栈信息中只能看到内存地址偏移量等信息:


Build fingerprint: ‘Rock/odin/odin:7.1.1/NMF26F/1527007828:user/dev-keys’

Revision: ‘0’

Author: collect by ‘libunwind’

ABI: ‘arm64-v8a’

pid: 28937, tid: 29314, name: 1.ui >>> com.sankuai.meituan.takeoutnew <<<

signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x0

backtrace:

r0 00000000 r1 ffffffff r2 c0e7cb2c r3 c15affcc

r4 c15aff88 r5 c0e7cb2c r6 c15aff90 r7 bf567800

r8 c0e7cc58 r9 00000000 sl c15aff0c fp 00000001

ip 80000000 sp c0e7cb28 lr c11a03f9 pc c1254088 cpsr 200c0030

#00 pc 002d7088 /data/app/com.sankuai.meituan.takeoutnew-1/lib/arm/libflutter.so

#01 pc 002d5a23 /data/app/com.sankuai.meituan.takeoutnew-1/lib/arm/libflutter.so

#02 pc 002d95b5 /data/app/com.sankuai.meituan.takeoutnew-1/lib/arm/libflutter.so

#03 pc 002d9f33 /data/app/com.sankuai.meituan.takeoutnew-1/lib/arm/libflutter.so

#04 pc 00068e6d /data/app/com.sankuai.meituan.takeoutnew-1/lib/arm/libflutter.so

#05 pc 00067da5 /data/app/com.sankuai.meituan.takeoutnew-1/lib/arm/libflutter.so

#06 pc 00067d5f /data/app/com.sankuai.meituan.takeoutnew-1/lib/arm/libflutter.so

#07 pc 003b1877 /data/app/com.sankuai.meituan.takeoutnew-1/lib/arm/libflutter.so

#08 pc 003b1db5 /data/app/com.sankuai.meituan.takeoutnew-1/lib/arm/libflutter.so

#09 pc 0000241c /data/data/com.sankuai.meituan.takeoutnew/app_flutter/vm_snapshot_instr

单纯这些信息很难定位问题,所以我们需要使用NDK提供的ndk-stack来解析出具体的代码位置:

ndk-stack -sym PATH [-dump PATH]

Symbolizes the stack trace from an Android native crash.

-sym PATH sets the root directory for symbols

-dump PATH sets the file containing the crash dump (default stdin)

如果使用了定制过的引擎,必须使用engine/src/out/android-release下编译出的libflutter.so文件。一般情况下我们使用的是官方版本的引擎,可以在flutter_infra页面直接下载带有符号表的SO文件,根据打包时使用的Flutter工具版本下载对应的文件即可。比如0.4.4 beta版本:

$ flutter --version # version命令可以看到Engine对应的版本 06afdfe54e

Flutter 0.4.4 ? channel beta ? https://github.com/flutter/flutter.git

Framework ? revision f9bb4289e9 (5 weeks ago) ? 2018-05-11 21:44:54 -0700

Engine ? revision 06afdfe54e

Tools ? Dart 2.0.0-dev.54.0.flutter-46ab040e58

$ cat flutter/bin/internal/engine.version # flutter安装目录下的engine.version文件也可以看到完整的版本信息 06afdfe54ebef9168a90ca00a6721c2d36e6aafa

06afdfe54ebef9168a90ca00a6721c2d36e6aafa

拿到引擎版本号后在https://console.cloud.google.com/storage/browser/flutter_infra/flutter/06afdfe54ebef9168a90ca00a6721c2d36e6aafa/?看到该版本对应的所有构建产物,下载android-arm-release、android-arm64-release和android-x86目录下的symbols.zip,并存放到对应目录:

执行ndk-stack即可看到实际发生崩溃的代码和具体行数信息:

ndk-stack -sym flutter-production-syms/06afdfe54ebef9168a90ca00a6721c2d36e6aafa/armeabi-v7a -dump flutter_jni_crash.txt

********** Crash dump: **********

Build fingerprint: ‘Rock/odin/odin:7.1.1/NMF26F/1527007828:user/dev-keys’

pid: 28937, tid: 29314, name: 1.ui >>> com.sankuai.meituan.takeoutnew <<<

signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x0

Stack frame #00 pc 002d7088 /data/app/com.sankuai.meituan.takeoutnew-1/lib/arm/libflutter.so: Routine minikin::WordBreaker::setText(unsigned short const*, unsigned int) at /b/build/slave/Linux_Engine/build/src/out/android_release/…/…/flutter/third_party/txt/src/minikin/WordBreaker.cpp:55

Stack frame #01 pc 002d5a23 /data/app/com.sankuai.meituan.takeoutnew-1/lib/arm/libflutter.so: Routine minikin::LineBreaker::setText() at /b/build/slave/Linux_Engine/build/src/out/android_release/…/…/flutter/third_party/txt/src/minikin/LineBreaker.cpp:74

Stack frame #02 pc 002d95b5 /data/app/com.sankuai.meituan.takeoutnew-1/lib/arm/libflutter.so: Routine txt::Paragraph::ComputeLineBreaks() at /b/build/slave/Linux_Engine/build/src/out/android_release/…/…/flutter/third_party/txt/src/txt/paragraph.cc:273

Stack frame #03 pc 002d9f33 /data/app/com.sankuai.meituan.takeoutnew-1/lib/arm/libflutter.so: Routine txt::Paragraph::Layout(double, bool) at /b/build/slave/Linux_Engine/build/src/out/android_release/…/…/flutter/third_party/txt/src/txt/paragraph.cc:428

Stack frame #04 pc 00068e6d /data/app/com.sankuai.meituan.takeoutnew-1/lib/arm/libflutter.so: Routine blink::ParagraphImplTxt::layout(double) at /b/build/slave/Linux_Engine/build/src/out/android_release/…/…/flutter/lib/ui/text/paragraph_impl_txt.cc:54

Stack frame #05 pc 00067da5 /data/app/com.sankuai.meituan.takeoutnew-1/lib/arm/libflutter.so: Routine tonic::DartDispatcher<tonic::IndicesHolder<0u>, void (blink::Paragraph::)(double)>::Dispatch(void (blink::Paragraph::)(double)) at /b/build/slave/Linux_Engine/build/src/out/android_release/…/…/topaz/lib/tonic/dart_args.h:150

Stack frame #06 pc 00067d5f /data/app/com.sankuai.meituan.takeoutnew-1/lib/arm/libflutter.so: Routine void tonic::DartCall<void (blink::Paragraph::)(double)>(void (blink::Paragraph::)(double), _Dart_NativeArguments*) at /b/build/slave/Linux_Engine/build/src/out/android_release/…/…/topaz/lib/tonic/dart_args.h:198

Stack frame #07 pc 003b1877 /data/app/com.sankuai.meituan.takeoutnew-1/lib/arm/libflutter.so: Routine dart::NativeEntry::AutoScopeNativeCallWrapperNoStackCheck(_Dart_NativeArguments*, void ()(_Dart_NativeArguments)) at /b/build/slave/Linux_Engine/build/src/out/android_release/…/…/third_party/dart/runtime/vm/native_entry.cc:198

Stack frame #08 pc 003b1db5 /data/app/com.sankuai.meituan.takeoutnew-1/lib/arm/libflutter.so: Routine dart::NativeEntry::LinkNativeCall(_Dart_NativeArguments*) at /b/build/slave/Linux_Engine/build/src/out/android_release/…/…/third_party/dart/runtime/vm/native_entry.cc:348

Stack frame #09 pc 0000241c /data/data/com.sankuai.meituan.takeoutnew/app_flutter/vm_snapshot_instr

Dart异常则比较简单,默认情况下Dart代码在编译成机器码时并没有去除符号表信息,所以Dart的异常堆栈本身就可以标识真实发生异常的代码文件和行数信息:

FlutterException: type ‘_InternalLinkedHashMap<dynamic, dynamic>’ is not a subtype of type ‘num’ in type cast

#0 _$CategoryGroupFromJson (package:wm_app/lib/all_category/model/category_model.g.dart:29)

#1 new CategoryGroup.fromJson (package:wm_app/all_category/model/category_model.dart:51)

#2 _$CategoryListDataFromJson. (package:wm_app/lib/all_category/model/category_model.g.dart:5)

#3 MappedListIterable.elementAt (dart:_internal/iterable.dart:414)

#4 ListIterable.toList (dart:_internal/iterable.dart:219)

#5 _$CategoryListDataFromJson (package:wm_app/lib/all_category/model/category_model.g.dart:6)

#6 new CategoryListData.fromJson (package:wm_app/all_category/model/category_model.dart:19)

#7 _$AllCategoryResponseFromJson (package:wm_app/lib/all_category/model/category_model.g.dart:19)

#8 new AllCategoryResponse.fromJson (package:wm_app/all_category/model/category_model.dart:29)

#9 AllCategoryPage.build. (package:wm_app/all_category/category_page.dart:46)

#10 _WaimaiLoadingState.build (package:wm_app/all_category/widgets/progressive_loading_page.dart:51)

#11 StatefulElement.build (package:flutter/src/widgets/framework.dart:3730)

#12 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3642)

#13 Element.rebuild (package:flutter/src/widgets/framework.dart:3495)

#14 BuildOwner.buildScope (package:flutter/src/widgets/framework.dart:2242)

总结

作为一名从事Android的开发者,很多人最近都在和我吐槽Android是不是快要凉了?而在我看来这正是市场成熟的表现,所有的市场都是温水煮青蛙,永远会淘汰掉不愿意学习改变,安于现状的那批人,希望所有的人能在大浪淘沙中留下来,因为对于市场的逐渐成熟,平凡并不是我们唯一的答案!
在最后我整理了一份资料,而且我们为了感谢很多支持的学者,资料是无偿分享的,需要的同学可以来学习学习
领取方式:GitHub地址
资料.png
资料图.jpg

场的逐渐成熟,平凡并不是我们唯一的答案!
在最后我整理了一份资料,而且我们为了感谢很多支持的学者,资料是无偿分享的,需要的同学可以来学习学习
领取方式:GitHub地址
[外链图片转存中…(img-67VPlXNS-1643956941262)]
[外链图片转存中…(img-tMeySZXF-1643956941262)]

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2022-02-05 21:51:20  更:2022-02-05 21:52:09 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 13:27:35-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码