Flutter Engine
The Flutter Engine
MethodChannel.java
Go to the documentation of this file.
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
5package io.flutter.plugin.common;
6
7import androidx.annotation.NonNull;
8import androidx.annotation.Nullable;
9import androidx.annotation.UiThread;
10import io.flutter.BuildConfig;
11import io.flutter.Log;
12import io.flutter.plugin.common.BinaryMessenger.BinaryMessageHandler;
13import io.flutter.plugin.common.BinaryMessenger.BinaryReply;
14import java.nio.ByteBuffer;
15
16/**
17 * A named channel for communicating with the Flutter application using asynchronous method calls.
18 *
19 * <p>Incoming method calls are decoded from binary on receipt, and Java results are encoded into
20 * binary before being transmitted back to Flutter. The {@link MethodCodec} used must be compatible
21 * with the one used by the Flutter application. This can be achieved by creating a <a
22 * href="https://api.flutter.dev/flutter/services/MethodChannel-class.html">MethodChannel</a>
23 * counterpart of this channel on the Dart side. The Java type of method call arguments and results
24 * is {@code Object}, but only values supported by the specified {@link MethodCodec} can be used.
25 *
26 * <p>The logical identity of the channel is given by its name. Identically named channels will
27 * interfere with each other's communication.
28 */
29public class MethodChannel {
30 private static final String TAG = "MethodChannel#";
31
32 private final BinaryMessenger messenger;
33 private final String name;
34 private final MethodCodec codec;
35 private final BinaryMessenger.TaskQueue taskQueue;
36
37 /**
38 * Creates a new channel associated with the specified {@link BinaryMessenger} and with the
39 * specified name and the standard {@link MethodCodec}.
40 *
41 * @param messenger a {@link BinaryMessenger}.
42 * @param name a channel name String.
43 */
44 public MethodChannel(@NonNull BinaryMessenger messenger, @NonNull String name) {
45 this(messenger, name, StandardMethodCodec.INSTANCE);
46 }
47
48 /**
49 * Creates a new channel associated with the specified {@link BinaryMessenger} and with the
50 * specified name and {@link MethodCodec}.
51 *
52 * @param messenger a {@link BinaryMessenger}.
53 * @param name a channel name String.
54 * @param codec a {@link MessageCodec}.
55 */
57 @NonNull BinaryMessenger messenger, @NonNull String name, @NonNull MethodCodec codec) {
58 this(messenger, name, codec, null);
59 }
60
61 /**
62 * Creates a new channel associated with the specified {@link BinaryMessenger} and with the
63 * specified name and {@link MethodCodec}.
64 *
65 * @param messenger a {@link BinaryMessenger}.
66 * @param name a channel name String.
67 * @param codec a {@link MessageCodec}.
68 * @param taskQueue a {@link BinaryMessenger.TaskQueue} that specifies what thread will execute
69 * the handler. Specifying null means execute on the platform thread. See also {@link
70 * BinaryMessenger#makeBackgroundTaskQueue()}.
71 */
73 @NonNull BinaryMessenger messenger,
74 @NonNull String name,
75 @NonNull MethodCodec codec,
76 @Nullable BinaryMessenger.TaskQueue taskQueue) {
77 if (BuildConfig.DEBUG) {
78 if (messenger == null) {
79 Log.e(TAG, "Parameter messenger must not be null.");
80 }
81 if (name == null) {
82 Log.e(TAG, "Parameter name must not be null.");
83 }
84 if (codec == null) {
85 Log.e(TAG, "Parameter codec must not be null.");
86 }
87 }
88 this.messenger = messenger;
89 this.name = name;
90 this.codec = codec;
91 this.taskQueue = taskQueue;
92 }
93
94 /**
95 * Invokes a method on this channel, expecting no result.
96 *
97 * @param method the name String of the method.
98 * @param arguments the arguments for the invocation, possibly null.
99 */
100 @UiThread
101 public void invokeMethod(@NonNull String method, @Nullable Object arguments) {
102 invokeMethod(method, arguments, null);
103 }
104
105 /**
106 * Invokes a method on this channel, optionally expecting a result.
107 *
108 * <p>Any uncaught exception thrown by the result callback will be caught and logged.
109 *
110 * @param method the name String of the method.
111 * @param arguments the arguments for the invocation, possibly null.
112 * @param callback a {@link Result} callback for the invocation result, or null.
113 */
114 @UiThread
115 public void invokeMethod(
116 @NonNull String method, @Nullable Object arguments, @Nullable Result callback) {
117 messenger.send(
118 name,
119 codec.encodeMethodCall(new MethodCall(method, arguments)),
120 callback == null ? null : new IncomingResultHandler(callback));
121 }
122
123 /**
124 * Registers a method call handler on this channel.
125 *
126 * <p>Overrides any existing handler registration for (the name of) this channel.
127 *
128 * <p>If no handler has been registered, any incoming method call on this channel will be handled
129 * silently by sending a null reply. This results in a <a
130 * href="https://api.flutter.dev/flutter/services/MissingPluginException-class.html">MissingPluginException</a>
131 * on the Dart side, unless an <a
132 * href="https://api.flutter.dev/flutter/services/OptionalMethodChannel-class.html">OptionalMethodChannel</a>
133 * is used.
134 *
135 * @param handler a {@link MethodCallHandler}, or null to deregister.
136 */
137 @UiThread
138 public void setMethodCallHandler(final @Nullable MethodCallHandler handler) {
139 // We call the 2 parameter variant specifically to avoid breaking changes in
140 // mock verify calls.
141 // See https://github.com/flutter/flutter/issues/92582.
142 if (taskQueue != null) {
143 messenger.setMessageHandler(
144 name, handler == null ? null : new IncomingMethodCallHandler(handler), taskQueue);
145 } else {
146 messenger.setMessageHandler(
147 name, handler == null ? null : new IncomingMethodCallHandler(handler));
148 }
149 }
150
151 /**
152 * Adjusts the number of messages that will get buffered when sending messages to channels that
153 * aren't fully set up yet. For example, the engine isn't running yet or the channel's message
154 * handler isn't set up on the Dart side yet.
155 */
156 public void resizeChannelBuffer(int newSize) {
157 BasicMessageChannel.resizeChannelBuffer(messenger, name, newSize);
158 }
159
160 /**
161 * Toggles whether the channel should show warning messages when discarding messages due to
162 * overflow. When 'warns' is false the channel is expected to overflow and warning messages will
163 * not be shown.
164 */
165 public void setWarnsOnChannelOverflow(boolean warns) {
166 BasicMessageChannel.setWarnsOnChannelOverflow(messenger, name, warns);
167 }
168
169 /** A handler of incoming method calls. */
170 public interface MethodCallHandler {
171 /**
172 * Handles the specified method call received from Flutter.
173 *
174 * <p>Handler implementations must submit a result for all incoming calls, by making a single
175 * call on the given {@link Result} callback. Failure to do so will result in lingering Flutter
176 * result handlers. The result may be submitted asynchronously and on any thread. Calls to
177 * unknown or unimplemented methods should be handled using {@link Result#notImplemented()}.
178 *
179 * <p>Any uncaught exception thrown by this method will be caught by the channel implementation
180 * and logged, and an error result will be sent back to Flutter.
181 *
182 * <p>The handler is called on the platform thread (Android main thread) by default, or
183 * otherwise on the thread specified by the {@link BinaryMessenger.TaskQueue} provided to the
184 * associated {@link MethodChannel} when it was created. See also <a
185 * href="https://github.com/flutter/flutter/wiki/The-Engine-architecture#threading">Threading in
186 * the Flutter Engine</a>.
187 *
188 * @param call A {@link MethodCall}.
189 * @param result A {@link Result} used for submitting the result of the call.
190 */
191 @UiThread
192 void onMethodCall(@NonNull MethodCall call, @NonNull Result result);
193 }
194
195 /**
196 * Method call result callback. Supports dual use: Implementations of methods to be invoked by
197 * Flutter act as clients of this interface for sending results back to Flutter. Invokers of
198 * Flutter methods provide implementations of this interface for handling results received from
199 * Flutter.
200 *
201 * <p>All methods of this class can be invoked on any thread.
202 */
203 public interface Result {
204 /**
205 * Handles a successful result.
206 *
207 * @param result The result, possibly null. The result must be an Object type supported by the
208 * codec. For instance, if you are using {@link StandardMessageCodec} (default), please see
209 * its documentation on what types are supported.
210 */
211 void success(@Nullable Object result);
212
213 /**
214 * Handles an error result.
215 *
216 * @param errorCode An error code String.
217 * @param errorMessage A human-readable error message String, possibly null.
218 * @param errorDetails Error details, possibly null. The details must be an Object type
219 * supported by the codec. For instance, if you are using {@link StandardMessageCodec}
220 * (default), please see its documentation on what types are supported.
221 */
222 void error(
223 @NonNull String errorCode, @Nullable String errorMessage, @Nullable Object errorDetails);
224
225 /** Handles a call to an unimplemented method. */
227 }
228
229 private final class IncomingResultHandler implements BinaryReply {
230 private final Result callback;
231
232 IncomingResultHandler(Result callback) {
233 this.callback = callback;
234 }
235
236 @Override
237 @UiThread
238 public void reply(ByteBuffer reply) {
239 try {
240 if (reply == null) {
241 callback.notImplemented();
242 } else {
243 try {
244 callback.success(codec.decodeEnvelope(reply));
245 } catch (FlutterException e) {
246 callback.error(e.code, e.getMessage(), e.details);
247 }
248 }
249 } catch (RuntimeException e) {
250 Log.e(TAG + name, "Failed to handle method call result", e);
251 }
252 }
253 }
254
255 private final class IncomingMethodCallHandler implements BinaryMessageHandler {
256 private final MethodCallHandler handler;
257
258 IncomingMethodCallHandler(MethodCallHandler handler) {
259 this.handler = handler;
260 }
261
262 @Override
263 @UiThread
264 public void onMessage(ByteBuffer message, final BinaryReply reply) {
266 try {
267 handler.onMethodCall(
268 call,
269 new Result() {
270 @Override
271 public void success(Object result) {
272 reply.reply(codec.encodeSuccessEnvelope(result));
273 }
274
275 @Override
276 public void error(String errorCode, String errorMessage, Object errorDetails) {
277 reply.reply(codec.encodeErrorEnvelope(errorCode, errorMessage, errorDetails));
278 }
279
280 @Override
281 public void notImplemented() {
282 reply.reply(null);
283 }
284 });
285 } catch (RuntimeException e) {
286 Log.e(TAG + name, "Failed to handle method call", e);
287 reply.reply(
289 "error", e.getMessage(), null, Log.getStackTraceString(e)));
290 }
291 }
292 }
293}
static final boolean DEBUG
static void e(@NonNull String tag, @NonNull String message)
Definition: Log.java:84
MethodChannel( @NonNull BinaryMessenger messenger, @NonNull String name, @NonNull MethodCodec codec, @Nullable BinaryMessenger.TaskQueue taskQueue)
MethodChannel( @NonNull BinaryMessenger messenger, @NonNull String name, @NonNull MethodCodec codec)
void setMethodCallHandler(final @Nullable MethodCallHandler handler)
void invokeMethod(@NonNull String method, @Nullable Object arguments)
void invokeMethod( @NonNull String method, @Nullable Object arguments, @Nullable Result callback)
MethodChannel(@NonNull BinaryMessenger messenger, @NonNull String name)
FlKeyEvent uint64_t FlKeyResponderAsyncCallback callback
const uint8_t uint32_t uint32_t GError ** error
static ::testing::Matcher< GBytes * > MethodCall(const std::string &name, ::testing::Matcher< FlValue * > args)
GAsyncResult * result
void onMessage(@Nullable ByteBuffer message, @NonNull BinaryReply reply)
void setMessageHandler(@NonNull String channel, @Nullable BinaryMessageHandler handler)
void send(@NonNull String channel, @Nullable ByteBuffer message)
void onMethodCall(@NonNull MethodCall call, @NonNull Result result)
void success(@Nullable Object result)
void error( @NonNull String errorCode, @Nullable String errorMessage, @Nullable Object errorDetails)
ByteBuffer encodeSuccessEnvelope(@Nullable Object result)
ByteBuffer encodeErrorEnvelope( @NonNull String errorCode, @Nullable String errorMessage, @Nullable Object errorDetails)
ByteBuffer encodeMethodCall(@NonNull MethodCall methodCall)
MethodCall decodeMethodCall(@NonNull ByteBuffer methodCall)
ByteBuffer encodeErrorEnvelopeWithStacktrace( @NonNull String errorCode, @Nullable String errorMessage, @Nullable Object errorDetails, @Nullable String errorStacktrace)
Object decodeEnvelope(@NonNull ByteBuffer envelope)
Win32Message message
void Log(const char *format,...) SK_PRINTF_LIKE(1
Definition: TestRunner.cpp:137
def call(args)
Definition: dom.py:159
std::function< void(const uint8_t *message, size_t message_size, BinaryReply reply)> BinaryMessageHandler
std::function< void(const uint8_t *reply, size_t reply_size)> BinaryReply
std::function< void(const MethodCall< T > &call, std::unique_ptr< MethodResult< T > > result)> MethodCallHandler
#define TAG()