Skip to content

Commit f589fb3

Browse files
authored
Merge pull request #4 from openwebf/feat/js_thread_model
feat: update js_thread_mode readme
2 parents 83c6550 + 3b11beb commit f589fb3

File tree

5 files changed

+222
-18
lines changed

5 files changed

+222
-18
lines changed

demos/js_thread_mode/README.md

+39-10
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,45 @@
1-
# js_thread_mode
1+
# JSRuntime Thread Mode
22

3-
js thread mode demo
3+
This demo illustrates the JSRuntime management in WebF with different threading modes. There are three threading models:
44

5-
## Getting Started
5+
1. Flutter UI Thread.
6+
2. Different dedicated Thread (Different Dedicated threads are used for different WebFPage).
7+
3. Same dedicated Thread (Same Dedicated threads are used for different WebFPage).
68

7-
This project is a starting point for a Flutter application.
9+
For more information about `Dedicated Thread Mode`, see the [documentation](https://openwebf.com/docs/tutorials/performance_optimization/multiple_thread_mode/#sharing-a-single-thread-across-multiple-webf-instances).
810

9-
A few resources to get you started if this is your first Flutter project:
11+
And the demo can test two ways to launch a new FlutterEngine:
1012

11-
- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab)
12-
- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook)
13+
1. Create a FlutterEngine using FlutterEngineGroup.
14+
2. Create a FlutterEngine directly.
1315

14-
For help getting started with Flutter development, view the
15-
[online documentation](https://docs.flutter.dev/), which offers tutorials,
16-
samples, guidance on mobile development, and a full API reference.
16+
17+
# How to use
18+
19+
Just run it like a normal flutter project.
20+
21+
# How to debug
22+
23+
1. Change the webf dependency in `pubspec.yaml` to a local path.
24+
2. View the creation and destruction logs of JSRuntime on different threads. (Refer to [the test code](https://github.com/openwebf/webf/commit/1b30372558b4b7d4556a0451618dbf3e2250955e))
25+
26+
If we use the webf test code above, interact with the test page as follows:
27+
28+
1. On the home page click the button "FlutterEngineGroup (supported iOS/Android) ".
29+
2. Click the button "Different dedicated thread" on the new page.
30+
3. The Webf test page loads successfully Back to the home page.
31+
32+
We can see the following Log:
33+
34+
35+
```
36+
[JSRuntime Lifecycle] threadId 0x16c8e3000 DartIsolateContext construction
37+
[JSRuntime Lifecycle] threadId 0x3090cb000 InitializeJSRuntime Begin
38+
[JSRuntime Lifecycle] threadId 0x3090cb000 InitializeJSRuntime Done
39+
[JSRuntime Lifecycle] threadId 0x3090cb000 FinalizeJSRuntime Begin
40+
[JSRuntime Lifecycle] threadId 0x3090cb000 FinalizeJSRuntime Done
41+
[JSRuntime Lifecycle] threadId 0x16c8e3000 FinalizeJSRuntime Begin
42+
[JSRuntime Lifecycle] threadId 0x16c8e3000 FinalizeJSRuntime Fail. runtime_ == nullptr
43+
```
44+
45+
This indicates that neither the DartIsolateContext construct nor the destructor creates the JSRuntime.

demos/js_thread_mode/ios/Runner.xcodeproj/project.pbxproj

+4
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
/* Begin PBXBuildFile section */
1010
001078082C660B33005FA74A /* WebFFlutterViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 001078072C660B33005FA74A /* WebFFlutterViewController.swift */; };
11+
00B9B3542C69FB9C00CA5A49 /* WebFDoubleFlutterViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00B9B3532C69FB9C00CA5A49 /* WebFDoubleFlutterViewController.swift */; };
1112
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
1213
1568F40A0DDB1345025BA771 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 872E661C9B11EA53E4D00FC2 /* Pods_RunnerTests.framework */; };
1314
331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; };
@@ -44,6 +45,7 @@
4445

4546
/* Begin PBXFileReference section */
4647
001078072C660B33005FA74A /* WebFFlutterViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebFFlutterViewController.swift; sourceTree = "<group>"; };
48+
00B9B3532C69FB9C00CA5A49 /* WebFDoubleFlutterViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebFDoubleFlutterViewController.swift; sourceTree = "<group>"; };
4749
036DF617EDBD2EEEC487B26C /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = "<group>"; };
4850
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
4951
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
@@ -154,6 +156,7 @@
154156
74858FAE1ED2DC5600515810 /* AppDelegate.swift */,
155157
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */,
156158
001078072C660B33005FA74A /* WebFFlutterViewController.swift */,
159+
00B9B3532C69FB9C00CA5A49 /* WebFDoubleFlutterViewController.swift */,
157160
);
158161
path = Runner;
159162
sourceTree = "<group>";
@@ -379,6 +382,7 @@
379382
isa = PBXSourcesBuildPhase;
380383
buildActionMask = 2147483647;
381384
files = (
385+
00B9B3542C69FB9C00CA5A49 /* WebFDoubleFlutterViewController.swift in Sources */,
382386
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,
383387
001078082C660B33005FA74A /* WebFFlutterViewController.swift in Sources */,
384388
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,

demos/js_thread_mode/ios/Runner/AppDelegate.swift

+18
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@ import UIKit
2424
// 执行相应操作
2525
self?.createNewEngineWithoutGroup()
2626
result(nil)
27+
} else if call.method == "newDoubleEngine" {
28+
// 执行相应操作
29+
self?.newDoubleEngine()
30+
result(nil)
2731
} else {
2832
result(FlutterMethodNotImplemented)
2933
}
@@ -59,8 +63,22 @@ import UIKit
5963
window?.rootViewController?.present(flutterVC, animated: true, completion: nil)
6064
}
6165

