Nativescript-angular: Exception when calling canGoBack within back button listener

Created on 4 Jul 2019  路  8Comments  路  Source: NativeScript/nativescript-angular

@bastianjoel commented on Thu Jul 04 2019

Environment

  • CLI: 5.4.0
  • Cross-platform modules: 5.4.3
  • Android Runtime: 5.4.0

Describe the bug
Calling canGoBack() of RouterExtensions causes the following exception when using inside back button listener.

An uncaught Exception occurred on "main" thread.
com.tns.NativeScriptException: 
Calling js method onBackPressed failed

TypeError: Cannot read property 'states' of null
File: "file:///data/data/org.nativescript.preview/files/app/tns_modules/nativescript-angular/router/ns-location-strategy.js, line: 236, column: 18

StackTrace: 
Frame: function:'NSLocationStrategy.canGoBack', file:'file:///data/data/org.nativescript.preview/files/app/tns_modules/nativescript-angular/router/ns-location-strategy.js', line: 236, column: 19
Frame: function:'RouterExtensions.canGoBack', file:'file:///data/data/org.nativescript.preview/files/app/tns_modules/nativescript-angular/router/router-extensions.js', line: 51, column: 41
Frame: function:'', file:'file:///data/data/org.nativescript.preview/files/app/app.component.js', line: 26, column: 34
Frame: function:'Observable.notify', file:'file:///data/data/org.nativescript.preview/files/app/tns_modules/tns-core-modules/data/observable/observable.js', line: 110, column: 15
Frame: function:'ActivityCallbacksImplementation.onBackPressed', file:'file:///data/data/org.nativescript.preview/files/app/tns_modules/tns-core-modules/ui/frame/frame.js', line: 888, column: 25
Frame: function:'NativeScriptActivity.onBackPressed', file:'file:///data/data/org.nativescript.preview/files/app/tns_modules/tns-core-modules/ui/frame/activity.js', line: 41, column: 21


at com.tns.Runtime.callJSMethodNative(Native Method)
at com.tns.Runtime.dispatchCallJSMethodNative(Runtime.java:1203)
at com.tns.Runtime.callJSMethodImpl(Runtime.java:1083)
at com.tns.Runtime.callJSMethod(Runtime.java:1070)
at com.tns.Runtime.callJSMethod(Runtime.java:1050)
at com.tns.Runtime.callJSMethod(Runtime.java:1042)
at com.tns.NativeScriptActivity.onBackPressed(NativeScriptActivity.java:59)
at android.app.Activity.onKeyUp(Activity.java:3087)
at android.view.KeyEvent.dispatch(KeyEvent.java:2722)
at android.app.Activity.dispatchKeyEvent(Activity.java:3380)
at android.support.v4.app.SupportActivity.superDispatchKeyEvent(ComponentActivity.java:108)
at android.support.v4.view.KeyEventDispatcher.dispatchKeyEvent(KeyEventDispatcher.java:84)
at android.support.v4.app.SupportActivity.dispatchKeyEvent(ComponentActivity.java:126)
at android.support.v7.app.AppCompatActivity.dispatchKeyEvent(AppCompatActivity.java:535)
at android.support.v7.view.WindowCallbackWrapper.dispatchKeyEvent(WindowCallbackWrapper.java:59)
at android.support.v7.app.AppCompatDelegateImpl$AppCompatWindowCallback.dispatchKeyEvent(AppCompatDelegateImpl.java:2533)
at com.android.internal.policy.DecorView.dispatchKeyEvent(DecorView.java:358)
at android.view.ViewRootImpl$ViewPostImeInputStage.processKeyEvent(ViewRootImpl.java:5347)
at android.view.ViewRootImpl$ViewPostImeInputStage.onProcess(ViewRootImpl.java:5215)
at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:4626)
at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:4679)
at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:4645)
at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:4653)
at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:4626)
at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:4679)
at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:4645)
at android.view.ViewRootImpl$AsyncInputStage.forward(ViewRootImpl.java:4801)
at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:4653)
at android.view.ViewRootImpl$AsyncInputStage.apply(ViewRootImpl.java:4858)
at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:4626)
at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:4679)
at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:4645)
at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:4653)
at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:4626)
at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:4679)
at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:4645)
at android.view.ViewRootImpl$AsyncInputStage.forward(ViewRootImpl.java:4834)
at android.view.ViewRootImpl$ImeInputStage.onFinishedInputEvent(ViewRootImpl.java:4995)
at android.view.inputmethod.InputMethodManager$PendingEvent.run(InputMethodManager.java:2602)
at android.view.inputmethod.InputMethodManager.invokeFinishedInputEventCallback(InputMethodManager.java:2112)
at android.view.inputmethod.InputMethodManager.finishedInputEvent(InputMethodManager.java:2103)
at android.view.inputmethod.InputMethodManager$ImeInputEventSender.onInputEventFinished(InputMethodManager.java:2579)
at android.view.InputEventSender.dispatchInputEventFinished(InputEventSender.java:141)
at android.os.MessageQueue.nativePollOnce(Native Method)
at android.os.MessageQueue.next(MessageQueue.java:326)
at android.os.Looper.loop(Looper.java:160)
at android.app.ActivityThread.main(ActivityThread.java:6854)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:860)

