33// found in the LICENSE file.
44
55import 'dart:async' ;
6+ import 'dart:developer' ;
7+ import 'dart:ui' show PlatformMessageResponseCallback;
68
79import 'package:flutter/foundation.dart' ;
810
911import 'binary_messenger.dart' ;
1012import 'binding.dart' ;
13+ import 'debug.dart' show debugProfilePlatformChannels;
1114import 'message_codec.dart' ;
1215import 'message_codecs.dart' ;
1316
17+ bool _debugProfilePlatformChannelsIsRunning = false ;
18+ const Duration _debugProfilePlatformChannelsRate = Duration (seconds: 1 );
19+ final Expando <BinaryMessenger > _debugBinaryMessengers = Expando <BinaryMessenger >();
20+
21+ class _ProfiledBinaryMessenger implements BinaryMessenger {
22+ const _ProfiledBinaryMessenger (this .proxy, this .channelTypeName, this .codecTypeName);
23+ final BinaryMessenger proxy;
24+ final String channelTypeName;
25+ final String codecTypeName;
26+
27+ @override
28+ Future <void > handlePlatformMessage (String channel, ByteData ? data, PlatformMessageResponseCallback ? callback) {
29+ return proxy.handlePlatformMessage (channel, data, callback);
30+ }
31+
32+ Future <ByteData ?>? sendWithPostfix (String channel, String postfix, ByteData ? message) async {
33+ final TimelineTask task = TimelineTask ();
34+ _debugRecordUpStream (channelTypeName, '$channel $postfix ' , codecTypeName, message);
35+ task.start ('Platform Channel send $channel $postfix ' );
36+ final ByteData ? result;
37+ try {
38+ result = await proxy.send (channel, message);
39+ } finally {
40+ task.finish ();
41+ }
42+ _debugRecordDownStream (channelTypeName, '$channel $postfix ' , codecTypeName, result);
43+ return result;
44+ }
45+
46+ @override
47+ Future <ByteData ?>? send (String channel, ByteData ? message) =>
48+ sendWithPostfix (channel, '' , message);
49+
50+ @override
51+ void setMessageHandler (String channel, MessageHandler ? handler) {
52+ proxy.setMessageHandler (channel, handler);
53+ }
54+ }
55+
56+ class _PlatformChannelStats {
57+ _PlatformChannelStats (this .channel, this .codec, this .type);
58+
59+ final String channel;
60+ final String codec;
61+ final String type;
62+
63+ int _upCount = 0 ;
64+ int _upBytes = 0 ;
65+ int get upBytes => _upBytes;
66+ void addUpStream (int bytes) {
67+ _upCount += 1 ;
68+ _upBytes += bytes;
69+ }
70+
71+ int _downCount = 0 ;
72+ int _downBytes = 0 ;
73+ int get downBytes => _downBytes;
74+ void addDownStream (int bytes) {
75+ _downCount += 1 ;
76+ _downBytes += bytes;
77+ }
78+
79+ double get averageUpPayload => _upBytes / _upCount;
80+ double get averageDownPayload => _downBytes / _downCount;
81+ }
82+
83+ final Map <String , _PlatformChannelStats > _debugProfilePlatformChannelsStats = < String , _PlatformChannelStats > {};
84+
85+ Future <void > _debugLaunchProfilePlatformChannels () async {
86+ if (! _debugProfilePlatformChannelsIsRunning) {
87+ _debugProfilePlatformChannelsIsRunning = true ;
88+ await Future <dynamic >.delayed (_debugProfilePlatformChannelsRate);
89+ _debugProfilePlatformChannelsIsRunning = false ;
90+ final StringBuffer log = StringBuffer ();
91+ log.writeln ('Platform Channel Stats:' );
92+ final List <_PlatformChannelStats > allStats =
93+ _debugProfilePlatformChannelsStats.values.toList ();
94+ // Sort highest combined bandwidth first.
95+ allStats.sort ((_PlatformChannelStats x, _PlatformChannelStats y) =>
96+ (y.upBytes + y.downBytes) - (x.upBytes + x.downBytes));
97+ for (final _PlatformChannelStats stats in allStats) {
98+ log.writeln (
99+ ' (name:"${stats .channel }" type:"${stats .type }" codec:"${stats .codec }" upBytes:${stats .upBytes } upBytes_avg:${stats .averageUpPayload .toStringAsFixed (1 )} downBytes:${stats .downBytes } downBytes_avg:${stats .averageDownPayload .toStringAsFixed (1 )})' );
100+ }
101+ debugPrint (log.toString ());
102+ _debugProfilePlatformChannelsStats.clear ();
103+ }
104+ }
105+
106+ void _debugRecordUpStream (String channelTypeName, String name,
107+ String codecTypeName, ByteData ? bytes) {
108+ final _PlatformChannelStats stats =
109+ _debugProfilePlatformChannelsStats[name] ?? =
110+ _PlatformChannelStats (name, codecTypeName, channelTypeName);
111+ stats.addUpStream (bytes? .lengthInBytes ?? 0 );
112+ _debugLaunchProfilePlatformChannels ();
113+ }
114+
115+ void _debugRecordDownStream (String channelTypeName, String name,
116+ String codecTypeName, ByteData ? bytes) {
117+ final _PlatformChannelStats stats =
118+ _debugProfilePlatformChannelsStats[name] ?? =
119+ _PlatformChannelStats (name, codecTypeName, channelTypeName);
120+ stats.addDownStream (bytes? .lengthInBytes ?? 0 );
121+ _debugLaunchProfilePlatformChannels ();
122+ }
123+
14124/// A named channel for communicating with platform plugins using asynchronous
15125/// message passing.
16126///
@@ -49,7 +159,15 @@ class BasicMessageChannel<T> {
49159 final MessageCodec <T > codec;
50160
51161 /// The messenger which sends the bytes for this channel, not null.
52- BinaryMessenger get binaryMessenger => _binaryMessenger ?? ServicesBinding .instance.defaultBinaryMessenger;
162+ BinaryMessenger get binaryMessenger {
163+ final BinaryMessenger result =
164+ _binaryMessenger ?? ServicesBinding .instance.defaultBinaryMessenger;
165+ return ! kReleaseMode && debugProfilePlatformChannels
166+ ? _debugBinaryMessengers[this ] ?? = _ProfiledBinaryMessenger (
167+ // ignore: no_runtimetype_tostring
168+ result, runtimeType.toString (), codec.runtimeType.toString ())
169+ : result;
170+ }
53171 final BinaryMessenger ? _binaryMessenger;
54172
55173 /// Sends the specified [message] to the platform plugins on this channel.
@@ -129,7 +247,15 @@ class MethodChannel {
129247 /// The messenger used by this channel to send platform messages.
130248 ///
131249 /// The messenger may not be null.
132- BinaryMessenger get binaryMessenger => _binaryMessenger ?? ServicesBinding .instance.defaultBinaryMessenger;
250+ BinaryMessenger get binaryMessenger {
251+ final BinaryMessenger result =
252+ _binaryMessenger ?? ServicesBinding .instance.defaultBinaryMessenger;
253+ return ! kReleaseMode && debugProfilePlatformChannels
254+ ? _debugBinaryMessengers[this ] ?? = _ProfiledBinaryMessenger (
255+ // ignore: no_runtimetype_tostring
256+ result, runtimeType.toString (), codec.runtimeType.toString ())
257+ : result;
258+ }
133259 final BinaryMessenger ? _binaryMessenger;
134260
135261 /// Backend implementation of [invokeMethod] .
@@ -154,10 +280,11 @@ class MethodChannel {
154280 @optionalTypeArgs
155281 Future <T ?> _invokeMethod <T >(String method, { required bool missingOk, dynamic arguments }) async {
156282 assert (method != null );
157- final ByteData ? result = await binaryMessenger.send (
158- name,
159- codec.encodeMethodCall (MethodCall (method, arguments)),
160- );
283+ final ByteData input = codec.encodeMethodCall (MethodCall (method, arguments));
284+ final ByteData ? result =
285+ ! kReleaseMode && debugProfilePlatformChannels ?
286+ await (binaryMessenger as _ProfiledBinaryMessenger ).sendWithPostfix (name, '#$method ' , input) :
287+ await binaryMessenger.send (name, input);
161288 if (result == null ) {
162289 if (missingOk) {
163290 return null ;
0 commit comments