react-native-skia
react-native-skia copied to clipboard
[Android] App crash in release build works fine in debug build
Description
Logs ---------------------------- PROCESS STARTED (27444) for package com.couplapp ---------------------------- 2023-08-09 19:58:54.086 27442-27442 DEBUG crash_dump64 A Cmdline: com.couplapp 2023-08-09 19:58:54.087 27442-27442 DEBUG crash_dump64 A pid: 26928, tid: 26928, name: com.couplapp >>> com.couplapp <<< 2023-08-09 19:58:54.087 27442-27442 DEBUG crash_dump64 A #00 pc 0000000000065694 /data/app/~~z7gaWc39l7ruURuChwu_Jw==/com.couplapp-Dq-SH5U1RGI0vq66qWdGeQ==/lib/arm64/libreanimated.so (BuildId: 4e2ea9c5275a9c49b5fdd425d29436cffd96a7b0) 2023-08-09 19:58:54.087 27442-27442 DEBUG crash_dump64 A #01 pc 0000000000080a78 /data/app/~~z7gaWc39l7ruURuChwu_Jw==/com.couplapp-Dq-SH5U1RGI0vq66qWdGeQ==/lib/arm64/libreanimated.so (reanimated::Scheduler::triggerUI()+240) (BuildId: 4e2ea9c5275a9c49b5fdd425d29436cffd96a7b0) 2023-08-09 19:58:54.087 27442-27442 DEBUG crash_dump64 A #02 pc 0000000000084098 /data/app/~~z7gaWc39l7ruURuChwu_Jw==/com.couplapp-Dq-SH5U1RGI0vq66qWdGeQ==/lib/arm64/libreanimated.so (facebook::jni::detail::MethodWrapper<void (reanimated::AndroidScheduler::)(), &(reanimated::AndroidScheduler::triggerUI()), reanimated::AndroidScheduler, void>::dispatch(facebook::jni::alias_ref<facebook::jni::detail::JTypeFor<facebook::jni::HybridClass<reanimated::AndroidScheduler, facebook::jni::detail::BaseHybridClass>::JavaPart, facebook::jni::JObject, void>::_javaobject>)+44) (BuildId: 4e2ea9c5275a9c49b5fdd425d29436cffd96a7b0) 2023-08-09 19:58:54.087 27442-27442 DEBUG crash_dump64 A #03 pc 0000000000084004 /data/app/~~z7gaWc39l7ruURuChwu_Jw==/com.couplapp-Dq-SH5U1RGI0vq66qWdGeQ==/lib/arm64/libreanimated.so (facebook::jni::detail::FunctionWrapper<void ()(facebook::jni::alias_ref<facebook::jni::detail::JTypeFor<facebook::jni::HybridClass<reanimated::AndroidScheduler, facebook::jni::detail::BaseHybridClass>::JavaPart, facebook::jni::JObject, void>::_javaobject>), facebook::jni::detail::JTypeFor<facebook::jni::HybridClass<reanimated::AndroidScheduler, facebook::jni::detail::BaseHybridClass>::JavaPart, facebook::jni::JObject, void>::_javaobject*, void>::call(_JNIEnv*, _jobject*, void ()(facebook::jni::alias_ref<facebook::jni::detail::JTypeFor<facebook::jni::HybridClass<reanimated::AndroidScheduler, facebook::jni::detail::BaseHybridClass>::JavaPart, facebook::jni::JObject, void>::_javaobject>))+60) (BuildId: 4e2ea9c5275a9c49b5fdd425d29436cffd96a7b0) 2023-08-09 19:58:54.087 27442-27442 DEBUG crash_dump64 A #04 pc 0000000000082e88 /data/app/~~z7gaWc39l7ruURuChwu_Jw==/com.couplapp-Dq-SH5U1RGI0vq66qWdGeQ==/lib/arm64/libreanimated.so (facebook::jni::detail::MethodWrapper<void (reanimated::AndroidScheduler::)(), &(reanimated::AndroidScheduler::triggerUI()), reanimated::AndroidScheduler, void>::call(_JNIEnv, _jobject*)+36) (BuildId: 4e2ea9c5275a9c49b5fdd425d29436cffd96a7b0) 2023-08-09 19:58:54.088 27442-27442 DEBUG crash_dump64 A #05 pc 0000000000073a54 /data/app/~~z7gaWc39l7ruURuChwu_Jw==/com.couplapp-Dq-SH5U1RGI0vq66qWdGeQ==/oat/arm64/base.odex (art_jni_trampoline+116) 2023-08-09 19:58:54.088 27442-27442 DEBUG crash_dump64 A #07 pc 0000000000347626 /data/app/~~z7gaWc39l7ruURuChwu_Jw==/com.couplapp-Dq-SH5U1RGI0vq66qWdGeQ==/oat/arm64/base.vdex (com.swmansion.reanimated.Scheduler$1.run+28) 2023-08-09 19:58:54.088 27442-27442 DEBUG crash_dump64 A #09 pc 000000000034764e /data/app/~~z7gaWc39l7ruURuChwu_Jw==/com.couplapp-Dq-SH5U1RGI0vq66qWdGeQ==/oat/arm64/base.vdex (com.swmansion.reanimated.Scheduler$2.runGuarded+12) 2023-08-09 19:58:54.088 27442-27442 DEBUG crash_dump64 A #10 pc 00000000000a9e94 /data/app/~~z7gaWc39l7ruURuChwu_Jw==/com.couplapp-Dq-SH5U1RGI0vq66qWdGeQ==/oat/arm64/base.odex (com.facebook.react.bridge.GuardedRunnable.run+52) 2023-08-09 19:58:54.171 4887-4887 audit auditd E type=1701 audit(1691591334.167:65904): auid=4294967295 uid=10448 gid=10448 ses=4294967295 subj=u:r:untrusted_app_30:s0:c192,c257,c512,c768 pid=26928 comm="com.couplapp" exe="/system/bin/app_process64" sig=11 res=1 2023-08-09 19:58:54.452 5403-5445 WindowManager system_server E win=Window{1bf733c u0 com.couplapp/com.couplapp.MainActivity EXITING} destroySurfaces: appStopped=false cleanupOnResume=false win.mWindowRemovalAllowed=true win.mRemoveOnExit=true win.mViewVisibility=0 caller=com.android.server.wm.ActivityRecord.destroySurfaces:6529 com.android.server.wm.ActivityRecord.destroySurfaces:6510 com.android.server.wm.WindowState.onExitAnimationDone:5965 com.android.server.wm.ActivityRecord$$ExternalSyntheticLambda10.accept:2 java.util.ArrayList.forEach:1262 com.android.server.wm.ActivityRecord.onAnimationFinished:8569 com.android.server.wm.ActivityRecord.postApplyAnimation:6243 ---------------------------- PROCESS ENDED (26928) for package com.couplapp ----------------------------
Version
0.1.195
Steps to reproduce
"react": "18.1.0", "react-native": "0.70.6", "@shopify/react-native-skia": "^0.1.195",
Snack, code example, screenshot, or link to a repository
import {
StyleSheet,
Text,
View,
Dimensions,
TouchableOpacity,
} from 'react-native';
import React from 'react';
import {curveBasis, line, scaleLinear, select} from 'd3';
import {
Canvas,
Circle,
Group,
LinearGradient,
Path,
Skia,
useTouchHandler,
useValue,
vec,
useComputedValue,
PathVerb,
Line,
} from '@shopify/react-native-skia';
import arrow from '../assets/images/greyArrowRight.png';
import {Image} from 'react-native';
import {getAdjacentMonth} from '../utils/lineGraphHelpers';
import {formatCurrency, log} from '../utils/helperFunctions';
const width = Dimensions.get('window').width * 1.08;
const height = 200;
const useGraphTouchHandler = (x, setValue) => {
return useTouchHandler({
onActive: e => {
const lowerBound = width / 8 + 10;
const upperBound = (width / 8) * 7;
if (e.x > lowerBound && e.x < upperBound) {
x.current = e.x;
setValue(e.x);
}
},
});
};
const Cursor = ({x, y}) => {
const transform = useComputedValue(
() => [{translateX: x.current}, {translateY: y.current}],
[x, y],
);
return (
<Group transform={transform}>
<Circle cx={0} cy={0} r={8} color={'#244566'} />
<Circle cx={0} cy={0} r={16} color={'#244566'} opacity={0.15} />
<Line
p1={vec(0, 0)}
p2={vec(0, height)}
color={'#244566'}
strokeWidth={1}
/>
</Group>
);
};
const selectCurve = (cmds, x) => {
let from = vec(0, 0);
for (let i = 0; i < cmds.length; i++) {
const cmd = cmds[i];
if (cmd[0] === PathVerb.Move) {
from = vec(cmd[1], cmd[2]);
} else if (cmd[0] === PathVerb.Cubic) {
const c1 = vec(cmd[1], cmd[2]);
const c2 = vec(cmd[3], cmd[4]);
const to = vec(cmd[5], cmd[6]);
if (x >= from.x && x <= to.x) {
return {
from,
c1,
c2,
to,
};
}
from = to;
}
}
return null;
};
const cubicBezier = (t, from, c1, c2, to) => {
const term = 1 - t;
const a = 1 * term ** 3 * t ** 0 * from;
const b = 3 * term ** 2 * t ** 1 * c1;
const c = 3 * term ** 1 * t ** 2 * c2;
const d = 1 * term ** 0 * t ** 3 * to;
return a + b + c + d;
};
const round = (value, precision = 0) => {
const p = Math.pow(10, precision);
return Math.round(value * p) / p;
};
const cuberoot = x => {
'worklet';
const y = Math.pow(Math.abs(x), 1 / 3);
return x < 0 ? -y : y;
};
const cubicBezierYForX = (x, a, b, c, d, precision = 2) => {
const pa = -a.x + 3 * b.x - 3 * c.x + d.x;
const pb = 3 * a.x - 6 * b.x + 3 * c.x;
const pc = -3 * a.x + 3 * b.x;
const pd = a.x - x;
const t = solveCubic(pa, pb, pc, pd)
.map(root => round(root, precision))
.filter(root => root >= 0 && root <= 1)[0];
return cubicBezier(t, a.y, b.y, c.y, d.y);
};
const solveCubic = (a, b, c, d) => {
if (Math.abs(a) < 1e-8) {
// Quadratic case, ax^2+bx+c=0
a = b;
b = c;
c = d;
if (Math.abs(a) < 1e-8) {
// Linear case, ax+b=0
a = b;
b = c;
if (Math.abs(a) < 1e-8) {
// Degenerate case
return [];
}
return [-b / a];
}
const D = b * b - 4 * a * c;
if (Math.abs(D) < 1e-8) {
return [-b / (2 * a)];
} else if (D > 0) {
return [(-b + Math.sqrt(D)) / (2 * a), (-b - Math.sqrt(D)) / (2 * a)];
}
return [];
}
// Convert to depressed cubic t^3+pt+q = 0 (subst x = t - b/3a)
const p = (3 * a * c - b * b) / (3 * a * a);
const q = (2 * b * b * b - 9 * a * b * c + 27 * a * a * d) / (27 * a * a * a);
let roots;
if (Math.abs(p) < 1e-8) {
// p = 0 -> t^3 = -q -> t = -q^1/3
roots = [cuberoot(-q)];
} else if (Math.abs(q) < 1e-8) {
// q = 0 -> t^3 + pt = 0 -> t(t^2+p)=0
roots = [0].concat(p < 0 ? [Math.sqrt(-p), -Math.sqrt(-p)] : []);
} else {
const D = (q * q) / 4 + (p * p * p) / 27;
if (Math.abs(D) < 1e-8) {
// D = 0 -> two roots
roots = [(-1.5 * q) / p, (3 * q) / p];
} else if (D > 0) {
// Only one real root
const u = cuberoot(-q / 2 - Math.sqrt(D));
roots = [u - p / (3 * u)];
} else {
// D < 0, three roots, but needs to use complex numbers/trigonometric solution
const u = 2 * Math.sqrt(-p / 3);
const t = Math.acos((3 * q) / p / u) / 3; // D < 0 implies p < 0 and acos argument in [-1..1]
const k = (2 * Math.PI) / 3;
roots = [u * Math.cos(t), u * Math.cos(t - k), u * Math.cos(t - 2 * k)];
}
}
// Convert back from depressed cubic
for (let i = 0; i < roots.length; i++) {
roots[i] -= b / (3 * a);
}
return roots;
};
const getYForX = (path, x, precision = 2) => {
const cmds = path.toCmds();
const c = selectCurve(cmds, x);
if (c === null) {
return 0;
}
return cubicBezierYForX(x, c.from, c.c1, c.c2, c.to, precision);
};
const months = {
1: 'Jan',
2: 'Feb',
3: 'Mar',
4: 'Apr',
5: 'May',
6: 'Jun',
7: 'Jul',
8: 'Aug',
9: 'Sep',
10: 'Oct',
11: 'Nov',
12: 'Dec',
};
const getGraphMonthValue = monthNo => {
const month = monthNo % 100;
const year = String(monthNo).split('').slice(0, 4).join('');
return year * 100 + (month * 100) / 12;
};
export default function LineGraph({
data = [],
getPrevMonth = () => {},
getNextMonth = () => {},
isPrev = true,
isNext = true,
}) {
const makeGraph = () => {
const edgedData = [
{value: 0, monthNo: getAdjacentMonth(data[0].monthNo, 'prev')},
...data,
{value: 0, monthNo: getAdjacentMonth(data[5].monthNo, 'next')},
].map(item => {
log(item);
return {
...item,
monthNo: getGraphMonthValue(item.monthNo),
};
});
log('path data', edgedData);
const minY = Math.min(...edgedData.map(item => item.value));
const maxY = Math.max(...edgedData.map(item => item.value));
const getYaxis = scaleLinear()
.domain([minY, maxY])
.range([height - 2, 0]);
const minX = Math.min(...edgedData?.map(item => item.monthNo));
const maxX = Math.max(...edgedData?.map(item => item.monthNo));
const getXaxis = scaleLinear().domain([minX, maxX]).range([0, width]);
const curvedLine = line()
.x(d => getXaxis(d.monthNo))
.y(d => getYaxis(d.value))
.curve(curveBasis)(edgedData);
return {
curve: curvedLine,
};
};
const getGradientAreaSplit = (
graphLine, // the line created above
width,
height, // in my use case this is the graph height / 2, i.e. area above or below y=0
variant = 'positive',
) => {
// 1) The initial graph line (which can go below or above y=0)
const gradientAreaSplit =
Skia.Path.MakeFromSVGString(graphLine ?? '0') ?? Skia.Path.Make();
const useFirstY =
(variant === 'positive' && gradientAreaSplit.getPoint(0).y > 0) ||
(variant === 'negative' && gradientAreaSplit.getPoint(0).y < 0);
gradientAreaSplit
// 2) Extend line to y=0 at the right end
.lineTo(width, height)
// 3) Extend line to y=0 at the left end
.lineTo(0, height)
// 4) Extend line to first point to close the area if that point is in the area, otherwise to y=0 (see useFirstY logic)
.lineTo(0, useFirstY ? gradientAreaSplit.getPoint(0).y : height);
return gradientAreaSplit;
};
const graphPath = makeGraph();
const skiaPath = Skia.Path.MakeFromSVGString(graphPath.curve);
const graphGradientPath = getGradientAreaSplit(
graphPath.curve,
width,
height + 40,
);
const [currentIndex, setCurrentIndex] = React.useState(0);
const [xValue, setXvalue] = React.useState(0);
const x = useValue(0);
const y = useComputedValue(
() => getYForX(skiaPath, x.current),
[skiaPath, x],
);
const getSpentFromX = x => {
const onePartWidth = width / 8;
const howManyParts = x / onePartWidth;
const arrIndex = Math.round(howManyParts - 1.5);
// log('@@ arrindex: ', arrIndex);
setCurrentIndex(Math.max(0, Math.min(5, arrIndex)));
};
const handleMonthTap = index => {
console.log('month index: ', index);
const onePartWidth = width / 8;
const localX = Math.floor(onePartWidth * (index * 1.02 + 1.5));
x.current = localX;
setXvalue(localX);
};
React.useEffect(() => {
getSpentFromX(xValue);
}, [xValue]);
const onTouch = useGraphTouchHandler(x, setXvalue);
return (
<>
<View style={styles.headerContainer}>
<Text style={styles.header}>Month-wise Spends</Text>
<View
style={{
flexDirection: 'row',
backgroundColor: '#E4F3FF',
paddingHorizontal: 16,
paddingVertical: 10,
borderRadius: 4,
}}>
<Text style={styles.header}>
{months[data[currentIndex].monthNo % 100]} '
{String(data[currentIndex].monthNo).split('').slice(2, 4).join('')}
</Text>
<Text
style={[
styles.header,
{marginLeft: 10, fontFamily: 'Inter-SemiBold'},
]}>
₹{formatCurrency(data[currentIndex].value)}
</Text>
</View>
</View>
<View
style={{transform: [{translateX: -width * 0.04}], paddingBottom: 10}}>
<Canvas style={{height: height, width}} onTouch={onTouch}>
<Path
path={graphGradientPath}
color="#244566"
strokeWidth={3}
style="stroke"
/>
<Path path={graphGradientPath}>
<LinearGradient
start={vec(0, height / 1.2)}
end={vec(0, 0)}
colors={['#fff', '#79C0FF85']}
/>
</Path>
<Cursor x={x} y={y} />
</Canvas>
</View>
<View style={styles.xAxisContainer}>
<TouchableOpacity
style={[styles.arrowButton, {opacity: isPrev ? 1 : 0.2}]}
disabled={!isPrev}
onPress={getPrevMonth}>
<Image
source={arrow}
style={[
styles.arrowImage,
{transform: [{rotate: '180deg'}, {translateY: -1}]},
]}
resizeMode="contain"
/>
</TouchableOpacity>
{data?.map((item, i) => (
<TouchableOpacity
key={i}
style={{padding: 10}}
onPress={() => handleMonthTap(i)}>
<Text
style={[
styles.monthName,
{
color:
item?.monthNo === data[currentIndex].monthNo
? '#204065'
: 'rgba(0, 0, 0, 0.60)',
fontFamily:
item?.monthNo === data[currentIndex].monthNo
? 'Inter-SemiBold'
: 'Inter-Regular',
},
]}>
{months[parseInt(item?.monthNo % 100)]}
</Text>
</TouchableOpacity>
))}
<TouchableOpacity
style={[styles.arrowButton, {opacity: isNext ? 1 : 0.2}]}
disabled={!isNext}
onPress={getNextMonth}>
<Image
source={arrow}
style={styles.arrowImage}
resizeMode="contain"
/>
</TouchableOpacity>
</View>
</>
);
}
@wcandillon @chrfalch Can you guys please check and help in this one, thanks in advance
Thank you for reporting this issue, we are really interested to fix it. Could you make the code snippet smaller? Also it is not runnable as a standalone piece of code as it has dependencies with external files. Anything you can do to help us reproduce the issue on our side would be extremely helpful.
Getting the same issue, getting [TypeError: undefined is not a function]
in releasew android builds. Seems to be coming from Canvas
(Using Skia 1.2.3
, RN 0.74.1
)
closing it but let me know if you have a reproduction