热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

在ReactNative的App中,集成Bugly你会遇到的一些坑

在,reactnative,的,app,中,集成,bugly,你会

一、前言

最近开新项目,准备尝试一下 ReactNative,所以前期做了一些调研工作,ReactNative 的优点非常的明显,可以做到跨平台,除了少部分 UI 效果可能需要对不同的平台进行单独适配,其中的核心逻辑代码,都是可以重用的。所以如果最终用 ReactNative 的话,可以省出某一端的客户端开发人员。而我这里调研的主要方向,就是它对国内第三方 SDK 的支持。

在国内,开发 App,一般都是会集成一些第三方服务的,例如:升级、崩溃分析、数据统计等等。而这些第三方服务,提供的 SDK ,通常只有 Native 层的,例如 Android 就是使用 Java 写的。而 ReactNative 本身 Javascript 和 Native 层(Java层)的通信,其实已经做的很好了,所以大部分情况下,我们只需要对这些 SDK 做一个简单的封装就可以正常使用它了。

本期就来分享一下,如何在 ReactNative 的基础之上,集成 Bugly。这里主要是看它的崩溃搜集,这也是 Bugly 的主要功能。对于崩溃的收集,我主要关心两个部分:

  1. 是需要统计到正确的崩溃栈。
  2. 统计到的崩溃栈要是易于阅读的。

其实主要工作卡在了后者,接下来让我们具体看看问题。

本文的分析都是基于最新的 ReactNative (v0.49) 版本来分析。

二、ReactNative 的崩溃统计

首先,ReactNative 中 Javascript 和 Native 层的通信,官方文档已经写的非常清楚了。在官方文档中,举了一个 Toast 模块的例子,写的很清晰,这里就不再赘述了,还不了解的,可以先看看文档。

ReactNative 原生模块(中文文档):

http://reactnative.cn/docs/0.49/native-modules-android.html#content

2.1 ReactNative 的编译模式

而在 ReactNative 的程序中,实际上运行的是 Js 的代码,而它也是分 Debug 和 Release 的。

在 Debug 模式下,会从本地开启一个 Packager 服务,然后 App 运行起来之后,直接从服务里拉取最新的编译后的 JS 代码,这样可以在开发阶段,做到代码实时更新的效果,只需要在设备上,重新 Load 一下即可。

而在 Release 模式下,ReactNative 会将 JS 代码,整体打包,然后放到 assets 目录下,然后从这里去加载 JS 代码。

这样的逻辑被封装在 ReactInstanceManager 类的 recreateReactContextInBackgroundInner() 方法中,有兴趣可以自行看看。

react-server

可以很清晰的看到,在 Debug 和 Release ,分别使用的不同的方式,加载 JS 文件的。这里为什么要说到 ReactNative App 的编译模式呢?其实和后面的逻辑有关系。

ReactNative 在 Debug 的情况下,其实还是很贴心的,如果出现崩溃的 Bug,会直接出红屏,提示你崩溃的栈的具体信息,这些内容可以帮助你快速的定位问题。

js-crash

这里给的例子,是一个 Js 层的崩溃,可以看到它崩溃栈中,很清晰的看到 App.js 文件的第 48 行 21列,会有一个 ReferenceError 的错误。

最方便的是,你直接点击崩溃栈的代码,会自动打开对应的 Js 文件。当然,如果是一个 Native 层的崩溃,虽然也会出红屏,但是点击并不能跳转。

而假如现在同样的代码,使用 Release 模式的话,则会直接崩溃了。

2.2 不同编译模式的 Js 有什么不同

假如 Release 和 Debug 一样,可以有如此清晰的崩溃栈,其实问题就已经得到解决。但是当你使用 Release 包来触发一个崩溃的时候,你就会发现,它并不是一样的。

使用命令,可以直接安装一个 Release 版本到设备上。

cd android && ./gradlew installRelease

这里其实是两行命令,先进入到 android 项目的目录,然后运行 ./gradlew installRelease 这个没什么好说的,如果运行失败,注意一下当前 shell 环境的目录路径。

此时,我们再运行它就会直接导致崩溃,来看看崩溃的 Log 输出。

js-crash-stack

很尴尬的是,虽然崩溃栈也被输出出来了,和前面红屏的截图对比一下,也能发现它们其实是一个内容。但是,这些代码被混淆过了,如果 Native App 一样,混淆过的代码,反编译来看会变成 a.b.c ,这里的效果也是类似的。

这样的崩溃栈,其实拿出来,可读性非常的差,但是并不是不可读的。

