一、前言
开发APP,通常都会接入一些第三方的错误统计工具,例如友盟、bugly等,而这些第三方提供的SDK目前来看,只支持Native层的统计。对于ReactNative项目来说,反馈的错误日志无法直接看出具体错误信息,如下是友盟统计的错误日志
com.facebook.react.common.JavascriptException: TypeError: null is not an object (evaluating 't(i).name')
This error is located at:
in P
in RCTView
in f
in n
in RCTView
in RCTView
in S
in RCTView
in h, stack:
getRegionTextArr@1091:751
value@1095:18200
pi@89:51757
Ml@89:70036
Ul@89:67144
Ul@-1
Pl@89:65839
Pl@-1
<unknown>@89:25495
unstable_runWithPriority@168:3915
sn@89:25442
cn@89:25377
_e@89:88686
Ne@89:13582
Ue@89:13755
receiveTouches@89:14547
value@24:3685
<unknown>@24:841
value@24:2939
value@24:813
value@-1
at com.facebook.react.modules.core.ExceptionsManagerModule.reportException(ExceptionsManagerModule.java:71)
at java.lang.reflect.Method.invoke(Native Method)
at com.facebook.react.bridge.JavaMethodWrapper.invoke(JavaMethodWrapper.java:371)
at com.facebook.react.bridge.JavaModuleWrapper.invoke(JavaModuleWrapper.java:150)
at com.facebook.react.bridge.queue.NativeRunnable.run(Native Method)
at android.os.Handler.handleCallback(Handler.java:873)
at android.os.Handler.dispatchMessage(Handler.java:99)
at com.facebook.react.bridge.queue.MessageQueueThreadHandler.dispatchMessage(MessageQueueThreadHandler.java:26)
at android.os.Looper.loop(Looper.java:224)
at com.facebook.react.bridge.queue.MessageQueueThreadImpl$4.run(MessageQueueThreadImpl.java:225)
at java.lang.Thread.run(Thread.java:764)
这样的错误日志,可读性非常的差,虽然可以通过解压apk,查看assets/index.android.bundle
的1091行751列找到报错位置,但是bundle中的代码已经被混淆了,很难找到代码中具体位置。
二、生成Mapping文件
ReactNative App 的打包,完全借助了react.gradle
这个文件,你可以在 Android 工程的build.gradle
文件中找到它。
继续最终 node/modules 下的 react.gradle 文件。
可以看到它实际上是通过 react-native bundle
命令,通过增加参数的形式,输出index.android.bundle
文件的。还可以添加一个可配置的参数 —sourcemap-output
,指定生成一个对应关系的 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
项目根目录下生成android-release.bundle.map
文件
三、用source-map解析定位
在和map文件相同的目录下,新建一个js文件map.js
,具体代码如下:
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: 1091,
column: 751
}))
})
注意行号和列号与日志反馈的一致,使用终端命令行进入当前文件夹目录,执行node map.js
,如下:
我们就拿到了错误日志中位置所对应的项目代码的具体位置,查看此处代码
结合友盟上的日志信息null is not an object (evaluating 't(i).name')
,可以得知getProvince(provinceId)
返回为null,由此便可以确定问题原因