From c06b0681fae6bd2eb2f9fe24f7519b3d340f6178 Mon Sep 17 00:00:00 2001 From: hshristov Date: Fri, 6 Nov 2015 10:04:56 +0200 Subject: [PATCH] Fixed android crash when process get killed --- CrossPlatformModules.csproj | 5 +- ui/frame/frame-common.ts | 18 ++ ui/frame/frame.android.ts | 332 ++++++++++++++++++++++++------------ 3 files changed, 248 insertions(+), 107 deletions(-) diff --git a/CrossPlatformModules.csproj b/CrossPlatformModules.csproj index 780cfb80f..043d41534 100644 --- a/CrossPlatformModules.csproj +++ b/CrossPlatformModules.csproj @@ -252,12 +252,13 @@ - all.xml + all.xml clean.xml + animation.d.ts @@ -2092,7 +2093,7 @@ False - + \ No newline at end of file diff --git a/ui/frame/frame-common.ts b/ui/frame/frame-common.ts index dea195294..e436d7a75 100644 --- a/ui/frame/frame-common.ts +++ b/ui/frame/frame-common.ts @@ -28,6 +28,23 @@ function buildEntryFromArgs(arg: any): definition.NavigationEntry { return entry; } +export function reloadPage(): void { + let frame = topmost(); + if (frame) { + let currentEntry = frame._currentEntry.entry; + let newEntry: definition.NavigationEntry = { + animated: false, + clearHistory: true, + context: currentEntry.context, + create: currentEntry.create, + moduleName: currentEntry.moduleName, + backstackVisible: currentEntry.backstackVisible + } + + frame.navigate(newEntry); + } +} + export function resolvePageFromEntry(entry: definition.NavigationEntry): pages.Page { var page: pages.Page; @@ -230,6 +247,7 @@ export class Frame extends view.CustomLayoutView implements definition.Frame { var navContext = navigationContext.entry; this._onNavigatingTo(navContext, navigationContext.isBackNavigation); + // TODO: This should happen once navigation is completed. if (navigationContext.entry.entry.clearHistory) { this._backStack.length = 0; } diff --git a/ui/frame/frame.android.ts b/ui/frame/frame.android.ts index 5ea22090b..0babe860d 100644 --- a/ui/frame/frame.android.ts +++ b/ui/frame/frame.android.ts @@ -16,138 +16,247 @@ var ANDROID_FRAME = "android_frame"; var BACKSTACK_TAG = "_backstackTag"; var NAV_DEPTH = "_navDepth"; var CLEARING_HISTORY = "_clearingHistory"; +var activityInitialized = false; var navDepth = -1; -class PageFragmentBody extends android.app.Fragment { - public frame: Frame; - public entry: definition.BackstackEntry; +var PageFragmentBody = (android.app.Fragment).extend({ + + onAttach: function (activity: android.app.Activity) { + console.log("PageFragmentBody onAttach called"); + this.super.onAttach(activity); + console.log("PageFragmentBody onAttach ended"); + }, - constructor(frame: Frame, entry: definition.BackstackEntry) { - super(); - - this.frame = frame; - this.entry = entry; - return global.__native(this); - } - - onAttach(activity: android.app.Activity) { - super.onAttach(activity); - trace.write(this.toString() + ".onAttach();", trace.categories.NativeLifecycle); - } - - onCreate(savedInstanceState: android.os.Bundle) { - super.onCreate(savedInstanceState); - trace.write(this.toString() + ".onCreate(); savedInstanceState: " + savedInstanceState, trace.categories.NativeLifecycle); - super.setHasOptionsMenu(true); - } - - onCreateView(inflater: android.view.LayoutInflater, container: android.view.ViewGroup, savedInstanceState: android.os.Bundle): android.view.View { - trace.write(this.toString() + ".onCreateView(); container: " + container + "; savedInstanceState: " + savedInstanceState, trace.categories.NativeLifecycle); - - if (this[CLEARING_HISTORY]) { - trace.write(`${this.toString() } wants to create a view, but we are currently clearing history. Returning null.`, trace.categories.NativeLifecycle); - return null; - } - - var entry: definition.BackstackEntry = this.entry; - var page: pages.Page = entry.resolvedPage; + onCreate: function (savedInstanceState: android.os.Bundle) { + console.log("PageFragmentBody onCreate called"); + this.super.onCreate(savedInstanceState); + this.super.setHasOptionsMenu(true); + }, + onCreateView: function (inflater: android.view.LayoutInflater, container: android.view.ViewGroup, savedInstanceState: android.os.Bundle): android.view.View { + console.log("PageFragmentBody onCreateView called"); + var entry = this.entry; + var page = entry.resolvedPage; if (savedInstanceState && savedInstanceState.getBoolean(HIDDEN, false)) { - // Manually hide the fragment if it was hidden before as Android will not do this every time. - super.getFragmentManager().beginTransaction().hide(this).commit(); - - // As the page will not be added to the frame it won't be attached - // We should attach it manually so that it creates its nativeView + this.super.getFragmentManager().beginTransaction().hide(this).commit(); page._onAttached(this.getActivity()); } else { onFragmentShown(this); } - - trace.write(this.toString() + ".onCreateView(); nativeView: " + page._nativeView, trace.categories.NativeLifecycle); + console.log("PageFragmentBody onCreateView ended"); return page._nativeView; - } - - onHiddenChanged(hidden: boolean) { - super.onHiddenChanged(hidden); - trace.write(this.toString() + ".onHiddenChanged(); hidden: " + hidden, trace.categories.NativeLifecycle); + }, + onHiddenChanged: function (hidden: boolean) { + console.log("PageFragmentBody onHiddenChanged called"); + this.super.onHiddenChanged(hidden); if (hidden) { onFragmentHidden(this); } else { onFragmentShown(this); } - } + console.log("PageFragmentBody onHiddenChanged ended"); + }, - onActivityCreated(savedInstanceState: android.os.Bundle) { - super.onActivityCreated(savedInstanceState); - trace.write(this.toString() + ".onActivityCreated(); savedInstanceState: " + savedInstanceState, trace.categories.NativeLifecycle); - } - - onSaveInstanceState(outState: android.os.Bundle) { - super.onSaveInstanceState(outState); - trace.write(this.toString() + ".onSaveInstanceState();", trace.categories.NativeLifecycle); + onActivityCreated: function (savedInstanceState: android.os.Bundle) { + console.log("PageFragmentBody onActivityCreated called"); + this.super.onActivityCreated(savedInstanceState); + console.log("PageFragmentBody onActivityCreated ended"); + }, + onSaveInstanceState: function (outState: android.os.Bundle) { + console.log("PageFragmentBody onSaveInstanceState called"); + this.super.onSaveInstanceState(outState); if (this.isHidden()) { outState.putBoolean(HIDDEN, true); } - } + console.log("PageFragmentBody onSaveInstanceState ended"); + }, - onViewStateRestored(savedInstanceState: android.os.Bundle) { - super.onViewStateRestored(savedInstanceState); - trace.write(this.toString() + ".onViewStateRestored(); savedInstanceState: " + savedInstanceState, trace.categories.NativeLifecycle); - } + onViewStateRestored: function (savedInstanceState: android.os.Bundle) { + console.log("PageFragmentBody onViewStateRestored called"); + this.super.onViewStateRestored(savedInstanceState); + trace.write(this.getTag() + ".onViewStateRestored(); savedInstanceState: " + savedInstanceState, trace.categories.NativeLifecycle); + console.log("PageFragmentBody onViewStateRestored ended"); + }, - onStart() { - super.onStart(); - trace.write(this.toString() + ".onStart();", trace.categories.NativeLifecycle); - } + onStart: function () { + console.log("PageFragmentBody onStart called"); + this.super.onStart(); + console.log("PageFragmentBody onStart ended"); + }, - onResume() { - super.onResume(); - trace.write(this.toString() + ".onResume();", trace.categories.NativeLifecycle); - } + onResume: function () { + console.log("PageFragmentBody onResume called"); + this.super.onResume(); + console.log("PageFragmentBody onResume ended"); + }, - onPause() { - super.onPause(); - trace.write(this.toString() + ".onPause();", trace.categories.NativeLifecycle); - } + onPause: function () { + console.log("PageFragmentBody onPause called"); + this.super.onPause(); + console.log("PageFragmentBody onPause ended"); + }, - onStop() { - super.onStop(); - trace.write(this.toString() + ".onStop();", trace.categories.NativeLifecycle); - } - - onDestroyView() { - super.onDestroyView(); - trace.write(this.toString() + ".onDestroyView();", trace.categories.NativeLifecycle); + onStop: function () { + console.log("PageFragmentBody onStop called"); + this.super.onStop(); + console.log("PageFragmentBody onStop ended"); + }, + onDestroyView: function () { + console.log("PageFragmentBody onDestroyView called"); + this.super.onDestroyView(); onFragmentHidden(this); - } + console.log("PageFragmentBody onDestroyView ended"); + }, - onDestroy() { - super.onDestroy(); - trace.write(this.toString() + ".onDestroy();", trace.categories.NativeLifecycle); - - // Explicitly free resources to allow Java Garbage collector to release resources associated with JavaScript implementations - e.g. large image files. - // Although we hint the V8 with the externally allocated memory, synchronization between the two GCs is not deterministic without an explicit call. - // TODO: Revisit this explicit Garbage Collector call when possible. + onDestroy: function () { + console.log("PageFragmentBody onDestroy called"); + this.super.onDestroy(); utils.GC(); - } + console.log("PageFragmentBody onDestroy ended"); + }, - onDetach() { - super.onDetach(); - trace.write(this.toString() + ".onDetach();", trace.categories.NativeLifecycle); + onDetach: function () { + console.log("PageFragmentBody onDetach called"); + this.super.onDetach(); + console.log("PageFragmentBody onDetach ended"); } +}); - public toString() { - return `${this.getTag() }[${this.entry.resolvedPage.id}]`; - } -} +//class PageFragmentBody extends android.app.Fragment { +// public frame: Frame; +// public entry: definition.BackstackEntry; -function onFragmentShown(fragment: PageFragmentBody) { +// constructor(frame: Frame, entry: definition.BackstackEntry) { +// super(); + +// this.frame = frame; +// this.entry = entry; +// return global.__native(this); +// } + +// onAttach(activity: android.app.Activity) { +// super.onAttach(activity); +// trace.write(this.toString() + ".onAttach();", trace.categories.NativeLifecycle); +// } + +// onCreate(savedInstanceState: android.os.Bundle) { +// super.onCreate(savedInstanceState); +// trace.write(this.toString() + ".onCreate(); savedInstanceState: " + savedInstanceState, trace.categories.NativeLifecycle); +// super.setHasOptionsMenu(true); +// } + +// onCreateView(inflater: android.view.LayoutInflater, container: android.view.ViewGroup, savedInstanceState: android.os.Bundle): android.view.View { +// trace.write(this.toString() + ".onCreateView(); container: " + container + "; savedInstanceState: " + savedInstanceState, trace.categories.NativeLifecycle); + +// if (this[CLEARING_HISTORY]) { +// trace.write(`${this.toString() } wants to create a view, but we are currently clearing history. Returning null.`, trace.categories.NativeLifecycle); +// return null; +// } + +// var entry: definition.BackstackEntry = this.entry; +// var page: pages.Page = entry.resolvedPage; + +// if (savedInstanceState && savedInstanceState.getBoolean(HIDDEN, false)) { +// // Manually hide the fragment if it was hidden before as Android will not do this every time. +// super.getFragmentManager().beginTransaction().hide(this).commit(); + +// // As the page will not be added to the frame it won't be attached +// // We should attach it manually so that it creates its nativeView +// page._onAttached(this.getActivity()); +// } +// else { +// onFragmentShown(this); +// } + +// trace.write(this.toString() + ".onCreateView(); nativeView: " + page._nativeView, trace.categories.NativeLifecycle); +// return page._nativeView; +// } + +// onHiddenChanged(hidden: boolean) { +// super.onHiddenChanged(hidden); +// trace.write(this.toString() + ".onHiddenChanged(); hidden: " + hidden, trace.categories.NativeLifecycle); + +// if (hidden) { +// onFragmentHidden(this); +// } +// else { +// onFragmentShown(this); +// } +// } + +// onActivityCreated(savedInstanceState: android.os.Bundle) { +// super.onActivityCreated(savedInstanceState); +// trace.write(this.toString() + ".onActivityCreated(); savedInstanceState: " + savedInstanceState, trace.categories.NativeLifecycle); +// } + +// onSaveInstanceState(outState: android.os.Bundle) { +// super.onSaveInstanceState(outState); +// trace.write(this.toString() + ".onSaveInstanceState();", trace.categories.NativeLifecycle); + +// if (this.isHidden()) { +// outState.putBoolean(HIDDEN, true); +// } +// } + +// onViewStateRestored(savedInstanceState: android.os.Bundle) { +// super.onViewStateRestored(savedInstanceState); +// trace.write(this.toString() + ".onViewStateRestored(); savedInstanceState: " + savedInstanceState, trace.categories.NativeLifecycle); +// } + +// onStart() { +// super.onStart(); +// trace.write(this.toString() + ".onStart();", trace.categories.NativeLifecycle); +// } + +// onResume() { +// super.onResume(); +// trace.write(this.toString() + ".onResume();", trace.categories.NativeLifecycle); +// } + +// onPause() { +// super.onPause(); +// trace.write(this.toString() + ".onPause();", trace.categories.NativeLifecycle); +// } + +// onStop() { +// super.onStop(); +// trace.write(this.toString() + ".onStop();", trace.categories.NativeLifecycle); +// } + +// onDestroyView() { +// super.onDestroyView(); +// trace.write(this.toString() + ".onDestroyView();", trace.categories.NativeLifecycle); + +// onFragmentHidden(this); +// } + +// onDestroy() { +// super.onDestroy(); +// trace.write(this.toString() + ".onDestroy();", trace.categories.NativeLifecycle); + +// // Explicitly free resources to allow Java Garbage collector to release resources associated with JavaScript implementations - e.g. large image files. +// // Although we hint the V8 with the externally allocated memory, synchronization between the two GCs is not deterministic without an explicit call. +// // TODO: Revisit this explicit Garbage Collector call when possible. +// utils.GC(); +// } + +// onDetach() { +// super.onDetach(); +// trace.write(this.toString() + ".onDetach();", trace.categories.NativeLifecycle); +// } + +// public toString() { +// return `${this.getTag() }[${this.entry.resolvedPage.id}]`; +// } +//} + +function onFragmentShown(fragment) { if (fragment[CLEARING_HISTORY]) { trace.write(`${fragment.toString() } has been shown, but we are currently clearing history. Returning.`, trace.categories.NativeLifecycle); return null; @@ -183,7 +292,7 @@ function onFragmentShown(fragment: PageFragmentBody) { frame._processNavigationQueue(page); } -function onFragmentHidden(fragment: PageFragmentBody) { +function onFragmentHidden(fragment) { if (fragment[CLEARING_HISTORY]) { trace.write(`${fragment.toString() } has been hidden, but we are currently clearing history. Returning.`, trace.categories.NativeLifecycle); return null; @@ -282,7 +391,16 @@ export class Frame extends frameCommon.Frame { var fragmentTransaction = manager.beginTransaction(); var newFragmentTag = "fragment" + navDepth; - var newFragment = new PageFragmentBody(this, backstackEntry); + //var newFragment = new PageFragmentBody(this, backstackEntry); + var newFragment = new PageFragmentBody(); + + var args = new android.os.Bundle(); + args.putInt("num", num); + newFragment.setArguments(args); + + newFragment.frame = this; + newFragment.entry = backstackEntry; + backstackEntry[BACKSTACK_TAG] = newFragmentTag; backstackEntry[NAV_DEPTH] = navDepth; @@ -404,7 +522,7 @@ export class Frame extends frameCommon.Frame { console.log("---------------------------"); console.log("Fragment Manager Back Stack (" + length + ")"); while (i >= 0) { - var fragment = manager.findFragmentByTag(manager.getBackStackEntryAt(i--).getName()); + var fragment = manager.findFragmentByTag(manager.getBackStackEntryAt(i--).getName()); console.log("[ " + fragment.toString() + " ]"); } } @@ -457,7 +575,11 @@ var NativeActivity = { // 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. - this.super.onCreate(savedInstanceState); + // If there is savedInstanceState and activityInitialized is false we are restarted but process was killed. + // For now we treat it like first run (e.g. we are not passing savedInstanceState so no fragments are being restored). + // When we add support for applicatioin save/load state - revise this logic. + var isRestart = !!savedInstanceState && activityInitialized; + this.super.onCreate(isRestart ? savedInstanceState : null); this.androidFrame.setActivity(this); @@ -469,7 +591,8 @@ var NativeActivity = { this.setContentView(this.androidFrame.rootViewGroup, new org.nativescript.widgets.CommonLayoutParams()); // If there is no instance state - we call navigateCore from here since Activity is created AFTER the navigate call and navigateCore will fail. - var isRestart = !!savedInstanceState; + + activityInitialized = true; this.frame._onActivityCreated(isRestart); }, @@ -496,7 +619,7 @@ var NativeActivity = { trace.write("NativeScriptActivity.onAttachFragment() : " + fragment.getTag(), trace.categories.NativeLifecycle); this.super.onAttachFragment(fragment); - if (!(fragment).entry) { + if (!(fragment).entry) { // There is no entry set to the fragment, so this must be destroyed fragment that was recreated by Android. // We should find its corresponding page in our backstack and set it manually. findPageForFragment(fragment, this.frame); @@ -738,8 +861,8 @@ function findPageForFragment(fragment: android.app.Fragment, frame: Frame) { } } if (page) { - (fragment).frame = frame; - (fragment).entry = entry; + (fragment).frame = frame; + (fragment).entry = entry; page[TAG] = fragmentTag; } @@ -753,5 +876,4 @@ function startActivity(activity: android.app.Activity, entry: definition.Navigat intent.setAction(android.content.Intent.ACTION_DEFAULT); // TODO: Put the navigation context (if any) in the intent activity.startActivity(intent); -} - +} \ No newline at end of file