Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Add support for blocking platform message send #4358

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,20 @@ public void send(T message, final Reply<T> callback) {
callback == null ? null : new IncomingReplyHandler(callback));
}

/**
* Sends the specified message to the Flutter application, then blocks waiting for a reply.
*
* <p>This method should be used only when the semantics of the caller's context precludes
* the use of asynchronous alternatives. Examples include implementing
* {@link android.app.Activity#onSaveInstanceState(android.os.Bundle)}.</p>
*
* @param message the message, possibly null.
* @return the reply, possibly null.
*/
public T sendBlocking(T message) {
return codec.decodeMessage(messenger.sendBlocking(name, codec.encodeMessage(message)));
}

/**
* Registers a message handler on this channel for receiving messages sent from the Flutter
* application.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,20 @@ public interface BinaryMessenger {
*/
void send(String channel, ByteBuffer message, BinaryReply callback);

/**
* Sends a binary message to the Flutter application and blocks waiting for a reply.
*
* <p>This method should be used only when the semantics of the caller's context precludes the use of
* asynchronous alternatives. Examples include implementing
* {@link android.app.Activity#onSaveInstanceState(android.os.Bundle)}.</p>
*
* @param channel the name {@link String} of the logical channel used for the message.
* @param message the message payload, a direct-allocated {@link ByteBuffer} with the message
* bytes between position zero and current position, or null.
* @return a {@link ByteBuffer} containing the reply payload, or null.
*/
ByteBuffer sendBlocking(String channel, ByteBuffer message);

/**
* Registers a handler to be invoked when the Flutter application sends a message
* to its host platform.
Expand Down
23 changes: 23 additions & 0 deletions shell/platform/android/io/flutter/plugin/common/MethodChannel.java
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,29 @@ public void invokeMethod(String method, Object arguments, Result callback) {
callback == null ? null : new IncomingResultHandler(callback));
}

/**
* Invokes a method on this channel, blocking while waiting for a result.
*
* <p>This method should be used only when the semantics of the caller's context precludes
* the use of asynchronous alternatives. Examples include implementing
* {@link android.app.Activity#onSaveInstanceState(android.os.Bundle)}.</p>
*
* @param method the name String of the method.
* @param arguments the arguments for the invocation, possibly null.
* @return the invocation result, possibly null.
* @throws UnsupportedOperationException, if no method handler has been registered
* on the Dart side.
*/
public Object invokeMethodBlocking(String method, Object arguments) {
final ByteBuffer reply = messenger.sendBlocking(name,
codec.encodeMethodCall(new MethodCall(method, arguments)));
if (reply == null) {
throw new UnsupportedOperationException(
"Method " + method + " not implemented by Dart code");
}
return codec.decodeEnvelope(reply);
}