66+
private func newDoubleEngine() {
67+
let newEngine1 = engineGroup?.makeEngine(withEntrypoint: "mainDoubleEngine", libraryURI: nil)
68+
if ((newEngine1) != nil) {
69+
GeneratedPluginRegistrant.register(with: newEngine1!)
70+
}
71+
let newEngine2 = engineGroup?.makeEngine(withEntrypoint: "mainDoubleEngine", libraryURI: nil)
72+
if ((newEngine2) != nil) {
73+
GeneratedPluginRegistrant.register(with: newEngine2!)
74+
}
75+
let flutterVC = WebFDoubleFlutterViewController(engine1: newEngine1!, engine2: newEngine2!)
76+
window?.rootViewController?.present(flutterVC, animated: true, completion: nil)
77+
}
78+
6279
private func destoryEngine(engineId: Int) {
6380
let flutterEngine = flutterEngines[engineId]
6481
flutterEngine?.destroyContext();
82+
flutterEngines.removeValue(forKey: engineId)
6583
}
6684
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import UIKit
2+
import Flutter
3+
4+
class WebFDoubleFlutterViewController: UIViewController {
5+
private let flutterVC1: WebFFlutterViewController
6+
private let flutterVC2: WebFFlutterViewController
7+
8+
init(engine1: FlutterEngine, engine2: FlutterEngine) {
9+
flutterVC1 = WebFFlutterViewController(engine: engine1, nibName: nil, bundle: nil)
10+
flutterVC2 = WebFFlutterViewController(engine: engine2, nibName: nil, bundle: nil)
11+
super.init(nibName: nil, bundle: nil)
12+
}
13+
14+
required init?(coder: NSCoder) {
15+
fatalError("init(coder:) has not been implemented")
16+
}
17+
18+
override func viewDidLoad() {
19+
super.viewDidLoad()
20+
21+
addChild(flutterVC1)
22+
addChild(flutterVC2)
23+
let safeFrame = self.view.frame
24+
let halfHeight = self.view.frame.height / 2.0
25+
flutterVC1.view.frame = CGRect(
26+
x: safeFrame.minX, y: safeFrame.minY, width: safeFrame.width, height: halfHeight)
27+
flutterVC2.view.frame = CGRect(
28+
x: safeFrame.minX, y: flutterVC1.view.frame.maxY, width: safeFrame.width, height: halfHeight)
29+
self.view.addSubview(flutterVC1.view)
30+
self.view.addSubview(flutterVC2.view)
31+
flutterVC1.didMove(toParent: self)
32+
flutterVC2.didMove(toParent: self)
33+
}
34+
}

demos/js_thread_mode/lib/main.dart

+127-8
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,29 @@ import 'package:webf/devtools.dart';
99
import 'package:webf/webf.dart';
1010

1111
@pragma('vm:entry-point')
12-
void mainWithoutNewEngine() => runApp(MyApp(showNewEngineButton: false));
12+
void mainWithoutNewEngine() => runApp(MyApp(
13+
showNewEngineButton: false,
14+
disableDoublePage: false,
15+
));
16+
17+
@pragma('vm:entry-point')
18+
void mainDoubleEngine() =>
19+
runApp(MyApp(showNewEngineButton: false, disableDoublePage: true));
1320

1421
void main() {
1522
runApp(MyApp(
1623
showNewEngineButton: true,
24+
disableDoublePage: false,
1725
));
1826
}
1927

2028
class MyApp extends StatelessWidget {
21-
MyApp({super.key, required this.showNewEngineButton});
29+
MyApp(
30+
{super.key,
31+
required this.showNewEngineButton,
32+
required this.disableDoublePage});
2233
bool showNewEngineButton = true;
34+
bool disableDoublePage = false;
2335

2436
// This widget is the root of your application.
2537
@override
@@ -29,20 +41,29 @@ class MyApp extends StatelessWidget {
2941
// theme: ThemeData.dark(),
3042
debugShowCheckedModeBanner: false,
3143
home: FirstPage(
32-
title: 'Landing Bay', showNewEngineButton: showNewEngineButton),
44+
title: 'Landing Bay',
45+
showNewEngineButton: showNewEngineButton,
46+
disableDoublePage: disableDoublePage,
47+
),
3348
);
3449
}
3550
}
3651