那么接下来来看看如何定位到这个崩溃的真实代码,value@304:1133 这里,就是线索。我们把 Apk 解压,拿到其内 assets/index.android.bundle 文件,它其内就是我们 ReactNative 编译好的 Js 文件,可以看到它的第 304 行 1133 列,就是我们需要定位的出了问题的代码。

index-bundle

这样的编译后的代码,查 Bug 查起来就非常的费时了,你首先需要根据当前版本发布出去的 Apk,然后根据其中的 index.android.bundle 文件,定位到具体的代码,之后再结合上下文全文搜索你的源代码,才能找到对应出错的代码。

注意我这里本身项目就是一个 Demo 项目,代码量比较少,还能准确的定位到问题,如果是一个实际的项目,在打 Release 包的时候,会将所有的 JS 文件全部打包到 index.android.bundle 文件中去。在这个例子中,如果 props.username.name 这段代码,我在很多地方都用到的话,筛选它也是非常麻烦的。

2.3 Release 缺少了什么?

从前面的内容可以了解到,Release 包同样也是可以定位到出错的代码的。但是,你依然需要全文的搜索这段代码,无法精准定位到具体出错代码所在的源文件,这是为什么?

Release 包的 Js 一定是经过混淆的,会剥离掉一些必要的信息,这些被剥离的信息,导致我们无法精准定位到代码的源文件上。

在 Debug 模式下,运行我们的 Packager Server ,然后在浏览器中访问:

http://localhost:8081/index.android.bundle?platform=android&dev=true

请确保你的 Packager Server 保持运行的情况下访问。

就可以看到当前 Debug 模式,App 所运行的 JS 代码。我们直接根据出错代码,精准定位一下。

debug-server

在这里,就可以很清晰的看到,它有一个 fileName 和 lineNumber 两个属性,分别用来记录当前源码的文件和这段代码所在的行数。而回忆一下之前 Release 版本的 JS 代码,你会发现关于源文件和行号的信息,被剥离了。

这也就是我们无法精准定位出错代码和锁在源文件的根本原因。

2.4 Mapping

既然已经明确的知道,在 Release 下,会过滤掉一些关于源文件和行号的信息,就如同 Android 的混淆一样,那它是否包含类似对照关系的 Mapping 文件,可以帮助我们还原回去?

那么我们就需要找到 index.android.bundle 这个文件,是如何产生的。

ReactNative App 的打包,完全借助了 react.gradle 这个文件,你可以在 Android 工程的 build.gradle 文件中找到它。

app-gradle

继续最终 node/modules 下的 react.gradle 文件。

react-gradle

可以看到它实际上是通过 react-native bundle 命令,通过增加参数的形式,输出 index.android.bundle 文件的。

而如果你查阅文档,你会发现 react-native 命令,还有一个可配置的参数 —sourcemap-output,它就是我们需要的。

完整的说明,你可以在这个网站上找到资料:

https://docs.bugsnag.com/platforms/react-native/showing-full-stacktraces/

我这里把关键信息截图出来看着更清晰。

source-map

--sourcemap-output 命令非常的简单,只需要配置一个输出的文件名就可以了。

这里我们直接在命令行里运行如下代码,就可以自动重新生成一个 index.android.bundle 文件,并且同时也会生产一个对应关系的 map 文件。

react-native bundle --platform android  --dev false  --entry-file index.js  --bundle-output android/app/src/main/assets/index.android.bundle  --assets-dest android/app/src/main/res/  --sourcemap-output android-release.bundle.map

运行效果如下:

build-map

注意这段命令,需要在 ReactNative 目录的根目录下执行,否者会提示你找不到 node_module 。执行完成,就可以在 ReactNative 项目目录下,看到输出的 android-release.bundle.map 文件了。

点开看看,完全看不懂,随便截个图让大家感受一下。

map

其实到这里,已经离我们的答案,更近一步了,Android 混淆的 Mapping 文件,也不是我们肉眼能清晰看懂的,我们接下来只需要找到它的解析规则就可以了。

解析这个 source-map ,NodeJs 为我们提供了一个专门的库来解析,这里不多解释,直接上代码。

map-js

/**  * Created by cxmyDev on 2017/10/31.  */ var sourceMap = require('source-map'); var fs = require('fs'); fs.readFile('../android-release.bundle.map', 'utf8', function (err, data) { var smc = new sourceMap.SourceMapConsumer(data); console.log(smc.originalPositionFor({ line: 304, column: 1133 })); });

注意看这里指定的 304 行 1133 列,我们运行一下,看看输出。

map-js-output

这段代码,会很清晰的输出对应的源文件名和行号,以及错的字段,还是很清晰的。

再来对照我们的源代码验证一下。

error-line

确实也如 map.js 脚本输出的一样。

2.5 小结

