• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 package io.flutter.plugin.common;
6 
7 import android.support.annotation.NonNull;
8 import android.support.annotation.Nullable;
9 import android.support.annotation.UiThread;
10 import android.util.Log;
11 
12 import io.flutter.BuildConfig;
13 import io.flutter.plugin.common.BinaryMessenger.BinaryMessageHandler;
14 import io.flutter.plugin.common.BinaryMessenger.BinaryReply;
15 
16 import java.nio.ByteBuffer;
17 
18 /**
19  * A named channel for communicating with the Flutter application using asynchronous
20  * method calls.
21  *
22  * <p>Incoming method calls are decoded from binary on receipt, and Java results are encoded
23  * into binary before being transmitted back to Flutter. The {@link MethodCodec} used must be
24  * compatible with the one used by the Flutter application. This can be achieved
25  * by creating a
26  * <a href="https://docs.flutter.io/flutter/services/MethodChannel-class.html">MethodChannel</a>
27  * counterpart of this channel on the Dart side. The Java type of method call arguments and results is
28  * {@code Object}, but only values supported by the specified {@link MethodCodec} can be used.</p>
29  *
30  * <p>The logical identity of the channel is given by its name. Identically named channels will interfere
31  * with each other's communication.</p>
32  */
33 public final class MethodChannel {
34     private static final String TAG = "MethodChannel#";
35 
36     private final BinaryMessenger messenger;
37     private final String name;
38     private final MethodCodec codec;
39 
40     /**
41      * Creates a new channel associated with the specified {@link BinaryMessenger}
42      * and with the specified name and the standard {@link MethodCodec}.
43      *
44      * @param messenger a {@link BinaryMessenger}.
45      * @param name a channel name String.
46      */
MethodChannel(BinaryMessenger messenger, String name)47     public MethodChannel(BinaryMessenger messenger, String name) {
48         this(messenger, name, StandardMethodCodec.INSTANCE);
49     }
50 
51     /**
52      * Creates a new channel associated with the specified {@link BinaryMessenger} and with the
53      * specified name and {@link MethodCodec}.
54      *
55      * @param messenger a {@link BinaryMessenger}.
56      * @param name a channel name String.
57      * @param codec a {@link MessageCodec}.
58      */
MethodChannel(BinaryMessenger messenger, String name, MethodCodec codec)59     public MethodChannel(BinaryMessenger messenger, String name, MethodCodec codec) {
60         if (BuildConfig.DEBUG) {
61             if (messenger == null) {
62                 Log.e(TAG, "Parameter messenger must not be null.");
63             }
64             if (name == null) {
65                 Log.e(TAG, "Parameter name must not be null.");
66             }
67             if (codec == null) {
68                 Log.e(TAG, "Parameter codec must not be null.");
69             }
70         }
71         this.messenger = messenger;
72         this.name = name;
73         this.codec = codec;
74     }
75 
76     /**
77      * Invokes a method on this channel, expecting no result.
78      *
79      * @param method the name String of the method.
80      * @param arguments the arguments for the invocation, possibly null.
81      */
82     @UiThread
invokeMethod(@onNull String method, @Nullable Object arguments)83     public void invokeMethod(@NonNull String method, @Nullable Object arguments) {
84         invokeMethod(method, arguments, null);
85     }
86 
87     /**
88      * Invokes a method on this channel, optionally expecting a result.
89      *
90      * <p>Any uncaught exception thrown by the result callback will be caught and logged.</p>
91      *
92      * @param method the name String of the method.
93      * @param arguments the arguments for the invocation, possibly null.
94      * @param callback a {@link Result} callback for the invocation result, or null.
95      */
96     @UiThread
invokeMethod(String method, @Nullable Object arguments, Result callback)97     public void invokeMethod(String method, @Nullable Object arguments, Result callback) {
98         messenger.send(name, codec.encodeMethodCall(new MethodCall(method, arguments)),
99             callback == null ? null : new IncomingResultHandler(callback));
100     }
101 
102     /**
103      * Registers a method call handler on this channel.
104      *
105      * <p>Overrides any existing handler registration for (the name of) this channel.</p>
106      *
107      * <p>If no handler has been registered, any incoming method call on this channel will be handled
108      * silently by sending a null reply. This results in a
109      * <a href="https://docs.flutter.io/flutter/services/MissingPluginException-class.html">MissingPluginException</a>
110      * on the Dart side, unless an
111      * <a href="https://docs.flutter.io/flutter/services/OptionalMethodChannel-class.html">OptionalMethodChannel</a>
112      * is used.</p>
113      *
114      * @param handler a {@link MethodCallHandler}, or null to deregister.
115      */
116     @UiThread
setMethodCallHandler(final @Nullable MethodCallHandler handler)117     public void setMethodCallHandler(final @Nullable MethodCallHandler handler) {
118         messenger.setMessageHandler(name,
119             handler == null ? null : new IncomingMethodCallHandler(handler));
120     }
121 
122     /**
123      * A handler of incoming method calls.
124      */
125     public interface MethodCallHandler {
126         /**
127          * Handles the specified method call received from Flutter.
128          *
129          * <p>Handler implementations must submit a result for all incoming calls, by making a single call
130          * on the given {@link Result} callback. Failure to do so will result in lingering Flutter result
131          * handlers. The result may be submitted asynchronously. Calls to unknown or unimplemented methods
132          * should be handled using {@link Result#notImplemented()}.</p>
133          *
134          * <p>Any uncaught exception thrown by this method will be caught by the channel implementation and
135          * logged, and an error result will be sent back to Flutter.</p>
136          *
137          * <p>The handler is called on the platform thread (Android main thread). For more details see
138          * <a href="https://github.com/flutter/engine/wiki/Threading-in-the-Flutter-Engine">Threading in the Flutter
139          * Engine</a>.</p>
140          *
141          * @param call A {@link MethodCall}.
142          * @param result A {@link Result} used for submitting the result of the call.
143          */
144         @UiThread
onMethodCall(@onNull MethodCall call, @NonNull Result result)145         void onMethodCall(@NonNull MethodCall call, @NonNull Result result);
146     }
147 
148     /**
149      * Method call result callback. Supports dual use: Implementations of methods
150      * to be invoked by Flutter act as clients of this interface for sending results
151      * back to Flutter. Invokers of Flutter methods provide implementations of this
152      * interface for handling results received from Flutter.
153      *
154      * <p>All methods of this class must be called on the platform thread (Android main thread). For more details see
155      * <a href="https://github.com/flutter/engine/wiki/Threading-in-the-Flutter-Engine">Threading in the Flutter
156      * Engine</a>.</p>
157      */
158     public interface Result {
159         /**
160          * Handles a successful result.
161          *
162          * @param result The result, possibly null.
163          */
164         @UiThread
success(@ullable Object result)165         void success(@Nullable Object result);
166 
167         /**
168          * Handles an error result.
169          *
170          * @param errorCode An error code String.
171          * @param errorMessage A human-readable error message String, possibly null.
172          * @param errorDetails Error details, possibly null
173          */
174         @UiThread
error(String errorCode, @Nullable String errorMessage, @Nullable Object errorDetails)175         void error(String errorCode, @Nullable String errorMessage, @Nullable Object errorDetails);
176 
177         /**
178          * Handles a call to an unimplemented method.
179          */
180         @UiThread
notImplemented()181         void notImplemented();
182     }
183 
184     private final class IncomingResultHandler implements BinaryReply {
185         private final Result callback;
186 
IncomingResultHandler(Result callback)187         IncomingResultHandler(Result callback) {
188             this.callback = callback;
189         }
190 
191         @Override
192         @UiThread
reply(ByteBuffer reply)193         public void reply(ByteBuffer reply) {
194             try {
195                 if (reply == null) {
196                     callback.notImplemented();
197                 } else {
198                     try {
199                         callback.success(codec.decodeEnvelope(reply));
200                     } catch (FlutterException e) {
201                         callback.error(e.code, e.getMessage(), e.details);
202                     }
203                 }
204             } catch (RuntimeException e) {
205                 Log.e(TAG + name, "Failed to handle method call result", e);
206             }
207         }
208     }
209 
210     private final class IncomingMethodCallHandler implements BinaryMessageHandler {
211         private final MethodCallHandler handler;
212 
IncomingMethodCallHandler(MethodCallHandler handler)213         IncomingMethodCallHandler(MethodCallHandler handler) {
214             this.handler = handler;
215         }
216 
217         @Override
218         @UiThread
onMessage(ByteBuffer message, final BinaryReply reply)219         public void onMessage(ByteBuffer message, final BinaryReply reply) {
220             final MethodCall call = codec.decodeMethodCall(message);
221             try {
222                 handler.onMethodCall(call, new Result() {
223                     @Override
224                     public void success(Object result) {
225                         reply.reply(codec.encodeSuccessEnvelope(result));
226                     }
227 
228                     @Override
229                     public void error(String errorCode, String errorMessage, Object errorDetails) {
230                         reply.reply(codec.encodeErrorEnvelope(errorCode, errorMessage, errorDetails));
231                     }
232 
233                     @Override
234                     public void notImplemented() {
235                         reply.reply(null);
236                     }
237                 });
238             } catch (RuntimeException e) {
239                 Log.e(TAG + name, "Failed to handle method call", e);
240                 reply.reply(codec.encodeErrorEnvelope("error", e.getMessage(), null));
241             }
242         }
243     }
244 }
245