3752
class FirstPage extends StatefulWidget {
3853
FirstPage(
39-
{super.key, required this.title, required this.showNewEngineButton});
54+
{super.key,
55+
required this.title,
56+
required this.showNewEngineButton,
57+
required this.disableDoublePage});
4058
final String title;
41-
final bool showNewEngineButton;
59+
bool showNewEngineButton = true;
60+
bool disableDoublePage = false;
4261

4362
@override
4463
State<StatefulWidget> createState() {
45-
return FirstPageState(showNewEngineButton: showNewEngineButton);
64+
return FirstPageState(
65+
showNewEngineButton: showNewEngineButton,
66+
disableDoublePage: disableDoublePage);
4667
}
4768
}
4869

@@ -53,10 +74,12 @@ enum ThreadMode {
5374
}
5475

5576
class FirstPageState extends State<FirstPage> {
56-
FirstPageState({required this.showNewEngineButton});
77+
FirstPageState(
78+
{required this.showNewEngineButton, required this.disableDoublePage});
5779

5880
static const platform = MethodChannel('com.example.flutter/new_engine');
59-
final bool showNewEngineButton;
81+
bool showNewEngineButton = true;
82+
bool disableDoublePage = false;
6083

6184
DedicatedThreadGroup threadGroup = DedicatedThreadGroup();
6285

@@ -113,7 +136,54 @@ class FirstPageState extends State<FirstPage> {
113136
),
114137
];
115138

139+
if (disableDoublePage == false) {
140+
buttons.add(const SizedBox(height: 20));
141+
buttons.add(ElevatedButton(
142+
onPressed: () {
143+
Navigator.push(context, MaterialPageRoute(builder: (context) {
144+
return WebFDemoTwoPage(
145+
controller1: webFController(ThreadMode.differentDedicated),
146+
controller2: webFController(ThreadMode.differentDedicated));
147+
}));
148+
},
149+
child: const Text('Different dedicated thread (Double page)'),
150+
));
151+
buttons.add(const SizedBox(height: 20));
152+
buttons.add(ElevatedButton(
153+
onPressed: () {
154+
Navigator.push(context, MaterialPageRoute(builder: (context) {
155+
return WebFDemoTwoPage(
156+
controller1: webFController(ThreadMode.sameDedicated),
157+
controller2: webFController(ThreadMode.sameDedicated));
158+
}));
159+
},
160+
child: const Text('Same dedicated thread (Double page)'),
161+
));
162+
buttons.add(const SizedBox(height: 20));
163+
buttons.add(ElevatedButton(
164+
onPressed: () {
165+
Navigator.push(context, MaterialPageRoute(builder: (context) {
166+
return WebFDemoTwoPage(
167+
controller1: webFController(ThreadMode.sameSingle),
168+
controller2: webFController(ThreadMode.sameSingle));
169+
}));
170+
},
171+
child: const Text('Same single thread (Double page)'),
172+
));
173+
}
174+
116175
if (showNewEngineButton) {
176+
buttons.add(const SizedBox(height: 20));
177+
buttons.add(ElevatedButton(
178+
onPressed: () async {
179+
try {
180+
await platform.invokeMethod('newDoubleEngine');
181+
} on PlatformException catch (e) {
182+
print("Failed to create engine: '${e.message}'.");
183+
}
184+
},
185+
child: const Text('Double Engine(supported iOS)'),
186+
));
117187
buttons.add(const SizedBox(height: 20));
118188
buttons.add(ElevatedButton(
119189
onPressed: () async {
@@ -181,3 +251,52 @@ class _WebFDemoState extends State<WebFDemo> {
181251
));
182252
}
183253
}
254+
255+
class WebFDemoTwoPage extends StatefulWidget {
256+
final WebFController controller1;
257+
final WebFController controller2;
258+
259+
const WebFDemoTwoPage({
260+
super.key,
261+
required this.controller1,
262+
required this.controller2,
263+
});
264+
265+
@override
266+
_WebFDemoTwoPageState createState() => _WebFDemoTwoPageState();
267+
}
268+
269+
class _WebFDemoTwoPageState extends State<WebFDemoTwoPage> {
270+
@override
271+
void dispose() {
272+
widget.controller1.dispose();
273+
widget.controller2.dispose();
274+
super.dispose();
275+
}
276+
277+
@override
278+
Widget build(BuildContext context) {
279+
return Scaffold(
280+
appBar: AppBar(
281+
title: const Text('WebF Demo'),
282+
),
283+
body: LayoutBuilder(
284+
builder: (BuildContext context, BoxConstraints constraints) {
285+
double halfHeight = constraints.maxHeight / 2;
286+
return Column(
287+
children: [
288+
SizedBox(
289+
height: halfHeight,
290+
child: WebF(controller: widget.controller1),
291+
),
292+
SizedBox(
293+
height: halfHeight,
294+
child: WebF(controller: widget.controller2),
295+
),
296+
],
297+
);
298+
},
299+
),
300+
);
301+
}
302+
}

0 commit comments

Comments
 (0)