NativeScript / NativeScript/plugins
[background-http][Android] onProgress/onSuccess throw TypeError when the upload service outlives the JS runtime (Task.fromId returns undefined)
Dieses Issue hat noch niemand übernommen.
- Vorherrschende Sprache
- TypeScript
- Sterne
- 206
- Forks
- 123
- Ø Merge
- 2 T. 1 Std.
- Gemergte PRs (30 T.)
- 1
Beschreibung
Which package(s)
@nativescript/background-http@6.0.2 — Android only.
Environment
@nativescript/core9.0.20, CLI 9.0.6,@nativescript/android9.0.4net.gotev:uploadservice4.9.2 (the plugin's default)- Reported by production users on Android; not reproducible on iOS (see below)
Issue
Task.cache lives only in the JS runtime, but the gotev upload service is a
foreground service that outlives it. If the OS kills the app process while an
upload is in flight, the service keeps going (or is restarted) and eventually
broadcasts progress/completion. The GlobalRequestObserver registered by
init() then dispatches into a fresh runtime whose Task.cache is empty,
Task.fromId() returns undefined, and the handlers dereference it right away.
Two reports from the same upload, in the order they arrived:
Calling js method onProgress failed
TypeError: Cannot read properties of undefined (reading 'setTotalUpload')
at onProgressReceiverProgress(vendor.mjs)
at onProgress(vendor.mjs)
at com.tns.gen.net.gotev.uploadservice.observer.request.RequestObserverDelegate.onProgress(RequestObserverDelegate.java:21)
at net.gotev.uploadservice.observer.request.BaseRequestObserver.onReceive(BaseRequestObserver.kt:29)
Calling js method onSuccess failed
TypeError: Cannot read properties of undefined (reading 'setUpload')
at onProgressReceiverCompleted(vendor.mjs)
at onSuccess(vendor.mjs)
at com.tns.gen.net.gotev.uploadservice.observer.request.RequestObserverDelegate.onSuccess(RequestObserverDelegate.java:32)
at net.gotev.uploadservice.observer.request.BaseRequestObserver.onReceive(BaseRequestObserver.kt:31)
Note the upload itself succeeded — only the JS callback blew up.
Root cause
packages/background-http/index.android.ts on main:
function onProgressReceiverProgress(context: Context, uploadInfo: UploadInfo) {
const uploadId = uploadInfo.getUploadId();
const task = Task.fromId(uploadId); // may be undefined
const totalBytes = uploadInfo.getTotalBytes();
const currentBytes = uploadInfo.getUploadedBytes();
task.setTotalUpload(totalBytes);
Task.fromId (L228) is a plain Task.cache[id] lookup, and all four handlers
use the result unguarded: onProgressReceiverProgress (L42),
onProgressReceiverCompleted (L58), onProgressReceiverCancelled (L90) and
onProgressReceiverError (L97).
iOS is not affected: Task.getTask() (index.ios.ts L277-287) creates a Task
when the native task isn't in the map, instead of returning undefined.
Impact
Without discardUncaughtJsExceptions this is a hard crash — the exception
escapes a native callback. With the flag it is "only" a discarded exception, but
since progress fires repeatedly, a single orphaned upload produces a burst of
them (in our case, one error report per broadcast).
Steps to reproduce
- Start a
multipartUploadlarge enough to take several seconds. - Kill the app process while it runs (
adb shell am force-stop <pkg>, or let
the OS reclaim it) — the upload service survives. - Reopen the app so a fresh runtime calls
init()and registers the observer. - The service reports the resumed/finished upload and the TypeError is thrown.
Suggested fix
Guard the lookup in the four handlers, e.g.:
function onProgressReceiverProgress(context: Context, uploadInfo: UploadInfo) {
const uploadId = uploadInfo.getUploadId();
const task = Task.fromId(uploadId);
+ if (!task) {
+ // Upload started by a previous process: there is no JS task to notify.
+ return;
+ }
Related (bigger, happy to file separately)
Even with the guard, the outcome of an upload started before the restart is
simply lost — the app can never learn whether it succeeded. Two API additions
would make it recoverable: letting the caller supply the upload id (it is
currently generated internally as session.id + '{n}'), and a way to re-attach
listeners to an in-flight or finished upload by id. Today the only workaround is
reconciling against the server after the fact.
Beitragsleitfaden
Erste Schritte
- Lies das ganze Issue und danach den Beitragsleitfaden des Projekts.
- Schreib ins Issue, dass du es übernimmst — das erspart doppelte Arbeit.
- Forke das Repository und arbeite in einem Branch.
- Öffne einen Pull Request, der die Issue-Nummer nennt.
Rechercherichtung
Beginne in packages/background-http/index.android.ts und untersuche Task.fromId zusammen mit onProgressReceiverProgress, onProgressReceiverCompleted, onProgressReceiverCancelled und onProgressReceiverError. Reproduziere das Problem mit einem langen multipartUpload und, falls nötig, einer Prozessbeendigung. Erledigt bedeutet, dass Broadcasts für Uploads, die in Task.cache fehlen, sicher zurückkehren, ohne eine Exception auszulösen, während normale Task-Callbacks weiterhin ausgeführt werden.
Vom Indexierungsmodell aus dem Issue-Text verfasst.
Bewertung
- Tech-Stack
- android, typescript
- Bereich
- mobile-dev
- Issue-Typ
- Bug
- Schwierigkeit
- 2/5
- Geschätzter Aufwand
- 1-3 Stunden
- Aktivitätsstatus
- Ruhig
- Klarheit
- Klar beschrieben
- Anfängerfreundlichkeit
- 82/100