From f0faddb1693bcbae4603429d3898cf14082c9b6a Mon Sep 17 00:00:00 2001 From: Panayot Cankov Date: Thu, 10 Aug 2017 17:28:09 +0300 Subject: [PATCH] Added @profile on several key methods in the Android lifecycle, refactored by extracting into methods a little --- .../application/application.android.ts | 49 ++++--- tns-core-modules/package.json | 5 +- tns-core-modules/ui/builder/builder.ts | 44 +++--- .../component-builder/component-builder.ts | 91 ++++++++----- tns-core-modules/ui/frame/frame-common.ts | 128 ++++++++++-------- tns-core-modules/ui/frame/frame.android.ts | 12 +- tns-core-modules/ui/page/page-common.ts | 4 + .../ui/styling/background.android.ts | 9 +- 8 files changed, 203 insertions(+), 139 deletions(-) diff --git a/tns-core-modules/application/application.android.ts b/tns-core-modules/application/application.android.ts index 5dd1f84330..455ac1874f 100644 --- a/tns-core-modules/application/application.android.ts +++ b/tns-core-modules/application/application.android.ts @@ -180,34 +180,45 @@ global.__onLiveSync = function () { }; function initLifecycleCallbacks() { - // TODO: Verify whether the logic for triggerring application-wide events based on Activity callbacks is working properly + const setThemeOnLaunch = profile("setThemeOnLaunch", (activity: android.app.Activity) => { + // Set app theme after launch screen was used during startup + const activityInfo = activity.getPackageManager().getActivityInfo(activity.getComponentName(), android.content.pm.PackageManager.GET_META_DATA); + if (activityInfo.metaData) { + const setThemeOnLaunch = activityInfo.metaData.getInt("SET_THEME_ON_LAUNCH", -1); + if (setThemeOnLaunch !== -1) { + activity.setTheme(setThemeOnLaunch); + } + } + }); + + const notifyActivityCreated = profile("notifyActivityCreated", function(activity: android.app.Activity, savedInstanceState: android.os.Bundle) { + androidApp.notify({ eventName: ActivityCreated, object: androidApp, activity, bundle: savedInstanceState }); + }); + + const subscribeForGlobalLayout = profile("subscribeForGlobalLayout", function(activity: android.app.Activity) { + const rootView = activity.getWindow().getDecorView().getRootView(); + let onGlobalLayoutListener = new android.view.ViewTreeObserver.OnGlobalLayoutListener({ + onGlobalLayout() { + notify({ eventName: displayedEvent, object: androidApp, activity }); + let viewTreeObserver = rootView.getViewTreeObserver(); + viewTreeObserver.removeOnGlobalLayoutListener(onGlobalLayoutListener); + } + }); + rootView.getViewTreeObserver().addOnGlobalLayoutListener(onGlobalLayoutListener); + }); + const lifecycleCallbacks = new android.app.Application.ActivityLifecycleCallbacks({ onActivityCreated: profile("onActivityCreated", function (activity: android.app.Activity, savedInstanceState: android.os.Bundle) { - // Set app theme after launch screen was used during startup - const activityInfo = activity.getPackageManager().getActivityInfo(activity.getComponentName(), android.content.pm.PackageManager.GET_META_DATA); - if (activityInfo.metaData) { - const setThemeOnLaunch = activityInfo.metaData.getInt("SET_THEME_ON_LAUNCH", -1); - if (setThemeOnLaunch !== -1) { - activity.setTheme(setThemeOnLaunch); - } - } + setThemeOnLaunch(activity); if (!androidApp.startActivity) { androidApp.startActivity = activity; } - androidApp.notify({ eventName: ActivityCreated, object: androidApp, activity, bundle: savedInstanceState }); + notifyActivityCreated(activity, savedInstanceState); if (hasListeners(displayedEvent)) { - const rootView = activity.getWindow().getDecorView().getRootView(); - let onGlobalLayoutListener = new android.view.ViewTreeObserver.OnGlobalLayoutListener({ - onGlobalLayout() { - notify({ eventName: displayedEvent, object: androidApp, activity }); - let viewTreeObserver = rootView.getViewTreeObserver(); - viewTreeObserver.removeOnGlobalLayoutListener(onGlobalLayoutListener); - } - }); - rootView.getViewTreeObserver().addOnGlobalLayoutListener(onGlobalLayoutListener); + subscribeForGlobalLayout(activity); } }), diff --git a/tns-core-modules/package.json b/tns-core-modules/package.json index 5dc0097b7a..580e04610a 100644 --- a/tns-core-modules/package.json +++ b/tns-core-modules/package.json @@ -39,7 +39,10 @@ "snapshot": { "android": { "tns-java-classes": { - "modules": ["ui/frame/activity", "ui/frame/fragment"] + "modules": [ + "ui/frame/activity", + "ui/frame/fragment" + ] } } } diff --git a/tns-core-modules/ui/builder/builder.ts b/tns-core-modules/ui/builder/builder.ts index b43db6faf0..3d3b966f1b 100644 --- a/tns-core-modules/ui/builder/builder.ts +++ b/tns-core-modules/ui/builder/builder.ts @@ -10,6 +10,7 @@ import { isString, isDefined } from "../../utils/types"; import { ComponentModule, setPropertyValue, getComponentModule } from "./component-builder"; import { platformNames, device } from "../../platform"; import { resolveFileName } from "../../file-system/file-name-resolver"; +import { profile } from "tns-core-modules/profiling"; import * as traceModule from "../../trace"; const ios = platformNames.ios.toLowerCase(); @@ -445,28 +446,28 @@ namespace xml2ui { this._state = TemplateParser.State.FINISHED; if (this._setTemplateProperty && this._templateProperty.name in this._templateProperty.parent.component) { - let template = this._build(); + let template = this.buildTemplate(); this._templateProperty.parent.component[this._templateProperty.name] = template; } } } - public _build(): Template { + public buildTemplate(): Template { var context = this._context; var errorFormat = this._templateProperty.errorFormat; var sourceTracker = this._templateProperty.sourceTracker; - var template: Template = () => { + var template: Template = profile("Template()", () => { var start: xml2ui.XmlArgsReplay; var ui: xml2ui.ComponentParser; (start = new xml2ui.XmlArgsReplay(this._recordedXmlStream, errorFormat)) - // No platform filter, it has been filtered allready + // No platform filter, it has been filtered already .pipe(new XmlStateParser(ui = new ComponentParser(context, errorFormat, sourceTracker))); start.replay(); return ui.rootComponentModule.component; - } + }); return template; } } @@ -492,7 +493,7 @@ namespace xml2ui { for (let i = 0; i < this._childParsers.length; i++) { templates.push({ key: this._childParsers[i]["key"], - createView: this._childParsers[i]._build() + createView: this._childParsers[i].buildTemplate() }); } this.templateProperty.parent.component[this.templateProperty.name] = templates; @@ -535,6 +536,22 @@ namespace xml2ui { this.sourceTracker = sourceTracker; } + @profile + private buildComponent(args: xml.ParserEvent): ComponentModule { + if (args.prefix && args.namespace) { + // Custom components + return loadCustomComponent(args.namespace, args.elementName, args.attributes, this.context, this.currentRootView); + } else { + // Default components + let namespace = args.namespace; + if (defaultNameSpaceMatcher.test(namespace || '')) { + //Ignore the default ...tns.xsd namespace URL + namespace = undefined; + } + return getComponentModule(args.elementName, namespace, args.attributes, this.context, this.moduleNamePath); + } + } + public parse(args: xml.ParserEvent): XmlStateConsumer { // Get the current parent. @@ -579,20 +596,7 @@ namespace xml2ui { } else { - var componentModule: ComponentModule; - - if (args.prefix && args.namespace) { - // Custom components - componentModule = loadCustomComponent(args.namespace, args.elementName, args.attributes, this.context, this.currentRootView); - } else { - // Default components - let namespace = args.namespace; - if (defaultNameSpaceMatcher.test(namespace || '')) { - //Ignore the default ...tns.xsd namespace URL - namespace = undefined; - } - componentModule = getComponentModule(args.elementName, namespace, args.attributes, this.context, this.moduleNamePath); - } + var componentModule = this.buildComponent(args); if (componentModule) { this.sourceTracker(componentModule.component, args.position); diff --git a/tns-core-modules/ui/builder/component-builder/component-builder.ts b/tns-core-modules/ui/builder/component-builder/component-builder.ts index 1727fed73a..8a83cfe2fd 100644 --- a/tns-core-modules/ui/builder/component-builder/component-builder.ts +++ b/tns-core-modules/ui/builder/component-builder/component-builder.ts @@ -7,6 +7,7 @@ import { isEventOrGesture } from "../../core/bindable"; import { File, path, knownFolders } from "../../../file-system"; import { getBindingOptions, bindingConstants } from "../binding-builder"; import { resolveFileName } from "../../../file-system/file-name-resolver"; +import { profile } from "tns-core-modules/profiling"; import * as debugModule from "../../../utils/debug"; import * as platform from "../../../platform"; @@ -24,14 +25,9 @@ const CODEFILE = "codeFile"; const CSSFILE = "cssFile"; const IMPORT = "import"; -export function getComponentModule(elementName: string, namespace: string, attributes: Object, exports: Object, moduleNamePath?: string): ComponentModule { +const createComponentInstance = profile("createComponentInstance", (elementName: string, namespace: string): { instance: View, instanceModule: Object } => { var instance: View; var instanceModule: Object; - var componentModule: ComponentModule; - - // Support lower-case-dashed component declaration in the XML (https://github.com/NativeScript/NativeScript/issues/309). - elementName = elementName.split("-").map(s => { return s[0].toUpperCase() + s.substring(1) }).join(""); - // Get module id. var moduleId = MODULES[elementName] || UI_PATH + (elementName.toLowerCase().indexOf("layout") !== -1 ? "layouts/" : "") + @@ -70,7 +66,10 @@ export function getComponentModule(elementName: string, namespace: string, attri throw new debug.ScopeError(ex, "Module '" + moduleId + "' not found for element '" + (namespace ? namespace + ":" : "") + elementName + "'."); } - let cssApplied = false; + return { instance, instanceModule }; +}); + +const getComponentModuleExports = profile("getComponentModuleExports", (instance: View, moduleExports: Object, attributes: Object): Object => { if (attributes) { if (attributes[IMPORT]) { let importPath = attributes[IMPORT].trim(); @@ -79,39 +78,43 @@ export function getComponentModule(elementName: string, namespace: string, attri importPath = path.join(knownFolders.currentApp().path, importPath.replace("~/", "")); } - exports = global.loadModule(importPath); - (instance).exports = exports; + moduleExports = global.loadModule(importPath); + (instance).exports = moduleExports; } - // if (instance instanceof Page) { - if (attributes[CODEFILE]) { - let codeFilePath = attributes[CODEFILE].trim(); - if (codeFilePath.indexOf("~/") === 0) { - codeFilePath = path.join(knownFolders.currentApp().path, codeFilePath.replace("~/", "")); - } + if (attributes[CODEFILE]) { + let codeFilePath = attributes[CODEFILE].trim(); + if (codeFilePath.indexOf("~/") === 0) { + codeFilePath = path.join(knownFolders.currentApp().path, codeFilePath.replace("~/", "")); + } - const codeFilePathWithExt = codeFilePath.indexOf(".js") !== -1 ? codeFilePath : `${codeFilePath}.js`; - if (File.exists(codeFilePathWithExt)) { - exports = global.loadModule(codeFilePath); - (instance).exports = exports; - } else { - throw new Error(`Code file with path "${codeFilePathWithExt}" cannot be found!`); - } + const codeFilePathWithExt = codeFilePath.indexOf(".js") !== -1 ? codeFilePath : `${codeFilePath}.js`; + if (File.exists(codeFilePathWithExt)) { + moduleExports = global.loadModule(codeFilePath); + (instance).exports = moduleExports; + } else { + throw new Error(`Code file with path "${codeFilePathWithExt}" cannot be found!`); } + } + } + return moduleExports; +}); - if (attributes[CSSFILE] && typeof (instance).addCssFile === "function") { - let cssFilePath = attributes[CSSFILE].trim(); - if (cssFilePath.indexOf("~/") === 0) { - cssFilePath = path.join(knownFolders.currentApp().path, cssFilePath.replace("~/", "")); - } - if (File.exists(cssFilePath)) { - (instance).addCssFile(cssFilePath); - cssApplied = true; - } else { - throw new Error(`Css file with path "${cssFilePath}" cannot be found!`); - } +const applyComponentCss = profile("applyComponentCss", (instance: View, moduleNamePath: string, attributes: Object) => { + let cssApplied = false; + if (attributes) { + if (attributes[CSSFILE] && typeof (instance).addCssFile === "function") { + let cssFilePath = attributes[CSSFILE].trim(); + if (cssFilePath.indexOf("~/") === 0) { + cssFilePath = path.join(knownFolders.currentApp().path, cssFilePath.replace("~/", "")); + } + if (File.exists(cssFilePath)) { + (instance).addCssFile(cssFilePath); + cssApplied = true; + } else { + throw new Error(`Css file with path "${cssFilePath}" cannot be found!`); } - // } + } } if (typeof (instance).addCssFile === "function") {//instance instanceof Page) { @@ -129,7 +132,9 @@ export function getComponentModule(elementName: string, namespace: string, attri (instance)._refreshCss(); } } +}); +const applyComponentAttributes = profile("applyComponentAttributes", (instance: View, instanceModule: Object, moduleExports: Object, attributes: Object) => { if (instance && instanceModule) { for (let attr in attributes) { @@ -157,16 +162,28 @@ export function getComponentModule(elementName: string, namespace: string, attri } if (subObj !== undefined && subObj !== null) { - setPropertyValue(subObj, instanceModule, exports, subPropName, attrValue); + setPropertyValue(subObj, instanceModule, moduleExports, subPropName, attrValue); } } else { - setPropertyValue(instance, instanceModule, exports, attr, attrValue); + setPropertyValue(instance, instanceModule, moduleExports, attr, attrValue); } } + } +}); + +export function getComponentModule(elementName: string, namespace: string, attributes: Object, moduleExports: Object, moduleNamePath?: string): ComponentModule { + // Support lower-case-dashed component declaration in the XML (https://github.com/NativeScript/NativeScript/issues/309). + elementName = elementName.split("-").map(s => { return s[0].toUpperCase() + s.substring(1) }).join(""); + const { instance, instanceModule } = createComponentInstance(elementName, namespace); + moduleExports = getComponentModuleExports(instance, moduleExports, attributes); + applyComponentCss(instance, moduleNamePath, attributes); + applyComponentAttributes(instance, instanceModule, moduleExports, attributes); + + var componentModule; + if (instance && instanceModule) { componentModule = { component: instance, exports: instanceModule }; } - return componentModule; } diff --git a/tns-core-modules/ui/frame/frame-common.ts b/tns-core-modules/ui/frame/frame-common.ts index 0a78df5fa1..847a3545f9 100644 --- a/tns-core-modules/ui/frame/frame-common.ts +++ b/tns-core-modules/ui/frame/frame-common.ts @@ -8,6 +8,7 @@ import { resolveFileName } from "../../file-system/file-name-resolver"; import { knownFolders, path } from "../../file-system"; import { parse, loadPage } from "../builder"; import * as application from "../../application"; +import { profile } from "tns-core-modules/profiling"; export { application }; @@ -81,71 +82,58 @@ export function reloadPage(): void { // attach on global, so it can be overwritten in NativeScript Angular (global).__onLiveSyncCore = reloadPage; -export function resolvePageFromEntry(entry: NavigationEntry): Page { - let page: Page; +const entryCreatePage = profile("entry.create", (entry: NavigationEntry): Page => { + const page = entry.create(); - if (entry.create) { - page = entry.create(); + if (!page) { + throw new Error("Failed to create Page with entry.create() function."); + } - if (!page) { - throw new Error("Failed to create Page with entry.create() function."); - } + page._refreshCss(); + return page; +}); + +interface PageModuleExports { + createPage?: () => Page; +} - page._refreshCss(); +const moduleCreatePage = profile("module.createPage", (moduleNamePath: string, moduleExports: PageModuleExports): Page => { + if (traceEnabled()) { + traceWrite("Calling createPage()", traceCategories.Navigation); } - else if (entry.moduleName) { - // Current app full path. - let currentAppPath = knownFolders.currentApp().path; - //Full path of the module = current app full path + module name. - let moduleNamePath = path.join(currentAppPath, entry.moduleName); - traceWrite("frame module path: " + moduleNamePath, traceCategories.Navigation); - traceWrite("frame module module: " + entry.moduleName, traceCategories.Navigation); + var page = moduleExports.createPage(); - let moduleExports; - // web-pack case where developers register their page JS file manually. - if (global.moduleExists(entry.moduleName)) { - if (traceEnabled()) { - traceWrite("Loading pre-registered JS module: " + entry.moduleName, traceCategories.Navigation); - } - moduleExports = global.loadModule(entry.moduleName); - } else { - let moduleExportsResolvedPath = resolveFileName(moduleNamePath, "js"); - if (moduleExportsResolvedPath) { - if (traceEnabled()) { - traceWrite("Loading JS file: " + moduleExportsResolvedPath, traceCategories.Navigation); - } - - // Exclude extension when doing require. - moduleExportsResolvedPath = moduleExportsResolvedPath.substr(0, moduleExportsResolvedPath.length - 3) - moduleExports = global.loadModule(moduleExportsResolvedPath); - } - } + let cssFileName = resolveFileName(moduleNamePath, "css"); + // If there is no cssFile only appCss will be applied at loaded. + if (cssFileName) { + page.addCssFile(cssFileName); + } + return page; +}); - if (moduleExports && moduleExports.createPage) { +const loadPageModule = profile("loadPageModule", (moduleNamePath: string, entry: NavigationEntry): PageModuleExports => { + // web-pack case where developers register their page JS file manually. + if (global.moduleExists(entry.moduleName)) { + if (traceEnabled()) { + traceWrite("Loading pre-registered JS module: " + entry.moduleName, traceCategories.Navigation); + } + return global.loadModule(entry.moduleName); + } else { + let moduleExportsResolvedPath = resolveFileName(moduleNamePath, "js"); + if (moduleExportsResolvedPath) { if (traceEnabled()) { - traceWrite("Calling createPage()", traceCategories.Navigation); + traceWrite("Loading JS file: " + moduleExportsResolvedPath, traceCategories.Navigation); } - page = moduleExports.createPage(); - let cssFileName = resolveFileName(moduleNamePath, "css"); - // If there is no cssFile only appCss will be applied at loaded. - if (cssFileName) { - page.addCssFile(cssFileName); - } - } else { - // cssFileName is loaded inside pageFromBuilder->loadPage - page = pageFromBuilder(moduleNamePath, moduleExports); - } - - if (!page) { - throw new Error("Failed to load Page from entry.moduleName: " + entry.moduleName); + // Exclude extension when doing require. + moduleExportsResolvedPath = moduleExportsResolvedPath.substr(0, moduleExportsResolvedPath.length - 3) + return global.loadModule(moduleExportsResolvedPath); } } + return null; +}); - return page; -} - -function pageFromBuilder(moduleNamePath: string, moduleExports: any): Page { +const pageFromBuilder = profile("pageFromBuilder", (moduleNamePath: string, moduleExports: any): Page => { let page: Page; // Possible XML file path. @@ -165,7 +153,37 @@ function pageFromBuilder(moduleNamePath: string, moduleExports: any): Page { // } return page; -} +}); + +export const resolvePageFromEntry = profile("resolvePageFromEntry", (entry: NavigationEntry): Page => { + let page: Page; + + if (entry.create) { + page = entryCreatePage(entry); + } else if (entry.moduleName) { + // Current app full path. + let currentAppPath = knownFolders.currentApp().path; + //Full path of the module = current app full path + module name. + const moduleNamePath = path.join(currentAppPath, entry.moduleName); + traceWrite("frame module path: " + moduleNamePath, traceCategories.Navigation); + traceWrite("frame module module: " + entry.moduleName, traceCategories.Navigation); + + const moduleExports = loadPageModule(moduleNamePath, entry); + + if (moduleExports && moduleExports.createPage) { + page = moduleCreatePage(moduleNamePath, moduleExports); + } else { + // cssFileName is loaded inside pageFromBuilder->loadPage + page = pageFromBuilder(moduleNamePath, moduleExports); + } + + if (!page) { + throw new Error("Failed to load Page from entry.moduleName: " + entry.moduleName); + } + } + + return page; +}); export interface NavigationContext { entry: BackstackEntry; @@ -377,6 +395,7 @@ export class FrameBase extends CustomLayoutView implements FrameDefinition { } } + @profile private performNavigation(navigationContext: NavigationContext) { let navContext = navigationContext.entry; @@ -391,6 +410,7 @@ export class FrameBase extends CustomLayoutView implements FrameDefinition { this._navigateCore(navContext); } + @profile private performGoBack(navigationContext: NavigationContext) { const navContext = navigationContext.entry; this._onNavigatingTo(navContext, navigationContext.isBackNavigation); diff --git a/tns-core-modules/ui/frame/frame.android.ts b/tns-core-modules/ui/frame/frame.android.ts index cbc3eab31d..528da02c0b 100644 --- a/tns-core-modules/ui/frame/frame.android.ts +++ b/tns-core-modules/ui/frame/frame.android.ts @@ -610,11 +610,8 @@ class ActivityCallbacksImplementation implements AndroidActivityCallbacks { const app = application.android; const intent = activity.getIntent(); - const launchArgs: application.LaunchEventData = { eventName: application.launchEvent, object: app, android: intent }; - application.notify(launchArgs); - + let rootView = this.notifyLaunch(intent); let frameId = -1; - let rootView = launchArgs.root; const extras = intent.getExtras(); // We have extras when we call - new Frame().navigate(); @@ -672,6 +669,13 @@ class ActivityCallbacksImplementation implements AndroidActivityCallbacks { activityInitialized = true; } + @profile + private notifyLaunch(intent: android.content.Intent): View { + const launchArgs: application.LaunchEventData = { eventName: application.launchEvent, object: application.android, android: intent }; + application.notify(launchArgs); + return launchArgs.root; + } + @profile public onSaveInstanceState(activity: android.app.Activity, outState: android.os.Bundle, superFunc: Function): void { superFunc.call(activity, outState); diff --git a/tns-core-modules/ui/page/page-common.ts b/tns-core-modules/ui/page/page-common.ts index f603d174ad..510ff45907 100644 --- a/tns-core-modules/ui/page/page-common.ts +++ b/tns-core-modules/ui/page/page-common.ts @@ -167,6 +167,7 @@ export class PageBase extends ContentView implements PageDefinition { }; } + @profile public onNavigatingTo(context: any, isBackNavigation: boolean, bindingContext?: any) { this._navigationContext = context; @@ -177,14 +178,17 @@ export class PageBase extends ContentView implements PageDefinition { this.notify(this.createNavigatedData(PageBase.navigatingToEvent, isBackNavigation)); } + @profile public onNavigatedTo(isBackNavigation: boolean) { this.notify(this.createNavigatedData(PageBase.navigatedToEvent, isBackNavigation)); } + @profile public onNavigatingFrom(isBackNavigation: boolean) { this.notify(this.createNavigatedData(PageBase.navigatingFromEvent, isBackNavigation)); } + @profile public onNavigatedFrom(isBackNavigation: boolean) { this.notify(this.createNavigatedData(PageBase.navigatedFromEvent, isBackNavigation)); diff --git a/tns-core-modules/ui/styling/background.android.ts b/tns-core-modules/ui/styling/background.android.ts index b9ea5fe197..41ea2d2e40 100644 --- a/tns-core-modules/ui/styling/background.android.ts +++ b/tns-core-modules/ui/styling/background.android.ts @@ -3,6 +3,7 @@ import { CacheLayerType, isDataURI, isFileOrResourcePath, layout, RESOURCE_PREFI import { parse } from "../../css-value"; import { path, knownFolders } from "../../file-system"; import * as application from "../../application"; +import { profile } from "tns-core-modules/profiling"; export * from "./background-common" interface AndroidView { @@ -245,16 +246,16 @@ function onLivesync(args): void { } application.on("livesync", onLivesync); -application.android.on("activityStarted", (args) => { +application.android.on("activityStarted", profile("initImageCache", args => { if (!imageFetcher) { initImageCache(args.activity); } else { imageFetcher.initCache(); } -}); +})); -application.android.on("activityStopped", (args) => { +application.android.on("activityStopped", profile("closeImageCache", args => { if (imageFetcher) { imageFetcher.closeCache(); } -}); \ No newline at end of file +})); \ No newline at end of file