mirror of
https://github.com/NativeScript/NativeScript.git
synced 2025-11-05 13:26:48 +08:00
Fix android activity being destroyed then recreated. (#2321)
This commit is contained in:
@@ -1,41 +1,46 @@
|
||||
import {activityCallbacks as callbacks} from "ui/frame";
|
||||
import {setActivityCallbacks, AndroidActivityCallbacks} from "ui/frame";
|
||||
|
||||
@JavaProxy("com.tns.NativeScriptActivity")
|
||||
class NativeScriptActivity extends android.app.Activity {
|
||||
private _callbacks: AndroidActivityCallbacks;
|
||||
constructor() {
|
||||
super();
|
||||
return global.__native(this);
|
||||
}
|
||||
super();
|
||||
return global.__native(this);
|
||||
}
|
||||
|
||||
protected onCreate(savedInstanceState: android.os.Bundle): void {
|
||||
callbacks.onCreate(this, savedInstanceState, super.onCreate);
|
||||
if (!this._callbacks) {
|
||||
setActivityCallbacks(this);
|
||||
}
|
||||
|
||||
this._callbacks.onCreate(this, savedInstanceState, super.onCreate);
|
||||
}
|
||||
|
||||
protected onSaveInstanceState(outState: android.os.Bundle): void {
|
||||
callbacks.onSaveInstanceState(this, outState, super.onSaveInstanceState);
|
||||
this._callbacks.onSaveInstanceState(this, outState, super.onSaveInstanceState);
|
||||
}
|
||||
|
||||
protected onStart(): void {
|
||||
callbacks.onStart(this, super.onStart);
|
||||
this._callbacks.onStart(this, super.onStart);
|
||||
}
|
||||
|
||||
protected onStop(): void {
|
||||
callbacks.onStop(this, super.onStop);
|
||||
this._callbacks.onStop(this, super.onStop);
|
||||
}
|
||||
|
||||
protected onDestroy(): void {
|
||||
callbacks.onDestroy(this, super.onDestroy);
|
||||
this._callbacks.onDestroy(this, super.onDestroy);
|
||||
}
|
||||
|
||||
public onBackPressed(): void {
|
||||
callbacks.onBackPressed(this, super.onBackPressed);
|
||||
this._callbacks.onBackPressed(this, super.onBackPressed);
|
||||
}
|
||||
|
||||
public onRequestPermissionsResult (requestCode: number, permissions: Array<String>, grantResults: Array<number>): void {
|
||||
callbacks.onRequestPermissionsResult(this, requestCode, permissions, grantResults, undefined /*TODO: Enable if needed*/);
|
||||
public onRequestPermissionsResult(requestCode: number, permissions: Array<String>, grantResults: Array<number>): void {
|
||||
this._callbacks.onRequestPermissionsResult(this, requestCode, permissions, grantResults, undefined /*TODO: Enable if needed*/);
|
||||
}
|
||||
|
||||
protected onActivityResult(requestCode: number, resultCode: number, data: android.content.Intent): void {
|
||||
callbacks.onActivityResult(this, requestCode, resultCode, data, super.onActivityResult);
|
||||
this._callbacks.onActivityResult(this, requestCode, resultCode, data, super.onActivityResult);
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
import * as frame from "ui/frame";
|
||||
import {AndroidFragmentCallbacks, setFragmentCallbacks, setFragmentClass} from "ui/frame";
|
||||
|
||||
@JavaProxy("com.tns.FragmentClass")
|
||||
class FragmentClass extends android.app.Fragment {
|
||||
// This field is updated in the frame module upon `new` (although hacky this eases the Fragment->callbacks association a lot)
|
||||
private _callbacks;
|
||||
private _callbacks: AndroidFragmentCallbacks;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
@@ -20,7 +20,11 @@ class FragmentClass extends android.app.Fragment {
|
||||
}
|
||||
|
||||
public onCreate(savedInstanceState: android.os.Bundle) {
|
||||
super.setHasOptionsMenu(true);
|
||||
if (!this._callbacks) {
|
||||
setFragmentCallbacks(this);
|
||||
}
|
||||
|
||||
this.setHasOptionsMenu(true);
|
||||
this._callbacks.onCreate(this, savedInstanceState, super.onCreate);
|
||||
}
|
||||
|
||||
@@ -46,4 +50,4 @@ class FragmentClass extends android.app.Fragment {
|
||||
}
|
||||
}
|
||||
|
||||
frame.setFragmentClass(FragmentClass);
|
||||
setFragmentClass(FragmentClass);
|
||||
@@ -16,13 +16,13 @@ let FRAMEID = "_frameId";
|
||||
let navDepth = -1;
|
||||
let fragmentId = -1;
|
||||
let activityInitialized = false;
|
||||
const PAGE_FRAGMENT_TAG = "_fragmentTag";
|
||||
const CALLBACKS = "_callbacks";
|
||||
|
||||
function onFragmentShown(fragment: android.app.Fragment) {
|
||||
if (trace.enabled) {
|
||||
trace.write(`SHOWN ${fragment}`, trace.categories.NativeLifecycle);
|
||||
}
|
||||
|
||||
let callbacks: FragmentCallbacksImplementation = fragment[CALLBACKS];
|
||||
if (callbacks.clearHistory) {
|
||||
// This is the fragment which was at the bottom of the stack (fragment0) when we cleared history and called
|
||||
@@ -38,7 +38,7 @@ function onFragmentShown(fragment: android.app.Fragment) {
|
||||
let frame = callbacks.frame;
|
||||
let entry = callbacks.entry;
|
||||
let page = entry.resolvedPage;
|
||||
page[PAGE_FRAGMENT_TAG] = entry.fragmentTag;
|
||||
page._fragmentTag = entry.fragmentTag;
|
||||
|
||||
let currentNavigationContext;
|
||||
let navigationQueue = (<any>frame)._navigationQueue;
|
||||
@@ -72,7 +72,7 @@ function onFragmentHidden(fragment: android.app.Fragment, destroyed: boolean) {
|
||||
let callbacks: FragmentCallbacksImplementation = fragment[CALLBACKS];
|
||||
let isBack = callbacks.entry.isBack;
|
||||
callbacks.entry.isBack = undefined;
|
||||
callbacks.entry.resolvedPage[PAGE_FRAGMENT_TAG] = undefined;
|
||||
callbacks.entry.resolvedPage._fragmentTag = undefined;
|
||||
|
||||
// Handle page transitions.
|
||||
transitionModule._onFragmentHidden(fragment, isBack, destroyed);
|
||||
@@ -145,7 +145,7 @@ export class Frame extends frameCommon.Frame {
|
||||
}
|
||||
|
||||
let clearHistory = backstackEntry.entry.clearHistory;
|
||||
|
||||
|
||||
// New Fragment
|
||||
if (clearHistory) {
|
||||
navDepth = -1;
|
||||
@@ -155,15 +155,14 @@ export class Frame extends frameCommon.Frame {
|
||||
let newFragmentTag = `fragment${fragmentId}[${navDepth}]`;
|
||||
ensureFragmentClass();
|
||||
let newFragment: android.app.Fragment = new fragmentClass();
|
||||
|
||||
let callbacks = new FragmentCallbacksImplementation();
|
||||
callbacks.frame = this;
|
||||
callbacks.entry = backstackEntry;
|
||||
|
||||
newFragment[CALLBACKS] = callbacks;
|
||||
let args = new android.os.Bundle();
|
||||
args.putInt(FRAMEID, this._android.frameId);
|
||||
newFragment.setArguments(args);
|
||||
setFragmentCallbacks(newFragment);
|
||||
|
||||
let callbacks = newFragment[CALLBACKS];
|
||||
callbacks.frame = this;
|
||||
callbacks.entry = backstackEntry;
|
||||
|
||||
// backstackEntry
|
||||
backstackEntry.isNavigation = true;
|
||||
@@ -189,7 +188,7 @@ export class Frame extends frameCommon.Frame {
|
||||
|
||||
// Clear History
|
||||
let length = manager.getBackStackEntryCount();
|
||||
let emptyNativeBackStack = clearHistory && length > 0;
|
||||
let emptyNativeBackStack = clearHistory && length > 0;
|
||||
if (emptyNativeBackStack) {
|
||||
for (let i = 0; i < length; i++) {
|
||||
let fragmentToRemove = manager.findFragmentByTag(manager.getBackStackEntryAt(i).getName());
|
||||
@@ -359,7 +358,7 @@ export class Frame extends frameCommon.Frame {
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
protected _processNavigationContext(navigationContext: frameCommon.NavigationContext) {
|
||||
let activity = this._android.activity;
|
||||
if (activity) {
|
||||
@@ -419,7 +418,7 @@ class AndroidFrame extends Observable implements definition.AndroidFrame {
|
||||
private _showActionBar = true;
|
||||
private _owner: Frame;
|
||||
private _cachePagesOnNavigate: boolean;
|
||||
|
||||
|
||||
constructor(owner: Frame) {
|
||||
super();
|
||||
this._owner = owner;
|
||||
@@ -491,7 +490,7 @@ class AndroidFrame extends Observable implements definition.AndroidFrame {
|
||||
return activity;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@@ -524,30 +523,31 @@ class AndroidFrame extends Observable implements definition.AndroidFrame {
|
||||
// can go back only if it is not the main one.
|
||||
return this.activity.getIntent().getAction() !== android.content.Intent.ACTION_MAIN;
|
||||
}
|
||||
|
||||
|
||||
public fragmentForPage(page: pages.Page): any {
|
||||
if(!page) {
|
||||
if (!page) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
let tag = page[PAGE_FRAGMENT_TAG];
|
||||
if(tag) {
|
||||
let tag = page._fragmentTag;
|
||||
if (tag) {
|
||||
let manager = this.activity.getFragmentManager();
|
||||
return manager.findFragmentByTag(tag);
|
||||
}
|
||||
|
||||
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
function findPageForFragment(fragment: android.app.Fragment, frame: Frame) {
|
||||
var fragmentTag = fragment.getTag();
|
||||
var page: pages.Page;
|
||||
var entry: definition.BackstackEntry;
|
||||
let fragmentTag = fragment.getTag();
|
||||
let page: pages.Page;
|
||||
let entry: definition.BackstackEntry;
|
||||
|
||||
if (trace.enabled) {
|
||||
trace.write(`Finding page for ${fragmentTag}.`, trace.categories.NativeLifecycle);
|
||||
}
|
||||
|
||||
if (fragmentTag === (<any>pages).DIALOG_FRAGMENT_TAG) {
|
||||
if (trace.enabled) {
|
||||
trace.write(`No need to find page for dialog fragment.`, trace.categories.NativeLifecycle);
|
||||
@@ -564,7 +564,7 @@ function findPageForFragment(fragment: android.app.Fragment, frame: Frame) {
|
||||
}
|
||||
else {
|
||||
var backStack = frame.backStack;
|
||||
for (var i = 0; i < backStack.length; i++) {
|
||||
for (let i = 0; i < backStack.length; i++) {
|
||||
if (backStack[i].fragmentTag === fragmentTag) {
|
||||
entry = backStack[i];
|
||||
break;
|
||||
@@ -584,7 +584,7 @@ function findPageForFragment(fragment: android.app.Fragment, frame: Frame) {
|
||||
callbacks.entry = entry;
|
||||
}
|
||||
else {
|
||||
//throw new Error(`Could not find a page for ${fragmentTag}.`);
|
||||
throw new Error(`Could not find a page for ${fragmentTag}.`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -618,26 +618,27 @@ function ensureAnimationFixed() {
|
||||
}
|
||||
|
||||
function ensureFragmentClass() {
|
||||
if(fragmentClass) {
|
||||
if (fragmentClass) {
|
||||
return;
|
||||
}
|
||||
|
||||
// this require will apply the FragmentClass implementation
|
||||
require("ui/frame/fragment");
|
||||
|
||||
if(!fragmentClass) {
|
||||
if (!fragmentClass) {
|
||||
throw new Error("Failed to initialize the extended android.app.Fragment class");
|
||||
}
|
||||
}
|
||||
|
||||
let fragmentClass: any;
|
||||
export function setFragmentClass(clazz: any) {
|
||||
if(fragmentClass) {
|
||||
if (fragmentClass) {
|
||||
throw new Error("Fragment class already initialized");
|
||||
}
|
||||
|
||||
fragmentClass = clazz;
|
||||
}
|
||||
|
||||
class FragmentCallbacksImplementation implements definition.AndroidFragmentCallbacks {
|
||||
public frame: Frame;
|
||||
public entry: definition.BackstackEntry;
|
||||
@@ -802,7 +803,7 @@ class ActivityCallbacksImplementation implements definition.AndroidActivityCallb
|
||||
|
||||
rootView = frame;
|
||||
}
|
||||
|
||||
|
||||
// If there is savedInstanceState this call will recreate all fragments that were previously in the navigation.
|
||||
// We take care of associating them with a Page from our backstack in the onAttachFragment callback.
|
||||
// If there is savedInstanceState and activityInitialized is false we are restarted but process was killed.
|
||||
@@ -823,7 +824,7 @@ class ActivityCallbacksImplementation implements definition.AndroidActivityCallb
|
||||
|
||||
activityInitialized = true;
|
||||
}
|
||||
|
||||
|
||||
public onSaveInstanceState(activity: android.app.Activity, outState: android.os.Bundle, superFunc: Function): void {
|
||||
superFunc.call(activity, outState);
|
||||
let view = this._rootView;
|
||||
@@ -831,7 +832,7 @@ class ActivityCallbacksImplementation implements definition.AndroidActivityCallb
|
||||
outState.putInt(INTENT_EXTRA, view.android.frameId);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public onStart(activity: any, superFunc: Function): void {
|
||||
superFunc.call(activity);
|
||||
|
||||
@@ -863,12 +864,12 @@ class ActivityCallbacksImplementation implements definition.AndroidActivityCallb
|
||||
}
|
||||
|
||||
superFunc.call(activity);
|
||||
|
||||
|
||||
if (trace.enabled) {
|
||||
trace.write("NativeScriptActivity.onDestroy();", trace.categories.NativeLifecycle);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public onBackPressed(activity: any, superFunc: Function): void {
|
||||
if (trace.enabled) {
|
||||
trace.write("NativeScriptActivity.onBackPressed;", trace.categories.NativeLifecycle);
|
||||
@@ -890,7 +891,7 @@ class ActivityCallbacksImplementation implements definition.AndroidActivityCallb
|
||||
superFunc.call(activity);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public onRequestPermissionsResult(activity: any, requestCode: number, permissions: Array<String>, grantResults: Array<number>, superFunc: Function): void {
|
||||
if (trace.enabled) {
|
||||
trace.write("NativeScriptActivity.onRequestPermissionsResult;", trace.categories.NativeLifecycle);
|
||||
@@ -928,4 +929,10 @@ class ActivityCallbacksImplementation implements definition.AndroidActivityCallb
|
||||
}
|
||||
}
|
||||
|
||||
export var activityCallbacks = new ActivityCallbacksImplementation();
|
||||
export function setActivityCallbacks(activity: android.app.Activity): void {
|
||||
activity[CALLBACKS] = new ActivityCallbacksImplementation();
|
||||
}
|
||||
|
||||
export function setFragmentCallbacks(fragment: android.app.Fragment): void {
|
||||
fragment[CALLBACKS] = new FragmentCallbacksImplementation();
|
||||
}
|
||||
7
tns-core-modules/ui/frame/frame.d.ts
vendored
7
tns-core-modules/ui/frame/frame.d.ts
vendored
@@ -122,11 +122,6 @@ declare module "ui/frame" {
|
||||
on(event: "optionSelected", callback: (args: observable.EventData) => void, thisArg?: any);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the default AndroidActivityCallbacks implementation, used to bridge Activity events to the Frame and navigation routine. This field is initialized only for the Android platform.
|
||||
*/
|
||||
export var activityCallbacks: AndroidActivityCallbacks;
|
||||
|
||||
/**
|
||||
* Sets the extended android.app.Fragment class to the Frame and navigation routine. An instance of this class will be created to represent the Page currently visible on the srceen. This method is available only for the Android platform.
|
||||
*/
|
||||
@@ -356,5 +351,7 @@ declare module "ui/frame" {
|
||||
//@private
|
||||
function reloadPage(): void;
|
||||
function resolvePageFromEntry(entry: NavigationEntry): pages.Page;
|
||||
function setFragmentCallbacks(fragment: android.app.Fragment): void;
|
||||
function setActivityCallbacks(activity: android.app.Activity): void;
|
||||
//@endprivate
|
||||
}
|
||||
@@ -60,6 +60,7 @@ export class Page extends ContentView implements dts.Page {
|
||||
private _actionBar: ActionBar;
|
||||
|
||||
public _modal: Page;
|
||||
public _fragmentTag: string;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
6
tns-core-modules/ui/page/page.d.ts
vendored
6
tns-core-modules/ui/page/page.d.ts
vendored
@@ -217,6 +217,12 @@ declare module "ui/page" {
|
||||
|
||||
//@private
|
||||
|
||||
/**
|
||||
* identifier for the fragment that shows this page.
|
||||
* Android only.
|
||||
*/
|
||||
_fragmentTag: string;
|
||||
|
||||
/**
|
||||
* A method called before navigating to the page.
|
||||
* @param context - The data passed to the page through the NavigationEntry.context property.
|
||||
|
||||
Reference in New Issue
Block a user