/**
* Registers a method call handler on this channel.
*
Expand Down
26 changes: 25 additions & 1 deletion shell/platform/android/io/flutter/view/FlutterNativeView.java
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ public static String getObservatoryUri() {

@Override
public void send(String channel, ByteBuffer message) {
send(channel, message, null);
send(channel, message, null);
}

@Override
Expand All @@ -90,6 +90,22 @@ public void send(String channel, ByteBuffer message, BinaryReply callback) {
}
}

@Override
public ByteBuffer sendBlocking(String channel, ByteBuffer message) {
if (!isAttached()) {
Log.d(TAG, "FlutterView.sendBlocking called on a detached view, channel=" + channel);
return null;
}
final byte[] reply;
if (message == null) {
reply = nativeDispatchEmptyPlatformMessageBlocking(mNativePlatformView, channel);
} else {
reply = nativeDispatchPlatformMessageBlocking(
mNativePlatformView, channel, message, message.position());
}
return (reply == null ? null : ByteBuffer.wrap(reply));
}

@Override
public void setMessageHandler(String channel, BinaryMessageHandler handler) {
if (handler == null) {
Expand Down Expand Up @@ -190,6 +206,14 @@ private static native void nativeDispatchEmptyPlatformMessage(long nativePlatfor
private static native void nativeDispatchPlatformMessage(long nativePlatformViewAndroid,
String channel, ByteBuffer message, int position, int responseId);

// Send an empty platform message to Dart and block waiting for the reply.
private static native byte[] nativeDispatchEmptyPlatformMessageBlocking(
long nativePlatformViewAndroid, String channel);

// Send a data-carrying platform message to Dart and block waiting for the reply.
private static native byte[] nativeDispatchPlatformMessageBlocking(
long nativePlatformViewAndroid, String channel, ByteBuffer message, int position);

// Send an empty response to a platform message received from Dart.
private static native void nativeInvokePlatformMessageEmptyResponseCallback(
long nativePlatformViewAndroid, int responseId);
Expand Down
5 changes: 5 additions & 0 deletions shell/platform/android/io/flutter/view/FlutterView.java
Original file line number Diff line number Diff line change
Expand Up @@ -811,6 +811,11 @@ public void send(String channel, ByteBuffer message, BinaryReply callback) {
mNativeView.send(channel, message, callback);
}

@Override
public ByteBuffer sendBlocking(String channel, ByteBuffer message) {
return mNativeView.sendBlocking(channel, message);
}

@Override
public void setMessageHandler(String channel, BinaryMessageHandler handler) {
mNativeView.setMessageHandler(channel, handler);
Expand Down
64 changes: 64 additions & 0 deletions shell/platform/android/platform_view_android.cc
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,41 @@ class PlatformMessageResponseAndroid : public blink::PlatformMessageResponse {
std::weak_ptr<PlatformView> view_;
};

class LatchedPlatformMessageResponseAndroid
: public blink::PlatformMessageResponse {
FRIEND_MAKE_REF_COUNTED(LatchedPlatformMessageResponseAndroid);

public:
void Complete(std::vector<uint8_t> data) override {
reply_ = std::move(data);
hasReply_ = true;
latch_.Signal();
}

void CompleteEmpty() override {
hasReply_ = false;
latch_.Signal();
}

void Wait() { latch_.Wait(); }

jbyteArray ToJavaByteArray(JNIEnv* env) {
if (hasReply_) {
jbyteArray reply_array = env->NewByteArray(reply_.size());
env->SetByteArrayRegion(reply_array, 0, reply_.size(),
reinterpret_cast<const jbyte*>(reply_.data()));
return reply_array;
} else {
return nullptr;
}
}

private:
fxl::AutoResetWaitableEvent latch_;
bool hasReply_;
std::vector<uint8_t> reply_;
};

static std::unique_ptr<AndroidSurface> InitializePlatformSurfaceGL() {
const PlatformView::SurfaceConfig offscreen_config = {
.red_bits = 8,
Expand Down Expand Up @@ -294,6 +329,35 @@ void PlatformViewAndroid::DispatchEmptyPlatformMessage(JNIEnv* env,
std::move(response)));
}

jbyteArray PlatformViewAndroid::DispatchPlatformMessageBlocking(
JNIEnv* env,
std::string name,
jobject java_message_data,
jint java_message_position) {
uint8_t* message_data =
static_cast<uint8_t*>(env->GetDirectBufferAddress(java_message_data));
std::vector<uint8_t> message =
std::vector<uint8_t>(message_data, message_data + java_message_position);
fxl::RefPtr<LatchedPlatformMessageResponseAndroid> response =
fxl::MakeRefCounted<LatchedPlatformMessageResponseAndroid>();
PlatformView::DispatchPlatformMessage(
fxl::MakeRefCounted<blink::PlatformMessage>(
std::move(name), std::move(message), response));
response->Wait();
return response->ToJavaByteArray(env);
}

jbyteArray PlatformViewAndroid::DispatchEmptyPlatformMessageBlocking(
JNIEnv* env,
std::string name) {
fxl::RefPtr<LatchedPlatformMessageResponseAndroid> response =
fxl::MakeRefCounted<LatchedPlatformMessageResponseAndroid>();
PlatformView::DispatchPlatformMessage(
fxl::MakeRefCounted<blink::PlatformMessage>(std::move(name), response));
response->Wait();
return response->ToJavaByteArray(env);
}

void PlatformViewAndroid::DispatchPointerDataPacket(JNIEnv* env,
jobject buffer,
jint position) {
Expand Down
8 changes: 8 additions & 0 deletions shell/platform/android/platform_view_android.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,14 @@ class PlatformViewAndroid : public PlatformView {
std::string name,
jint response_id);

jbyteArray DispatchPlatformMessageBlocking(JNIEnv* env,
std::string name,
jobject message_data,
jint message_position);

jbyteArray DispatchEmptyPlatformMessageBlocking(JNIEnv* env,
std::string name);

void DispatchPointerDataPacket(JNIEnv* env, jobject buffer, jint position);

void InvokePlatformMessageResponseCallback(JNIEnv* env,
Expand Down
34 changes: 32 additions & 2 deletions shell/platform/android/platform_view_android_jni.cc
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ static void DispatchPlatformMessage(JNIEnv* env,
jobject message,
jint position,
jint responseId) {
return PLATFORM_VIEW->DispatchPlatformMessage(
PLATFORM_VIEW->DispatchPlatformMessage(
env, fml::jni::JavaStringToString(env, channel), message, position,
responseId);
}
Expand All @@ -208,10 +208,28 @@ static void DispatchEmptyPlatformMessage(JNIEnv* env,
jlong platform_view,
jstring channel,
jint responseId) {
return PLATFORM_VIEW->DispatchEmptyPlatformMessage(
PLATFORM_VIEW->DispatchEmptyPlatformMessage(
env, fml::jni::JavaStringToString(env, channel), responseId);
}

static jbyteArray DispatchPlatformMessageBlocking(JNIEnv* env,
jobject jcaller,
jlong platform_view,
jstring channel,
jobject message,
jint position) {
return PLATFORM_VIEW->DispatchPlatformMessageBlocking(
env, fml::jni::JavaStringToString(env, channel), message, position);
}

static jbyteArray DispatchEmptyPlatformMessageBlocking(JNIEnv* env,
jobject jcaller,
jlong platform_view,
jstring channel) {
return PLATFORM_VIEW->DispatchEmptyPlatformMessageBlocking(
env, fml::jni::JavaStringToString(env, channel));
}

static void DispatchPointerDataPacket(JNIEnv* env,
jobject jcaller,
jlong platform_view,
Expand Down Expand Up @@ -344,6 +362,18 @@ bool PlatformViewAndroid::Register(JNIEnv* env) {
.signature = "(JLjava/lang/String;Ljava/nio/ByteBuffer;II)V",
.fnPtr = reinterpret_cast<void*>(&shell::DispatchPlatformMessage),
},
{
.name = "nativeDispatchEmptyPlatformMessageBlocking",
.signature = "(JLjava/lang/String;)[B",
.fnPtr = reinterpret_cast<void*>(
&shell::DispatchEmptyPlatformMessageBlocking),
},
{
.name = "nativeDispatchPlatformMessageBlocking",
.signature = "(JLjava/lang/String;Ljava/nio/ByteBuffer;I)[B",
.fnPtr =
reinterpret_cast<void*>(&shell::DispatchPlatformMessageBlocking),
},
{
.name = "nativeInvokePlatformMessageResponseCallback",
.signature = "(JILjava/nio/ByteBuffer;I)V",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,19 @@ FLUTTER_EXPORT
- (void)sendOnChannel:(NSString*)channel
message:(NSData* _Nullable)message
binaryReply:(FlutterBinaryReply _Nullable)callback;
/**
Sends a binary message to the Flutter side on the specified channel, blocking
while waiting for a reply.

Should be used only when the semantics of the caller's context precludes
the use of asynchronous alternatives.

- Parameters:
- channel: The channel name.
- message: The message.
- Returns: The reply.
*/
- (NSData* _Nullable)sendBlockingOnChannel:(NSString*)channel message:(NSData* _Nullable)message;

