Skip to content

Commit 796b9b1

Browse files
authored
fix: typedarray & raf (#1729)
* fix: null data * chore: bump * fix: raf * chore: add raf test * chore: bump
1 parent b7646cb commit 796b9b1

File tree

4 files changed

+237
-3
lines changed

4 files changed

+237
-3
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "@nativescript/android",
33
"description": "NativeScript Runtime for Android",
4-
"version": "8.4.0-alpha.0",
4+
"version": "8.4.0-alpha.3",
55
"repository": {
66
"type": "git",
77
"url": "https://github.com/NativeScript/android-runtime.git"
Lines changed: 231 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,231 @@
1+
global.zonedCallback = function (callback) {
2+
if (global.zone) {
3+
// Zone v0.5.* style callback wrapping
4+
return global.zone.bind(callback);
5+
}
6+
if (global.Zone) {
7+
// Zone v0.6.* style callback wrapping
8+
return global.Zone.current.wrap(callback);
9+
} else {
10+
return callback;
11+
}
12+
};
13+
14+
15+
class FPSCallback {
16+
constructor(onFrame) {
17+
18+
this.impl = null
19+
this.onFrame
20+
this.running = false
21+
this.sdkVersion = 0
22+
this.nativeFramesSupported = false
23+
this.running = false;
24+
this.onFrame = onFrame;
25+
26+
this.sdkVersion = parseInt(android.os.Build.VERSION.SDK);
27+
this.nativeFramesSupported = this.sdkVersion >= 24 && this._isNativeFramesSupported();
28+
29+
if (this.nativeFramesSupported) {
30+
this.impl = (nanos) => {
31+
this.handleFrame(nanos);
32+
};
33+
34+
console.log('impl', this.impl);
35+
} else {
36+
this.impl = new android.view.Choreographer.FrameCallback({
37+
doFrame: (nanos) => {
38+
this.handleFrame(nanos);
39+
},
40+
});
41+
}
42+
}
43+
44+
_isNativeFramesSupported() {
45+
return typeof global.__postFrameCallback === 'function' && typeof global.__removeFrameCallback === 'function';
46+
}
47+
48+
start() {
49+
if (this.running) {
50+
return;
51+
}
52+
53+
if (this.nativeFramesSupported) {
54+
global.__postFrameCallback(this.impl);
55+
} else {
56+
android.view.Choreographer.getInstance().postFrameCallback(this.impl);
57+
}
58+
59+
this.running = true;
60+
}
61+
62+
stop() {
63+
if (!this.running) {
64+
return;
65+
}
66+
67+
if (this.nativeFramesSupported) {
68+
global.__removeFrameCallback(this.impl);
69+
} else {
70+
android.view.Choreographer.getInstance().removeFrameCallback(this.impl);
71+
}
72+
73+
this.running = false;
74+
}
75+
76+
handleFrame(nanos) {
77+
if (!this.running) {
78+
return;
79+
}
80+
81+
// divide by 1 000 000 since the parameter is in nanoseconds
82+
this.onFrame(nanos / 1000000);
83+
// add the FrameCallback instance again since it is automatically removed from the Choreographer
84+
85+
if (this.nativeFramesSupported) {
86+
global.__postFrameCallback(this.impl);
87+
} else {
88+
android.view.Choreographer.getInstance().postFrameCallback(this.impl);
89+
}
90+
}
91+
}
92+
93+
94+
function getTimeInFrameBase() {
95+
return java.lang.System.nanoTime() / 1000000;
96+
}
97+
98+
99+
function dispatchToMainThread(func) {
100+
const runOnMainThread = global.__runOnMainThread;
101+
if (runOnMainThread) {
102+
runOnMainThread(() => {
103+
func();
104+
});
105+
} else {
106+
new android.os.Handler(android.os.Looper.getMainLooper()).post(
107+
new java.lang.Runnable({
108+
run: func,
109+
})
110+
);
111+
}
112+
}
113+
114+
115+
let scheduled = false;
116+
let macroTaskQueue = [];
117+
118+
function drainMacrotaskQueue() {
119+
const currentQueue = macroTaskQueue;
120+
macroTaskQueue = [];
121+
scheduled = false;
122+
currentQueue.forEach((task) => {
123+
try {
124+
task();
125+
} catch (err) {
126+
const msg = err ? err.stack || err : err;
127+
}
128+
});
129+
}
130+
131+
function queueMacrotask(task) {
132+
macroTaskQueue.push(task);
133+
if (!scheduled) {
134+
scheduled = true;
135+
dispatchToMainThread(drainMacrotaskQueue);
136+
}
137+
}
138+
139+
let animationId = 0;
140+
let currentFrameAnimationCallbacks = {}; // requests that were scheduled in this frame and must be called ASAP
141+
let currentFrameScheduled = false;
142+
let nextFrameAnimationCallbacks = {}; // requests there were scheduled in another request and must be called in the next frame
143+
let shouldStop = true;
144+
let inAnimationFrame = false;
145+
let fpsCallback;
146+
let lastFrameTime = 0;
147+
148+
function getNewId() {
149+
return animationId++;
150+
}
151+
152+
function ensureNative() {
153+
if (fpsCallback) {
154+
return;
155+
}
156+
fpsCallback = new FPSCallback(doFrame);
157+
}
158+
159+
function callAnimationCallbacks(thisFrameCbs, frameTime) {
160+
inAnimationFrame = true;
161+
for (const animationId in thisFrameCbs) {
162+
if (thisFrameCbs[animationId]) {
163+
try {
164+
thisFrameCbs[animationId](frameTime);
165+
} catch (err) {
166+
const msg = err ? err.stack || err : err;
167+
}
168+
}
169+
}
170+
inAnimationFrame = false;
171+
}
172+
173+
function doCurrentFrame() {
174+
// if we're not getting accurate frame times
175+
// set last frame time as the current time
176+
if (!fpsCallback || !fpsCallback.running) {
177+
lastFrameTime = getTimeInFrameBase();
178+
}
179+
currentFrameScheduled = false;
180+
const thisFrameCbs = currentFrameAnimationCallbacks;
181+
currentFrameAnimationCallbacks = {};
182+
callAnimationCallbacks(thisFrameCbs, lastFrameTime);
183+
}
184+
185+
function doFrame(currentTimeMillis) {
186+
lastFrameTime = currentTimeMillis;
187+
shouldStop = true;
188+
const thisFrameCbs = nextFrameAnimationCallbacks;
189+
nextFrameAnimationCallbacks = {};
190+
callAnimationCallbacks(thisFrameCbs, lastFrameTime);
191+
if (shouldStop) {
192+
fpsCallback.stop(); // TODO: check performance without stopping to allow consistent frame times
193+
}
194+
}
195+
196+
function ensureCurrentFrameScheduled() {
197+
if (!currentFrameScheduled) {
198+
currentFrameScheduled = true;
199+
queueMacrotask(doCurrentFrame);
200+
}
201+
}
202+
203+
function requestAnimationFrame(cb) {
204+
const animId = getNewId();
205+
if (!inAnimationFrame) {
206+
ensureCurrentFrameScheduled();
207+
currentFrameAnimationCallbacks[animId] = zonedCallback(cb);
208+
return animId;
209+
}
210+
ensureNative();
211+
nextFrameAnimationCallbacks[animId] = zonedCallback(cb);
212+
shouldStop = false;
213+
fpsCallback.start();
214+
215+
return animId;
216+
}
217+
218+
function cancelAnimationFrame(id) {
219+
delete currentFrameAnimationCallbacks[id];
220+
delete nextFrameAnimationCallbacks[id];
221+
}
222+
223+
224+
module.exports = {
225+
FPSCallback,
226+
getTimeInFrameBase,
227+
requestAnimationFrame,
228+
cancelAnimationFrame,
229+
queueMacrotask,
230+
dispatchToMainThread
231+
}

test-app/runtime/src/main/cpp/CallbackHandlers.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1651,7 +1651,10 @@ void CallbackHandlers::PostFrameCallback(const FunctionCallbackInfo<v8::Value> &
16511651
auto id = pId->IntegerValue(context).FromMaybe(0);
16521652
if(frameCallbackCache_.contains(id)){
16531653
auto cb = frameCallbackCache_.find(id);
1654-
PostCallback(args, &cb->second, context);
1654+
if(cb != frameCallbackCache_.end()){
1655+
cb->second.removed = false;
1656+
PostCallback(args, &cb->second, context);
1657+
}
16551658
return;
16561659
}
16571660
}

test-app/runtime/src/main/cpp/JsArgConverter.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -287,7 +287,7 @@ bool JsArgConverter::ConvertArg(const Local<Value>& arg, int index) {
287287
bufferCastType = JsArgConverter::GetCastType(array);
288288
}
289289

290-
if(data != nullptr) {
290+
if(data == nullptr) {
291291
data = static_cast<uint8_t *>(store->Data()) + offset;
292292
}
293293

0 commit comments

Comments
 (0)