Skip to content

Commit 6380b56

Browse files
authored
[pigeon] implements background platform channels support (#1022)
* [pigeon] added support for background platform channels * added objc support * updated the docstring * added java support * added error if taskqueues are used with flutter apis * added compilation test for java * added ios compilation test * updated readme and version * stuart feedback
1 parent d0d9eac commit 6380b56

File tree

14 files changed

+246
-13
lines changed

14 files changed

+246
-13
lines changed

packages/pigeon/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## 2.0.1
2+
3+
* Adds support for TaskQueues for serial background execution.
4+
15
## 2.0.0
26

37
* Implements nullable parameters.

packages/pigeon/README.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,22 @@ abstract class Api {
165165
}
166166
```
167167

168+
### TaskQueues
169+
170+
When targeting a Flutter version that supports the
171+
[TaskQueue API](https://docs.flutter.dev/development/platform-integration/platform-channels?tab=type-mappings-kotlin-tab#channels-and-platform-threading)
172+
the threading model for handling HostApi methods can be selected with the
173+
`TaskQueue` annotation:
174+
175+
```dart
176+
@HostApi()
177+
abstract class Api2Host {
178+
@TaskQueue(type: TaskQueueType.serialBackgroundThread)
179+
int add(int x, int y);
180+
}
181+
```
182+
183+
168184
## Feedback
169185

170186
File an issue in [flutter/flutter](https://github.com/flutter/flutter) with the

packages/pigeon/lib/ast.dart

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import 'package:collection/collection.dart' show ListEquality;
66
import 'package:meta/meta.dart';
7+
import 'pigeon_lib.dart';
78

89
final Function _listEquals = const ListEquality<dynamic>().equals;
910

@@ -29,6 +30,7 @@ class Method extends Node {
2930
this.isAsynchronous = false,
3031
this.offset,
3132
this.objcSelector = '',
33+
this.taskQueueType = TaskQueueType.serial,
3234
});
3335

3436
/// The name of the method.
@@ -49,6 +51,9 @@ class Method extends Node {
4951
/// An override for the generated objc selector (ex. "divideNumber:by:").
5052
String objcSelector;
5153

54+
/// Specifies how handlers are dispatched with respect to threading.
55+
TaskQueueType taskQueueType;
56+
5257
@override
5358
String toString() {
5459
final String objcSelectorStr =

packages/pigeon/lib/generator_tools.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import 'dart:mirrors';
88
import 'ast.dart';
99

1010
/// The current version of pigeon. This must match the version in pubspec.yaml.
11-
const String pigeonVersion = '2.0.0';
11+
const String pigeonVersion = '2.0.1';
1212

1313
/// Read all the content from [stdin] to a String.
1414
String readStdin() {

packages/pigeon/lib/java_generator.dart

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@
22
// Use of this source code is governed by a BSD-style license that can be
33
// found in the LICENSE file.
44

5-
import 'package:pigeon/functional.dart';
6-
75
import 'ast.dart';
6+
import 'functional.dart';
87
import 'generator_tools.dart';
8+
import 'pigeon_lib.dart';
99

1010
/// Options that control how Java code will be generated.
1111
class JavaOptions {
@@ -152,11 +152,22 @@ void _writeHostApi(Indent indent, Api api) {
152152
final String channelName = makeChannelName(api, method);
153153
indent.write('');
154154
indent.scoped('{', '}', () {
155+
String? taskQueue;
156+
if (method.taskQueueType != TaskQueueType.serial) {
157+
taskQueue = 'taskQueue';
158+
indent.writeln(
159+
'BinaryMessenger.TaskQueue taskQueue = binaryMessenger.makeBackgroundTaskQueue();');
160+
}
155161
indent.writeln('BasicMessageChannel<Object> channel =');
156162
indent.inc();
157163
indent.inc();
158-
indent.writeln(
159-
'new BasicMessageChannel<>(binaryMessenger, "$channelName", getCodec());');
164+
indent.write(
165+
'new BasicMessageChannel<>(binaryMessenger, "$channelName", getCodec()');
166+
if (taskQueue != null) {
167+
indent.addln(', $taskQueue);');
168+
} else {
169+
indent.addln(');');
170+
}
160171
indent.dec();
161172
indent.dec();
162173
indent.write('if (api != null) ');

packages/pigeon/lib/objc_generator.dart

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// found in the LICENSE file.
44

55
import 'package:pigeon/functional.dart';
6+
import 'package:pigeon/pigeon_lib.dart';
67

78
import 'ast.dart';
89
import 'generator_tools.dart';
@@ -588,15 +589,20 @@ void _writeHostApiSource(Indent indent, ObjcOptions options, Api api) {
588589
assert(api.location == ApiLocation.host);
589590
final String apiName = _className(options.prefix, api.name);
590591

591-
void writeChannelAllocation(Method func, String varName) {
592+
void writeChannelAllocation(Method func, String varName, String? taskQueue) {
592593
indent.writeln('FlutterBasicMessageChannel *$varName =');
593594
indent.inc();
594-
indent.writeln('[FlutterBasicMessageChannel');
595+
indent.writeln('[[FlutterBasicMessageChannel alloc]');
595596
indent.inc();
596-
indent.writeln('messageChannelWithName:@"${makeChannelName(api, func)}"');
597+
indent.writeln('initWithName:@"${makeChannelName(api, func)}"');
597598
indent.writeln('binaryMessenger:binaryMessenger');
598-
indent
599-
.writeln('codec:${_getCodecGetterName(options.prefix, api.name)}()];');
599+
indent.write('codec:${_getCodecGetterName(options.prefix, api.name)}()');
600+
if (taskQueue != null) {
601+
indent.addln('');
602+
indent.writeln('taskQueue:$taskQueue];');
603+
} else {
604+
indent.writeln('];');
605+
}
600606
indent.dec();
601607
indent.dec();
602608
}
@@ -698,7 +704,13 @@ void _writeHostApiSource(Indent indent, ObjcOptions options, Api api) {
698704
for (final Method func in api.methods) {
699705
indent.write('');
700706
indent.scoped('{', '}', () {
701-
writeChannelAllocation(func, channelName);
707+
String? taskQueue;
708+
if (func.taskQueueType != TaskQueueType.serial) {
709+
taskQueue = 'taskQueue';
710+
indent.writeln(
711+
'NSObject<FlutterTaskQueue> *$taskQueue = [binaryMessenger makeBackgroundTaskQueue];');
712+
}
713+
writeChannelAllocation(func, channelName, taskQueue);
702714
indent.write('if (api) ');
703715
indent.scoped('{', '}', () {
704716
writeChannelApiBinding(func, channelName);

packages/pigeon/lib/pigeon_lib.dart

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,33 @@ class ObjCSelector {
9494
final String value;
9595
}
9696

97+
/// Type of TaskQueue which determines how handlers are dispatched for
98+
/// HostApi's.
99+
enum TaskQueueType {
100+
/// Handlers are invoked serially on the default thread. This is the value if
101+
/// unspecified.
102+
serial,
103+
104+
/// Handlers are invoked serially on a background thread.
105+
serialBackgroundThread,
106+
107+
// TODO(gaaclarke): Add support for concurrent task queues.
108+
// /// Handlers are invoked concurrently on a background thread.
109+
// concurrentBackgroundThread,
110+
}
111+
112+
/// Metadata annotation to control how handlers are dispatched for HostApi's.
113+
/// Note that the TaskQueue API might not be available on the target version of
114+
/// Flutter, see also:
115+
/// https://docs.flutter.dev/development/platform-integration/platform-channels.
116+
class TaskQueue {
117+
/// The constructor for a TaskQueue.
118+
const TaskQueue({required this.type});
119+
120+
/// The type of the TaskQueue.
121+
final TaskQueueType type;
122+
}
123+
97124
/// Represents an error as a result of parsing and generating code.
98125
class Error {
99126
/// Parametric constructor for Error.
@@ -507,6 +534,13 @@ List<Error> _validateAst(Root root, String source) {
507534
));
508535
}
509536
}
537+
if (method.taskQueueType != TaskQueueType.serial &&
538+
api.location != ApiLocation.host) {
539+
result.add(Error(
540+
message: 'Unsupported TaskQueue specification on ${method.name}',
541+
lineNumber: _calculateLineNumberNullable(source, method.offset),
542+
));
543+
}
510544
}
511545
}
512546

@@ -757,6 +791,18 @@ class _RootBuilder extends dart_ast_visitor.RecursiveAstVisitor<Object?> {
757791
return null;
758792
}
759793

794+
T? _stringToEnum<T>(List<T> values, String? str) {
795+
if (str == null) {
796+
return null;
797+
}
798+
for (final T value in values) {
799+
if (value.toString() == str) {
800+
return value;
801+
}
802+
}
803+
return null;
804+
}
805+
760806
@override
761807
Object? visitMethodDeclaration(dart_ast.MethodDeclaration node) {
762808
final dart_ast.FormalParameterList parameters = node.parameters!;
@@ -770,6 +816,17 @@ class _RootBuilder extends dart_ast_visitor.RecursiveAstVisitor<Object?> {
770816
.asNullable<dart_ast.SimpleStringLiteral>()
771817
?.value ??
772818
'';
819+
final dart_ast.ArgumentList? taskQueueArguments =
820+
_findMetadata(node.metadata, 'TaskQueue')?.arguments;
821+
final String? taskQueueTypeName = taskQueueArguments == null
822+
? null
823+
: getFirstChildOfType<dart_ast.NamedExpression>(taskQueueArguments)
824+
?.expression
825+
.asNullable<dart_ast.PrefixedIdentifier>()
826+
?.name;
827+
final TaskQueueType taskQueueType =
828+
_stringToEnum(TaskQueueType.values, taskQueueTypeName) ??
829+
TaskQueueType.serial;
773830
if (_currentApi != null) {
774831
// Methods without named return types aren't supported.
775832
final dart_ast.TypeAnnotation returnType = node.returnType!;
@@ -785,7 +842,8 @@ class _RootBuilder extends dart_ast_visitor.RecursiveAstVisitor<Object?> {
785842
arguments: arguments,
786843
isAsynchronous: isAsynchronous,
787844
objcSelector: objcSelector,
788-
offset: node.offset));
845+
offset: node.offset,
846+
taskQueueType: taskQueueType));
789847
} else if (_currentClass != null) {
790848
_errors.add(Error(
791849
message:
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
import 'package:pigeon/pigeon.dart';
6+
7+
@HostApi()
8+
abstract class Api2Host {
9+
@TaskQueue(type: TaskQueueType.serialBackgroundThread)
10+
int add(int x, int y);
11+
}

packages/pigeon/platform_tests/ios_unit_tests/ios/Runner.xcodeproj/project.pbxproj

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
0D02163D27BC7B48009BD76F /* nullable_returns.gen.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D02163C27BC7B48009BD76F /* nullable_returns.gen.m */; };
1111
0D36469D27C6BE3C0069B7BF /* NullableReturnsTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D36469C27C6BE3C0069B7BF /* NullableReturnsTest.m */; };
1212
0D3646A027C6DCEC0069B7BF /* MockBinaryMessenger.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D36469F27C6DCEC0069B7BF /* MockBinaryMessenger.m */; };
13+
0D21E59A27D0502D0051D07D /* background_platform_channels.gen.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D21E59827D0502D0051D07D /* background_platform_channels.gen.m */; };
1314
0D50127523FF75B100CD5B95 /* RunnerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D50127423FF75B100CD5B95 /* RunnerTests.m */; };
1415
0D6FD3C526A76D400046D8BD /* primitive.gen.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D6FD3C426A76D400046D8BD /* primitive.gen.m */; };
1516
0D6FD3C726A777C00046D8BD /* PrimitiveTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D6FD3C626A777C00046D8BD /* PrimitiveTest.m */; };
@@ -72,6 +73,8 @@
7273
0D36469C27C6BE3C0069B7BF /* NullableReturnsTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NullableReturnsTest.m; sourceTree = "<group>"; };
7374
0D36469E27C6DCEC0069B7BF /* MockBinaryMessenger.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MockBinaryMessenger.h; sourceTree = "<group>"; };
7475
0D36469F27C6DCEC0069B7BF /* MockBinaryMessenger.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MockBinaryMessenger.m; sourceTree = "<group>"; };
76+
0D21E59827D0502D0051D07D /* background_platform_channels.gen.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = background_platform_channels.gen.m; sourceTree = "<group>"; };
77+
0D21E59927D0502D0051D07D /* background_platform_channels.gen.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = background_platform_channels.gen.h; sourceTree = "<group>"; };
7578
0D50127223FF75B100CD5B95 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
7679
0D50127423FF75B100CD5B95 /* RunnerTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RunnerTests.m; sourceTree = "<group>"; };
7780
0D50127623FF75B100CD5B95 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
@@ -204,6 +207,8 @@
204207
97C146F01CF9000F007C117D /* Runner */ = {
205208
isa = PBXGroup;
206209
children = (
210+
0D21E59927D0502D0051D07D /* background_platform_channels.gen.h */,
211+
0D21E59827D0502D0051D07D /* background_platform_channels.gen.m */,
207212
0D02163B27BC7B48009BD76F /* nullable_returns.gen.h */,
208213
0D02163C27BC7B48009BD76F /* nullable_returns.gen.m */,
209214
0DBD8C40279B741800E4FDBA /* non_null_fields.gen.h */,
@@ -424,6 +429,7 @@
424429
0DD2E6BC2684031300A7D764 /* host2flutter.gen.m in Sources */,
425430
0DA5DFD626CC39D600D2354B /* multiple_arity.gen.m in Sources */,
426431
0DD2E6BE2684031300A7D764 /* message.gen.m in Sources */,
432+
0D21E59A27D0502D0051D07D /* background_platform_channels.gen.m in Sources */,
427433
0DD2E6BA2684031300A7D764 /* void_arg_host.gen.m in Sources */,
428434
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
429435
0D02163D27BC7B48009BD76F /* nullable_returns.gen.m in Sources */,

packages/pigeon/pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ name: pigeon
22
description: Code generator tool to make communication between Flutter and the host platform type-safe and easier.
33
repository: https://github.com/flutter/packages/tree/main/packages/pigeon
44
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3Apigeon
5-
version: 2.0.0 # This must match the version in lib/generator_tools.dart
5+
version: 2.0.1 # This must match the version in lib/generator_tools.dart
66

77
environment:
88
sdk: ">=2.12.0 <3.0.0"

0 commit comments

Comments
 (0)