/**
Registers a message handler for incoming binary messages from the Flutter side
Expand Down
32 changes: 32 additions & 0 deletions shell/platform/darwin/ios/framework/Headers/FlutterChannels.h
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,19 @@ FLUTTER_EXPORT
*/
- (void)sendMessage:(id _Nullable)message reply:(FlutterReply _Nullable)callback;

/**
Sends the specified message to the Flutter side, blocking while waiting for
a reply.

Should be used only when the semantics of the caller's context precludes
the use of asynchronous alternatives.

- Parameters:
- message: The message. Must be supported by the codec of this channel.
- Returns: The reply.
*/
- (id _Nullable)sendBlockingMessage:(id _Nullable)message;

/**
Registers a message handler with this channel.

Expand Down Expand Up @@ -244,6 +257,25 @@ FLUTTER_EXPORT
arguments:(id _Nullable)arguments
result:(FlutterResult _Nullable)callback;

/**
Invokes the specified Flutter method with the specified arguments, blocking
while waiting for a reply.

Should be used only when the semantics of the caller's context precludes
the use of asynchronous alternatives.

- Parameters:
- method: The name of the method to invoke.
- arguments: The arguments. Must be a value supported by the codec of this
channel.
- Returns: The result of the invocation. The result will be a `FlutterError`
instance, if the method call resulted in an error on the Flutter side.
Will be `FlutterMethodNotImplemented`, if the method called was not
implemented on the Flutter side. Any other value, including `nil`, should
be interpreted as successful results.
*/
- (id _Nullable)invokeBlockingMethod:(NSString*)method arguments:(id _Nullable)arguments;

/**
Registers a handler for method calls from the Flutter side.

Expand Down
12 changes: 12 additions & 0 deletions shell/platform/darwin/ios/framework/Source/FlutterChannels.mm
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@ - (void)sendMessage:(id)message reply:(FlutterReply)callback {
[_messenger sendOnChannel:_name message:[_codec encode:message] binaryReply:reply];
}

- (id)sendBlockingMessage:(id)message {
return [_codec decode:[_messenger sendBlockingOnChannel:_name message:[_codec encode:message]]];
}

- (void)setMessageHandler:(FlutterMessageHandler)handler {
if (!handler) {
[_messenger setMessageHandlerOnChannel:_name binaryMessageHandler:nil];
Expand Down Expand Up @@ -203,6 +207,14 @@ - (void)invokeMethod:(NSString*)method arguments:(id)arguments result:(FlutterRe
[_messenger sendOnChannel:_name message:message binaryReply:reply];
}

- (id)invokeBlockingMethod:(NSString*)method arguments:(id)arguments {
FlutterMethodCall* methodCall =
[FlutterMethodCall methodCallWithMethodName:method arguments:arguments];
NSData* message = [_codec encodeMethodCall:methodCall];
NSData* reply = [_messenger sendBlockingOnChannel:_name message:message];
return (reply == nil) ? FlutterMethodNotImplemented : [_codec decodeEnvelope:reply];
}

- (void)setMethodCallHandler:(FlutterMethodCallHandler)handler {
if (!handler) {
[_messenger setMessageHandlerOnChannel:_name binaryMessageHandler:nil];
Expand Down
Loading