Skip to content

Commit bba7a82

Browse files
author
Hristo Hristov
authored
Disable recycling, refactoring & fixes (NativeScript#4705)
* Added tests for native view recycling Disabled android native view recycling Move toString from view-common to view-base Fix crash on application restore and navigation back on API26 Added setAsRootView method Added missing logo into perf-tests/recycling app * additional fix for image-source-tests. ios is case sensitive. * Add @Private to some internal properties Fix where padding is not respected when background is reset.
1 parent 2701ea3 commit bba7a82

File tree

18 files changed

+551
-270
lines changed

18 files changed

+551
-270
lines changed

apps/app/logo.png

4.8 KB
Loading

apps/app/perf-app/recycling/main-page.ts

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import { StackLayout } from "tns-core-modules/ui/layouts/stack-layout";
22
import { TextView } from "tns-core-modules/ui/text-view";
33
import { Button } from "tns-core-modules/ui/button";
4+
import * as platform from "tns-core-modules/platform";
5+
import * as application from "tns-core-modules/application";
46

57
import * as tests from "./tests";
68

@@ -13,6 +15,28 @@ function getStack(stack: StackLayout): StackLayout {
1315
return p;
1416
}
1517

18+
export function navigatingTo(args) {
19+
// Request permission to write test-results.xml file for API >= 23
20+
if (platform.isAndroid && parseInt(platform.device.sdkVersion) >= 23) {
21+
let handler = (args: application.AndroidActivityRequestPermissionsEventData) => {
22+
application.android.off(application.AndroidApplication.activityRequestPermissionsEvent, handler);
23+
if (args.requestCode === 1234 && args.grantResults.length > 0 && args.grantResults[0] === android.content.pm.PackageManager.PERMISSION_GRANTED) {
24+
console.log("Permission for write to external storage GRANTED!")
25+
} else {
26+
console.log("Permission for write to external storage not granted!");
27+
}
28+
};
29+
30+
application.android.on(application.AndroidApplication.activityRequestPermissionsEvent, handler);
31+
32+
if ((<any>android.support.v4.content.ContextCompat).checkSelfPermission(application.android.currentContext, (<any>android).Manifest.permission.WRITE_EXTERNAL_STORAGE) !== android.content.pm.PackageManager.PERMISSION_GRANTED) {
33+
(<any>android.support.v4.app.ActivityCompat).requestPermissions(application.android.currentContext, [(<any>android).Manifest.permission.WRITE_EXTERNAL_STORAGE], 1234);
34+
}
35+
} else {
36+
console.log("Permission for write to external storage GRANTED!")
37+
}
38+
}
39+
1640
export function onNavigatingFrom() {
1741
clearInterval(runner);
1842
}
@@ -39,7 +63,30 @@ export function onTap(args) {
3963
track(text);
4064

4165
let tasks = [
42-
() => track(tests.testAll(p)),
66+
() => track(tests.testSetup(p)),
67+
() => track(tests.testFlexboxLayout(p)),
68+
() => track(tests.testDockLayout(p)),
69+
() => track(tests.testGridLayout(p)),
70+
() => track(tests.testStackLayout(p)),
71+
() => track(tests.testWrapLayout(p)),
72+
() => track(tests.testAbsoluteLayout(p)),
73+
() => track(tests.testButton(p)),
74+
() => track(tests.testActionBar(p)),
75+
() => track(tests.testActivityIndicator(p)),
76+
() => track(tests.testBorder(p)),
77+
() => track(tests.testContentView(p)),
78+
() => track(tests.testDatePicker(p)),
79+
() => track(tests.testHtmlView(p)),
80+
() => track(tests.testImage(p)),
81+
() => track(tests.testLabel(p)),
82+
() => track(tests.testListPicker(p)),
83+
() => track(tests.testListView(p)),
84+
() => track(tests.testPage(p)),
85+
() => track(tests.testProgress(p)),
86+
() => track(tests.testRepeater(p)),
87+
() => track(tests.testSwitch(p)),
88+
() => track(tests.testTextField(p)),
89+
() => track(tests.testTextView(p)),
4390
() => track("Complete!")
4491
];
4592
let i = 0;
Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1-
<Page xmlns="http://schemas.nativescript.org/tns.xsd" navigatingFrom="onNavigatingFrom">
2-
<GridLayout rows="30,100,*">
3-
<Button text="Start test..." tap="onTap" style="font-size:8" />
1+
<Page xmlns="http://schemas.nativescript.org/tns.xsd"
2+
navigatingFrom="onNavigatingFrom"
3+
navigatingTo="navigatingTo">
4+
<StackLayout>
5+
<Button text="Start test..." tap="onTap" style="font-size:11" />
46
<StackLayout id="placeholder" row="1"/>
5-
<TextView id="result" row="2" />
6-
</GridLayout>
7+
<TextView id="result" row="2" editable="false" />
8+
</StackLayout>
79
</Page>

apps/app/perf-app/recycling/tests.ts

Lines changed: 166 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -26,57 +26,122 @@ import { TextField } from 'tns-core-modules/ui/text-field';
2626
import { TextView } from 'tns-core-modules/ui/text-view';
2727
import { TimePicker } from 'tns-core-modules/ui/time-picker';
2828
import { View } from 'tns-core-modules/ui/core/view';
29-
import { FormattedString, Span } from "tns-core-modules/text/formatted-string";
29+
import { FormattedString, Span } from 'tns-core-modules/text/formatted-string';
30+
import { _getProperties, _getStyleProperties } from 'tns-core-modules/ui/core/properties';
31+
declare var __startCPUProfiler;
32+
declare var __stopCPUProfiler;
3033

31-
export function testAll(layout: StackLayout): string {
34+
const count = 200;
35+
export function testSetup(layout: StackLayout): string {
3236
setupSetters();
33-
const count = 100;
34-
let result = '';
35-
// result += test(layout, () => new FlexboxLayout(), count);
36-
// result += test(layout, () => new ActionBar(), count);
37-
// result += test(layout, () => new ActivityIndicator(), count);
38-
// result += test(layout, () => new Border(), count);
39-
result += test(layout, () => new Button(), count);
40-
// result += test(layout, () => new ContentView(), count);
41-
// result += test(layout, () => new DatePicker(), count);
42-
// result += test(layout, () => new HtmlView(), count);
43-
// result += test(layout, () => new Image(), count);
44-
// result += test(layout, () => new Label(), count);
45-
// result += test(layout, () => new AbsoluteLayout(), count);
46-
// result += test(layout, () => new DockLayout(), count);
47-
// result += test(layout, () => new GridLayout(), count);
48-
// result += test(layout, () => new StackLayout(), count);
49-
// result += test(layout, () => new WrapLayout(), count);
50-
// result += test(layout, () => new ListPicker(), count);
51-
// result += test(layout, () => new ListView(), count);
52-
// result += test(layout, () => new Page(), count);
53-
// result += test(layout, () => new Progress(), count);
54-
// result += test(layout, () => new Repeater(), count);
55-
// result += test(layout, () => new Switch(), count);
56-
// result += test(layout, () => new TextField(), count);
57-
// result += test(layout, () => new TextView(), count);
58-
59-
// Throws
60-
// result += test(layout, () => new TabView(), count);
61-
// result += test(layout, () => new SegmentedBar(), count);
62-
// result += test(layout, () => new TimePicker(), count);
63-
64-
return result;
37+
return '';
38+
}
39+
40+
export function testFlexboxLayout(layout: StackLayout): string {
41+
return test(layout, () => new FlexboxLayout(), count);
42+
}
43+
44+
export function testDockLayout(layout: StackLayout): string {
45+
return test(layout, () => new DockLayout(), count);
46+
}
47+
48+
export function testGridLayout(layout: StackLayout): string {
49+
return test(layout, () => new GridLayout(), count);
50+
}
51+
52+
export function testStackLayout(layout: StackLayout): string {
53+
return test(layout, () => new StackLayout(), count);
54+
}
55+
56+
export function testWrapLayout(layout: StackLayout): string {
57+
return test(layout, () => new WrapLayout(), count);
58+
}
59+
60+
export function testAbsoluteLayout(layout: StackLayout): string {
61+
return test(layout, () => new AbsoluteLayout(), count);
62+
}
63+
64+
export function testButton(layout: StackLayout): string {
65+
return test(layout, () => new Button(), count);
66+
}
67+
68+
export function testActionBar(layout: StackLayout): string {
69+
return test(layout, () => new ActionBar(), count);
70+
}
71+
72+
export function testActivityIndicator(layout: StackLayout): string {
73+
return test(layout, () => new ActivityIndicator(), count);
74+
}
75+
76+
export function testBorder(layout: StackLayout): string {
77+
return test(layout, () => new Border(), count);
78+
}
79+
80+
export function testContentView(layout: StackLayout): string {
81+
return test(layout, () => new ContentView(), count);
82+
}
83+
84+
export function testDatePicker(layout: StackLayout): string {
85+
return test(layout, () => new DatePicker(), count);
86+
}
87+
88+
export function testHtmlView(layout: StackLayout): string {
89+
return test(layout, () => new HtmlView(), count);
90+
}
91+
92+
export function testImage(layout: StackLayout): string {
93+
return test(layout, () => new Image(), count);
94+
}
95+
96+
export function testLabel(layout: StackLayout): string {
97+
return test(layout, () => new Label(), count);
98+
}
99+
100+
export function testListPicker(layout: StackLayout): string {
101+
return test(layout, () => new ListPicker(), count);
102+
}
103+
104+
export function testListView(layout: StackLayout): string {
105+
return test(layout, () => new ListView(), count);
106+
}
107+
108+
export function testPage(layout: StackLayout): string {
109+
return test(layout, () => new Page(), count);
110+
}
111+
112+
export function testProgress(layout: StackLayout): string {
113+
return test(layout, () => new Progress(), count);
114+
}
115+
116+
export function testRepeater(layout: StackLayout): string {
117+
return test(layout, () => new Repeater(), count);
118+
}
119+
120+
export function testSwitch(layout: StackLayout): string {
121+
return test(layout, () => new Switch(), count);
122+
}
123+
124+
export function testTextField(layout: StackLayout): string {
125+
return test(layout, () => new TextField(), count);
126+
}
127+
128+
export function testTextView(layout: StackLayout): string {
129+
return test(layout, () => new TextView(), count);
65130
}
66131

67132
function test(layout: StackLayout, createView: () => View, count: number): string {
68133
const viewMap1 = new Map<string, any>();
69134
const cssMap1 = new Map<string, any>();
70-
135+
71136
viewMap1.set('isEnabled', false);
72-
let result = execute(layout, createView, count, viewMap1, cssMap1) + ', ';
137+
let result = execute(layout, createView, count, viewMap1, cssMap1)
73138

74139
viewMap1.set('text', 'text');
75140
viewMap1.set('automationText', "automationText");
76141
cssMap1.set('width', 100);
77142
cssMap1.set('height', 100);
78143
cssMap1.set('rotate', '90');
79-
result += execute(layout, createView, count, viewMap1, cssMap1) + ', ';
144+
result += execute(layout, createView, count, viewMap1, cssMap1)
80145

81146
viewMap1.set('clipToBounds', false);
82147
viewMap1.set('left', '20');
@@ -91,7 +156,7 @@ function test(layout: StackLayout, createView: () => View, count: number): strin
91156
cssMap1.set('horizontalAlignment', 'center');
92157
cssMap1.set('verticalAlignment', 'center');
93158

94-
result += execute(layout, createView, count, viewMap1, cssMap1) + ', ';
159+
result += execute(layout, createView, count, viewMap1, cssMap1)
95160

96161
viewMap1.set('row', '1');
97162
viewMap1.set('rowSpan', '2');
@@ -110,45 +175,79 @@ function test(layout: StackLayout, createView: () => View, count: number): strin
110175
cssMap1.set('backgroundColor', 'red');
111176
cssMap1.set('backgroundImage', '~/logo.png');
112177

113-
result += execute(layout, createView, count, viewMap1, cssMap1) + ', ';
178+
result += execute(layout, createView, count, viewMap1, cssMap1)
114179
result += execute(layout, createView, count, setters, cssSetters);
115180

116-
return `${createView().typeName}: ${result}\n`;
181+
return `${createView().typeName}: ${result}`;
117182
}
118183

184+
let b = false;
119185
function execute(layout: StackLayout, createView: () => View, count: number,
120186
viewProps: Map<string, any>, cssProps: Map<string, any>): string {
121-
const not = profile(layout, createView, count, false, viewProps, cssProps);
122-
const recycled = profile(layout, createView, count, true, viewProps, cssProps);
123-
124-
const improved = ((not - recycled) / not) * 100;
125-
console.log(`recycled time: ${recycled}`);
126-
console.log(`not recycled time: ${not}`);
127-
const propCount = viewProps.size + cssProps.size;
128-
return `${propCount}: ${improved.toFixed(0)}%`;
187+
188+
gc();
189+
java.lang.System.gc();
190+
gc();
191+
java.lang.System.gc();
192+
// b = !b;
193+
// let not: { time: number, count: number };
194+
let recycled: { time: number, count: number };
195+
// if (b) {
196+
// not = profile(layout, createView, count, false, viewProps, cssProps);
197+
// recycled = profile(layout, createView, count, true, viewProps, cssProps);
198+
// } else {
199+
recycled = profile(layout, createView, count, true, viewProps, cssProps);
200+
// not = profile(layout, createView, count, false, viewProps, cssProps);
201+
// }
202+
203+
// console.log(`recycled: ${recycled.time}`);
204+
// console.log(`not: ${not.time}`);
205+
// const improved = ((not.time - recycled.time) / not.time) * 100;
206+
const propCount = recycled.count;
207+
return `\t${recycled.time.toFixed(0)}`;
129208
}
130209

210+
const props = _getProperties();
211+
const styleProps = _getStyleProperties();
212+
131213
function profile(layout: StackLayout, createView: () => View, count: number, recycle: boolean,
132-
viewProps: Map<string, any>, cssProps: Map<string, any>): number {
214+
viewProps: Map<string, any>, cssProps: Map<string, any>): { time: number, count: number } {
133215

134216
const view = createView();
135217
view.recycleNativeView = recycle ? 'always' : 'never';
136218
const style = view.style;
137-
138-
viewProps.forEach((v, k) => view[k] = v);
139-
cssProps.forEach((v, k) => style[k] = v);
140-
141-
gc();
142-
java.lang.System.gc();
143-
const start = time();
144-
145-
for (let i = 0; i < count; i++) {
146-
layout.addChild(view);
147-
layout.removeChild(view);
219+
// DatePicker throws OOM
220+
const c = view.typeName === 'DatePicker' ? 1 : 5;
221+
let total = 0;
222+
let x = 0;
223+
for (let i = 0; i < c; i++) {
224+
x = 0;
225+
viewProps.forEach((v, k) => {
226+
const p = props.find(vp => (<any>vp).name === k);
227+
// if (p && view[p.setNative]) {
228+
view[k] = v;
229+
x++;
230+
// }
231+
});
232+
233+
cssProps.forEach((v, k) => {
234+
const p = styleProps.find(vp => (<any>vp).name === k);
235+
// if (p && view[p.setNative]) {
236+
style[k] = v;
237+
x++;
238+
// }
239+
});
240+
241+
const start = time();
242+
for (let i = 0; i < count; i++) {
243+
layout.addChild(view);
244+
layout.removeChild(view);
245+
}
246+
const end = time() - start;
247+
total += end;
148248
}
149249

150-
const end = time() - start;
151-
return end;
250+
return { time: total / c, count: x };
152251
}
153252

154253
let setters: Map<string, any>;
@@ -282,11 +381,11 @@ function setupSetters(): void {
282381
setters.set('androidOffscreenTabLimit', '2');
283382

284383
// text-base
285-
const formattedText = new FormattedString();
286-
const span = new Span();
287-
span.text = 'span';
288-
formattedText.spans.push(span);
289-
setters.set('formattedText', formattedText);
384+
// const formattedText = new FormattedString();
385+
// const span = new Span();
386+
// span.text = 'span';
387+
// formattedText.spans.push(span);
388+
// setters.set('formattedText', formattedText);
290389

291390
// text-base
292391
setters.set('secure', 'true');

tests/app/image-source/image-source-snippet.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ export function imageSourceFromAsset(imageAsset){
66
let source = new imageSource.ImageSource();
77
source.fromAsset(imageAsset).then((source) => {
88
let folder = fs.knownFolders.documents().path;
9-
let fileName = "Test.png"
9+
let fileName = "test.png"
1010
let path = fs.path.join(folder, fileName);
1111
let saved = source.saveToFile(path, "png");
1212
if(saved){

0 commit comments

Comments
 (0)