到此,我们算是完成了 ReactNative App,崩溃分析的一个完整的链路逻辑,我们只需要自己写个脚本工具,就可以帮我们精准定位了。

前面有点长,这里总结一下本小结的内容。

  1. ReactNative 不同的编译模式,使用的 JS 来源不同。Debug 模式来自 Packager Server,而 Release 模式,来自 Apk 的 assets 目录。
  2. Debug 模式下的崩溃,会触发红屏,而 Release 模式下的崩溃,会直接导致 App 崩溃。
  3. Debug 模式,之所以可以显示崩溃栈的基本信息,是因为编译的 JS 文件中,包含了对应的源文件和代码行号。而这些在 Release 模式下的 JS 是没有的。
  4. Release 模式的崩溃栈是被混淆后的,可以通过崩溃栈显示的行号和列号,来定位代码,但是无法定位具体源文件。
  5. 通过 react-native 命名,增加 --sourcemap-output参数,指定输出需要的混淆 Mapping 文件,它其内包含了混淆的信息。
  6. 解读 ReactNative Mapping 文件,可以使用 source-map 这个 NodeJs 库来进行解析,可以精准定位到行号和源文件名。

三、集成 Bugly 的坑

Bugly 的集成,非常的简单。如果之前用过 Bugly 的,并且阅读 ReactNative 和 原生通信 这部分文档的话,差不多十分钟就可以集成完毕。

还不了解 ReactNative 和原生通信内容的,建议先阅读一下本文档了解一下。

ReactNative 原生模块(中文文档):

http://reactnative.cn/docs/0.49/native-modules-android.html#content

Bugly 的注册没有什么门槛,这里直接使用个人 QQ 号就可以登录,创建一个专门为 ReactNative 测试的 App,然后根据文档绑定对应的 AppID 即可。

不清楚的可以查阅 Bugly 的文档:

https://bugly.qq.com/docs/user-guide/instruction-manual-android/?v=20171030170001

这部分内容没什么好说的,都是标准话的流程。接下来我们来看看集成它将面临的坑。

3.1 Debug 模式下不会上报崩溃

之前也提到,Debug 模式下,如果触发了崩溃,会直接进入红屏状态,显示当前崩溃栈的信息。这个功能,在我们开发阶段,非常的好用,能快速定位问题。

但是正是因为 ReactNative 会在 Debug 模式下,Hook 住我们的崩溃栈,从而会导致 Bugly SDK 无法搜集到对应的崩溃也就无法进行上报。

所以,如果你在 ReactNative 项目内,集成了 Bugly 之后。造的崩溃没有得到上报,检查一下自己编译模式,一定要切换到 Release 模式下。

3.2 崩溃信息整合

Bugly 为了方便开发者查看,会将类似崩溃栈的崩溃,整合成一个,然后进行计数统计,只显示当前崩溃了多少次和影响的人数。

而在 ReactNative 项目中,如果是 Native 层出现的崩溃,那其实没有什么差别,崩溃信息和我们平时开发常规 App 一样。

但是,如果这个崩溃是发生在 Js 层的话,它最终会把崩溃抛到 Native 层,同样也是可以统计的的。但是这些崩溃会被封装成一个 JavascriptException 抛出来,从而导致它们被简单的归为了 JavascriptException 。可能它们描述的是不同的 Bug,但是却被归位一类,这样之后查阅起来,就需要人工进行筛选。

这里看两个崩溃,第一个发生在 Js 层,第二个发生在 Native 层。

bugly-crash

3.3 解读 Bugly 中,js层的崩溃

Native 层的崩溃,和常规 App 一样,没什么好说的。这里只看 Js 层的崩溃信息。

js-stack

从这个崩溃栈你可以发现,其实下面 Java 的栈,基本上没有任何信息。这里主要是阅读上面 TypeError 后面的信息。这里描述了 Js 层崩溃的所有信息,包含错误和崩溃栈。

前面的内容如果认真看了,应该不难发现此处就是对 JS 崩溃输出的格式化拉平成一行了,所以如果我们要针对 Bugly 的崩溃栈编写解析脚本,就需要考虑到这些情况。

四、总结

本文说是 ReactNative 集成 Bugly 的一些坑,实际上讲的更多的是在生产环境下,如何分析 ReactNative 的崩溃栈。这些被搜集的原始信息,如何被还原成我们需要的信息。

不过这些,还是期待国内环境下,更多第三方 SDK 能支持到 ReactNative,毕竟官方团队支持的肯定要比我们自己写补丁脚本来的方便实用。


本文转自承香墨影博客园博客,原文链接:http://www.cnblogs.com/plokmju/p/7825865.html,如需转载请自行联系原作者



