2017-03-01 13:54:32 +01:00
|
|
|
// Copyright 2017 The Chromium Authors. All rights reserved.
|
|
|
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
|
|
|
// found in the LICENSE file.
|
|
|
|
|
|
|
|
|
|
package io.flutter.plugin.common;
|
|
|
|
|
|
|
|
|
|
import android.util.Log;
|
2017-04-18 14:30:31 +02:00
|
|
|
import io.flutter.plugin.common.BinaryMessenger.BinaryMessageHandler;
|
|
|
|
|
import io.flutter.plugin.common.BinaryMessenger.BinaryReply;
|
2017-03-01 13:54:32 +01:00
|
|
|
import java.nio.ByteBuffer;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* A named channel for communicating with the Flutter application using asynchronous
|
2017-03-29 13:43:54 +02:00
|
|
|
* method calls.
|
2017-03-01 13:54:32 +01:00
|
|
|
*
|
2017-04-19 21:53:46 +02:00
|
|
|
* <p>Incoming method calls are decoded from binary on receipt, and Java results are encoded
|
2017-03-01 13:54:32 +01:00
|
|
|
* into binary before being transmitted back to Flutter. The {@link MethodCodec} used must be
|
|
|
|
|
* compatible with the one used by the Flutter application. This can be achieved
|
2017-04-19 21:53:46 +02:00
|
|
|
* by creating a
|
|
|
|
|
* <a href="https://docs.flutter.io/flutter/services/MethodChannel-class.html">MethodChannel</a>
|
|
|
|
|
* counterpart of this channel on the Dart side. The Java type of method call arguments and results is
|
|
|
|
|
* {@code Object}, but only values supported by the specified {@link MethodCodec} can be used.</p>
|
2017-03-01 13:54:32 +01:00
|
|
|
*
|
2017-04-19 21:53:46 +02:00
|
|
|
* <p>The logical identity of the channel is given by its name. Identically named channels will interfere
|
|
|
|
|
* with each other's communication.</p>
|
2017-03-01 13:54:32 +01:00
|
|
|
*/
|
2017-04-18 14:30:31 +02:00
|
|
|
public final class MethodChannel {
|
|
|
|
|
private static final String TAG = "MethodChannel#";
|
2017-03-01 13:54:32 +01:00
|
|
|
|
2017-04-18 14:30:31 +02:00
|
|
|
private final BinaryMessenger messenger;
|
2017-03-01 13:54:32 +01:00
|
|
|
private final String name;
|
|
|
|
|
private final MethodCodec codec;
|
|
|
|
|
|
|
|
|
|
/**
|
2017-04-18 14:30:31 +02:00
|
|
|
* Creates a new channel associated with the specified {@link BinaryMessenger}
|
|
|
|
|
* and with the specified name and the standard {@link MethodCodec}.
|
2017-03-01 13:54:32 +01:00
|
|
|
*
|
2017-04-18 14:30:31 +02:00
|
|
|
* @param messenger a {@link BinaryMessenger}.
|
2017-03-01 13:54:32 +01:00
|
|
|
* @param name a channel name String.
|
|
|
|
|
*/
|
2017-04-18 14:30:31 +02:00
|
|
|
public MethodChannel(BinaryMessenger messenger, String name) {
|
|
|
|
|
this(messenger, name, StandardMethodCodec.INSTANCE);
|
2017-03-01 13:54:32 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2017-04-18 14:30:31 +02:00
|
|
|
* Creates a new channel associated with the specified {@link BinaryMessenger} and with the
|
2017-03-01 13:54:32 +01:00
|
|
|
* specified name and {@link MethodCodec}.
|
|
|
|
|
*
|
2017-04-18 14:30:31 +02:00
|
|
|
* @param messenger a {@link BinaryMessenger}.
|
2017-03-01 13:54:32 +01:00
|
|
|
* @param name a channel name String.
|
|
|
|
|
* @param codec a {@link MessageCodec}.
|
|
|
|
|
*/
|
2017-04-18 14:30:31 +02:00
|
|
|
public MethodChannel(BinaryMessenger messenger, String name, MethodCodec codec) {
|
|
|
|
|
assert messenger != null;
|
2017-03-06 13:22:29 +01:00
|
|
|
assert name != null;
|
|
|
|
|
assert codec != null;
|
2017-04-18 14:30:31 +02:00
|
|
|
this.messenger = messenger;
|
2017-03-06 13:22:29 +01:00
|
|
|
this.name = name;
|
|
|
|
|
this.codec = codec;
|
2017-03-01 13:54:32 +01:00
|
|
|
}
|
|
|
|
|
|
2017-03-17 09:04:59 +01:00
|
|
|
/**
|
|
|
|
|
* Invokes a method on this channel, expecting no result.
|
|
|
|
|
*
|
|
|
|
|
* @param method the name String of the method.
|
|
|
|
|
* @param arguments the arguments for the invocation, possibly null.
|
|
|
|
|
*/
|
|
|
|
|
public void invokeMethod(String method, Object arguments) {
|
|
|
|
|
invokeMethod(method, arguments, null);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2017-04-19 21:53:46 +02:00
|
|
|
* Invokes a method on this channel, optionally expecting a result.
|
|
|
|
|
*
|
|
|
|
|
* <p>Any uncaught exception thrown by the result callback will be caught and logged.</p>
|
2017-03-17 09:04:59 +01:00
|
|
|
*
|
2017-03-24 18:23:51 -07:00
|
|
|
* @param method the name String of the method.
|
|
|
|
|
* @param arguments the arguments for the invocation, possibly null.
|
2017-04-19 21:53:46 +02:00
|
|
|
* @param callback a {@link Result} callback for the invocation result, or null.
|
2017-03-17 09:04:59 +01:00
|
|
|
*/
|
2017-04-18 14:30:31 +02:00
|
|
|
public void invokeMethod(String method, Object arguments, Result callback) {
|
|
|
|
|
messenger.send(name, codec.encodeMethodCall(new MethodCall(method, arguments)),
|
|
|
|
|
callback == null ? null : new IncomingResultHandler(callback));
|
2017-03-17 09:04:59 +01:00
|
|
|
}
|
|
|
|
|
|
2017-03-01 13:54:32 +01:00
|
|
|
/**
|
|
|
|
|
* Registers a method call handler on this channel.
|
|
|
|
|
*
|
2017-04-19 21:53:46 +02:00
|
|
|
* <p>Overrides any existing handler registration for (the name of) this channel.</p>
|
|
|
|
|
*
|
|
|
|
|
* <p>If no handler has been registered, any incoming method call on this channel will be handled
|
|
|
|
|
* silently by sending a null reply. This results in a
|
|
|
|
|
* <a href="https://docs.flutter.io/flutter/services/MissingPluginException-class.html">MissingPluginException</a>
|
|
|
|
|
* on the Dart side, unless an
|
|
|
|
|
* <a href="https://docs.flutter.io/flutter/services/OptionalMethodChannel-class.html">OptionalMethodChannel</a>
|
|
|
|
|
* is used.</p>
|
2017-03-01 13:54:32 +01:00
|
|
|
*
|
|
|
|
|
* @param handler a {@link MethodCallHandler}, or null to deregister.
|
|
|
|
|
*/
|
|
|
|
|
public void setMethodCallHandler(final MethodCallHandler handler) {
|
2017-04-18 14:30:31 +02:00
|
|
|
messenger.setMessageHandler(name,
|
|
|
|
|
handler == null ? null : new IncomingMethodCallHandler(handler));
|
2017-03-01 13:54:32 +01:00
|
|
|
}
|
|
|
|
|
|
2017-04-19 21:53:46 +02:00
|
|
|
/**
|
|
|
|
|
* A handler of incoming method calls.
|
|
|
|
|
*/
|
|
|
|
|
public interface MethodCallHandler {
|
|
|
|
|
/**
|
|
|
|
|
* Handles the specified method call received from Flutter.
|
|
|
|
|
*
|
|
|
|
|
* <p>Handler implementations must submit a result for all incoming calls, by making a single call
|
|
|
|
|
* on the given {@link Result} callback. Failure to do so will result in lingering Flutter result
|
|
|
|
|
* handlers. The result may be submitted asynchronously. Calls to unknown or unimplemented methods
|
|
|
|
|
* should be handled using {@link Result#notImplemented()}.</p>
|
|
|
|
|
*
|
2017-04-27 07:52:56 +02:00
|
|
|
* <p>Any uncaught exception thrown by this method will be caught by the channel implementation and
|
|
|
|
|
* logged, and an error result will be sent back to Flutter.</p>
|
2017-04-19 21:53:46 +02:00
|
|
|
*
|
|
|
|
|
* @param call A {@link MethodCall}.
|
|
|
|
|
* @param result A {@link Result} used for submitting the result of the call.
|
|
|
|
|
*/
|
|
|
|
|
void onMethodCall(MethodCall call, Result result);
|
|
|
|
|
}
|
|
|
|
|
|
2017-03-01 13:54:32 +01:00
|
|
|
/**
|
2017-04-18 14:30:31 +02:00
|
|
|
* Method call result callback. Supports dual use: Implementations of methods
|
|
|
|
|
* to be invoked by Flutter act as clients of this interface for sending results
|
|
|
|
|
* back to Flutter. Invokers of Flutter methods provide implementations of this
|
|
|
|
|
* interface for handling results received from Flutter.
|
2017-03-01 13:54:32 +01:00
|
|
|
*/
|
2017-04-18 14:30:31 +02:00
|
|
|
public interface Result {
|
2017-03-29 13:43:54 +02:00
|
|
|
/**
|
|
|
|
|
* Handles a successful result.
|
|
|
|
|
*
|
|
|
|
|
* @param result The result, possibly null.
|
|
|
|
|
*/
|
|
|
|
|
void success(Object result);
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Handles an error result.
|
|
|
|
|
*
|
|
|
|
|
* @param errorCode An error code String.
|
|
|
|
|
* @param errorMessage A human-readable error message String, possibly null.
|
|
|
|
|
* @param errorDetails Error details, possibly null
|
|
|
|
|
*/
|
|
|
|
|
void error(String errorCode, String errorMessage, Object errorDetails);
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Handles a call to an unimplemented method.
|
|
|
|
|
*/
|
|
|
|
|
void notImplemented();
|
2017-03-01 13:54:32 +01:00
|
|
|
}
|
|
|
|
|
|
2017-04-18 14:30:31 +02:00
|
|
|
private final class IncomingResultHandler implements BinaryReply {
|
|
|
|
|
private final Result callback;
|
2017-03-17 09:04:59 +01:00
|
|
|
|
2017-04-18 14:30:31 +02:00
|
|
|
IncomingResultHandler(Result callback) {
|
|
|
|
|
this.callback = callback;
|
2017-03-17 09:04:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
2017-04-18 14:30:31 +02:00
|
|
|
public void reply(ByteBuffer reply) {
|
2017-04-19 21:53:46 +02:00
|
|
|
try {
|
|
|
|
|
if (reply == null) {
|
|
|
|
|
callback.notImplemented();
|
|
|
|
|
} else {
|
|
|
|
|
try {
|
2017-04-27 07:52:56 +02:00
|
|
|
callback.success(codec.decodeEnvelope(reply));
|
2017-04-19 21:53:46 +02:00
|
|
|
} catch (FlutterException e) {
|
|
|
|
|
callback.error(e.code, e.getMessage(), e.details);
|
|
|
|
|
}
|
2017-03-29 13:43:54 +02:00
|
|
|
}
|
2017-04-19 21:53:46 +02:00
|
|
|
} catch (RuntimeException e) {
|
|
|
|
|
Log.e(TAG + name, "Failed to handle method call result", e);
|
2017-03-17 09:04:59 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-18 14:30:31 +02:00
|
|
|
private final class IncomingMethodCallHandler implements BinaryMessageHandler {
|
2017-03-01 13:54:32 +01:00
|
|
|
private final MethodCallHandler handler;
|
|
|
|
|
|
2017-04-18 14:30:31 +02:00
|
|
|
IncomingMethodCallHandler(MethodCallHandler handler) {
|
2017-03-01 13:54:32 +01:00
|
|
|
this.handler = handler;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
2017-04-18 14:30:31 +02:00
|
|
|
public void onMessage(ByteBuffer message, final BinaryReply reply) {
|
2017-04-27 07:52:56 +02:00
|
|
|
final MethodCall call = codec.decodeMethodCall(message);
|
2017-03-01 13:54:32 +01:00
|
|
|
try {
|
2017-04-18 14:30:31 +02:00
|
|
|
handler.onMethodCall(call, new Result() {
|
2017-03-01 13:54:32 +01:00
|
|
|
@Override
|
|
|
|
|
public void success(Object result) {
|
2017-04-27 07:52:56 +02:00
|
|
|
reply.reply(codec.encodeSuccessEnvelope(result));
|
2017-03-01 13:54:32 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void error(String errorCode, String errorMessage, Object errorDetails) {
|
2017-04-27 07:52:56 +02:00
|
|
|
reply.reply(codec.encodeErrorEnvelope(errorCode, errorMessage, errorDetails));
|
2017-03-01 13:54:32 +01:00
|
|
|
}
|
|
|
|
|
|
2017-03-29 13:43:54 +02:00
|
|
|
@Override
|
|
|
|
|
public void notImplemented() {
|
2017-04-18 14:30:31 +02:00
|
|
|
reply.reply(null);
|
2017-03-01 13:54:32 +01:00
|
|
|
}
|
|
|
|
|
});
|
2017-04-19 21:53:46 +02:00
|
|
|
} catch (RuntimeException e) {
|
2017-03-01 13:54:32 +01:00
|
|
|
Log.e(TAG + name, "Failed to handle method call", e);
|
2017-04-27 07:52:56 +02:00
|
|
|
reply.reply(codec.encodeErrorEnvelope("error", e.getMessage(), null));
|
2017-03-01 13:54:32 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|