To Reproduce
General:

  1. Add a android back button listener and define property tries
application.android.on(application.AndroidApplication.activityBackPressedEvent, (args: any) => {
    if (!this.router.canGoBack()) {
        args.cancel = (this.tries++ > 0) ? false : true;

        setTimeout(() => {
            this.tries = 0;
        }, 2000);
    } else {
        args.cancel = false;
    }
});
  1. Close app by using the back button twice
  2. Reopen app
  3. Tap the back button again

Expected behavior

Sample project
https://play.nativescript.org/?template=play-ng&id=AO3iYR&v=343

Additional context

bug android

Most helpful comment

When you use the back button twice you're not completely exiting the app, but the UI is destroyed (including angular). What's happening is:

  1. You call android.on(function_on_instance1)
  2. You press back button to exit
  3. it calls function_on_instance1
  4. it exits and destroys the angular instance
  5. open app, new angular instance
  6. You call android.on(function_on_instance2)
  7. You press back button to exit
  8. it calls function_on_instance1 then function_on_instance2

Solution:

ngOnInit() {
application.android.on(application.AndroidApplication.activityBackPressedEvent, handleBackButton, this);
}
ngOnDestroy() {
application.android.off(application.AndroidApplication.activityBackPressedEvent, handleBackButton, this);
}
handleBackButton(args: any) {
    this.ngZone.run(() => {
        args.cancel = true;
        if (this.router.canGoBack()) {
            this.router.back();
        }
    }
}

All 8 comments

I am getting this issue. Anyone has a solution?

Hello, have the same problem when triggering the back-button on specific pages. (dont understand the original example with triggering twice and reopen. For me, just the "normal" workflow is breaking it.

application.android.on(application.AndroidApplication.activityBackPressedEvent, (args: any) => {
    this.ngZone.run(() => {
        args.cancel = true;
        if (this.router.canGoBack()) {
            this.router.back();
        }
    }
});

I also have this problem!

facing the same problem.

I have same issue on IOS :'(

I鈥檝e same this issue. let me know if this is resolve. thanks everyone

When you use the back button twice you're not completely exiting the app, but the UI is destroyed (including angular). What's happening is:

  1. You call android.on(function_on_instance1)
  2. You press back button to exit
  3. it calls function_on_instance1
  4. it exits and destroys the angular instance
  5. open app, new angular instance
  6. You call android.on(function_on_instance2)
  7. You press back button to exit
  8. it calls function_on_instance1 then function_on_instance2

Solution:

ngOnInit() {
application.android.on(application.AndroidApplication.activityBackPressedEvent, handleBackButton, this);
}
ngOnDestroy() {
application.android.off(application.AndroidApplication.activityBackPressedEvent, handleBackButton, this);
}
handleBackButton(args: any) {
    this.ngZone.run(() => {
        args.cancel = true;
        if (this.router.canGoBack()) {
            this.router.back();
        }
    }
}

When you use the back button twice you're not completely exiting the app, but the UI is destroyed (including angular). What's happening is:

  1. You call android.on(function_on_instance1)
  2. You press back button to exit
  3. it calls function_on_instance1
  4. it exits and destroys the angular instance
  5. open app, new angular instance
  6. You call android.on(function_on_instance2)
  7. You press back button to exit
  8. it calls function_on_instance1 then function_on_instance2

Solution:

ngOnInit() {
application.android.on(application.AndroidApplication.activityBackPressedEvent, handleBackButton, this);
}
ngOnDestroy() {
application.android.off(application.AndroidApplication.activityBackPressedEvent, handleBackButton, this);
}
handleBackButton(args: any) {
    this.ngZone.run(() => {
        args.cancel = true;
        if (this.router.canGoBack()) {
            this.router.back();
        }
    }
}

Thanks for this @edusperoni !

Was this page helpful?
0 / 5 - 0 ratings