推荐阅读
  • 本文介绍了闭包的定义和运转机制,重点解释了闭包如何能够接触外部函数的作用域中的变量。通过词法作用域的查找规则,闭包可以访问外部函数的作用域。同时还提到了闭包的作用和影响。 ... [详细]
  • 本文讨论了在Windows 8上安装gvim中插件时出现的错误加载问题。作者将EasyMotion插件放在了正确的位置,但加载时却出现了错误。作者提供了下载链接和之前放置插件的位置,并列出了出现的错误信息。 ... [详细]
  • 如何使用Java获取服务器硬件信息和磁盘负载率
    本文介绍了使用Java编程语言获取服务器硬件信息和磁盘负载率的方法。首先在远程服务器上搭建一个支持服务端语言的HTTP服务,并获取服务器的磁盘信息,并将结果输出。然后在本地使用JS编写一个AJAX脚本,远程请求服务端的程序,得到结果并展示给用户。其中还介绍了如何提取硬盘序列号的方法。 ... [详细]
  • baresip android编译、运行教程1语音通话
    本文介绍了如何在安卓平台上编译和运行baresip android,包括下载相关的sdk和ndk,修改ndk路径和输出目录,以及创建一个c++的安卓工程并将目录考到cpp下。详细步骤可参考给出的链接和文档。 ... [详细]
  • 本文介绍了在CentOS上安装Python2.7.2的详细步骤,包括下载、解压、编译和安装等操作。同时提供了一些注意事项,以及测试安装是否成功的方法。 ... [详细]
  • 树莓派语音控制的配置方法和步骤
    本文介绍了在树莓派上实现语音控制的配置方法和步骤。首先感谢博主Eoman的帮助,文章参考了他的内容。树莓派的配置需要通过sudo raspi-config进行,然后使用Eoman的控制方法,即安装wiringPi库并编写控制引脚的脚本。具体的安装步骤和脚本编写方法在文章中详细介绍。 ... [详细]
  • 本文记录了在vue cli 3.x中移除console的一些采坑经验,通过使用uglifyjs-webpack-plugin插件,在vue.config.js中进行相关配置,包括设置minimizer、UglifyJsPlugin和compress等参数,最终成功移除了console。同时,还包括了一些可能出现的报错情况和解决方法。 ... [详细]
  • 本文介绍了在Mac上安装Xamarin并使用Windows上的VS开发iOS app的方法,包括所需的安装环境和软件,以及使用Xamarin.iOS进行开发的步骤。通过这种方法,即使没有Mac或者安装苹果系统,程序员们也能轻松开发iOS app。 ... [详细]
  • VueCLI多页分目录打包的步骤记录
    本文介绍了使用VueCLI进行多页分目录打包的步骤,包括页面目录结构、安装依赖、获取Vue CLI需要的多页对象等内容。同时还提供了自定义不同模块页面标题的方法。 ... [详细]
  • vue使用
    关键词: ... [详细]
  • 本文介绍了lua语言中闭包的特性及其在模式匹配、日期处理、编译和模块化等方面的应用。lua中的闭包是严格遵循词法定界的第一类值,函数可以作为变量自由传递,也可以作为参数传递给其他函数。这些特性使得lua语言具有极大的灵活性,为程序开发带来了便利。 ... [详细]
  • 生成式对抗网络模型综述摘要生成式对抗网络模型(GAN)是基于深度学习的一种强大的生成模型,可以应用于计算机视觉、自然语言处理、半监督学习等重要领域。生成式对抗网络 ... [详细]
  • 本文介绍了三种方法来实现在Win7系统中显示桌面的快捷方式,包括使用任务栏快速启动栏、运行命令和自己创建快捷方式的方法。具体操作步骤详细说明,并提供了保存图标的路径,方便以后使用。 ... [详细]
  • 本文介绍了使用cacti监控mssql 2005运行资源情况的操作步骤,包括安装必要的工具和驱动,测试mssql的连接,配置监控脚本等。通过php连接mssql来获取SQL 2005性能计算器的值,实现对mssql的监控。详细的操作步骤和代码请参考附件。 ... [详细]
  • 【shell】网络处理:判断IP是否在网段、两个ip是否同网段、IP地址范围、网段包含关系
    本文介绍了使用shell脚本判断IP是否在同一网段、判断IP地址是否在某个范围内、计算IP地址范围、判断网段之间的包含关系的方法和原理。通过对IP和掩码进行与计算,可以判断两个IP是否在同一网段。同时,还提供了一段用于验证IP地址的正则表达式和判断特殊IP地址的方法。 ... [详细]
author-avatar
的发个地方
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有