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 import java.nio.ByteBuffer; 12 13 import io.flutter.BuildConfig; 14 import io.flutter.plugin.common.BinaryMessenger.BinaryReply; 15 import io.flutter.plugin.common.BinaryMessenger.BinaryMessageHandler; 16 17 /** 18 * A named channel for communicating with the Flutter application using basic, asynchronous message passing. 19 * 20 * <p>Messages are encoded into binary before being sent, and binary messages received are decoded 21 * into Java objects. The {@link MessageCodec} used must be compatible with the 22 * one used by the Flutter application. This can be achieved by creating a 23 * <a href="https://docs.flutter.io/flutter/services/BasicMessageChannel-class.html">BasicMessageChannel</a> 24 * counterpart of this channel on the Dart side. The static Java type of messages sent and received 25 * is {@code Object}, but only values supported by the specified {@link MessageCodec} can be used.</p> 26 * 27 * <p>The logical identity of the channel is given by its name. Identically named channels will interfere 28 * with each other's communication.</p> 29 */ 30 public final class BasicMessageChannel<T> { 31 private static final String TAG = "BasicMessageChannel#"; 32 33 @NonNull 34 private final BinaryMessenger messenger; 35 @NonNull 36 private final String name; 37 @NonNull 38 private final MessageCodec<T> codec; 39 40 /** 41 * Creates a new channel associated with the specified {@link BinaryMessenger} 42 * and with the specified name and {@link MessageCodec}. 43 * 44 * @param messenger a {@link BinaryMessenger}. 45 * @param name a channel name String. 46 * @param codec a {@link MessageCodec}. 47 */ BasicMessageChannel(@onNull BinaryMessenger messenger, @NonNull String name, @NonNull MessageCodec<T> codec)48 public BasicMessageChannel(@NonNull BinaryMessenger messenger, @NonNull String name, @NonNull MessageCodec<T> codec) { 49 if (BuildConfig.DEBUG) { 50 if (messenger == null) { 51 Log.e(TAG, "Parameter messenger must not be null."); 52 } 53 if (name == null) { 54 Log.e(TAG, "Parameter name must not be null."); 55 } 56 if (codec == null) { 57 Log.e(TAG, "Parameter codec must not be null."); 58 } 59 } 60 this.messenger = messenger; 61 this.name = name; 62 this.codec = codec; 63 } 64 65 /** 66 * Sends the specified message to the Flutter application on this channel. 67 * 68 * @param message the message, possibly null. 69 */ send(@ullable T message)70 public void send(@Nullable T message) { 71 send(message, null); 72 } 73 74 /** 75 * Sends the specified message to the Flutter application, optionally expecting a reply. 76 * 77 * <p>Any uncaught exception thrown by the reply callback will be caught and logged.</p> 78 * 79 * @param message the message, possibly null. 80 * @param callback a {@link Reply} callback, possibly null. 81 */ 82 @UiThread send(@ullable T message, @Nullable final Reply<T> callback)83 public void send(@Nullable T message, @Nullable final Reply<T> callback) { 84 messenger.send(name, codec.encodeMessage(message), 85 callback == null ? null : new IncomingReplyHandler(callback)); 86 } 87 88 /** 89 * Registers a message handler on this channel for receiving messages sent from the Flutter 90 * application. 91 * 92 * <p>Overrides any existing handler registration for (the name of) this channel.</p> 93 * 94 * <p>If no handler has been registered, any incoming message on this channel will be handled silently 95 * by sending a null reply.</p> 96 * 97 * @param handler a {@link MessageHandler}, or null to deregister. 98 */ 99 @UiThread setMessageHandler(@ullable final MessageHandler<T> handler)100 public void setMessageHandler(@Nullable final MessageHandler<T> handler) { 101 messenger.setMessageHandler(name, 102 handler == null ? null : new IncomingMessageHandler(handler)); 103 } 104 105 /** 106 * A handler of incoming messages. 107 */ 108 public interface MessageHandler<T> { 109 110 /** 111 * Handles the specified message received from Flutter. 112 * 113 * <p>Handler implementations must reply to all incoming messages, by submitting a single reply 114 * message to the given {@link Reply}. Failure to do so will result in lingering Flutter reply 115 * handlers. The reply may be submitted asynchronously.</p> 116 * 117 * <p>Any uncaught exception thrown by this method, or the preceding message decoding, will be 118 * caught by the channel implementation and logged, and a null reply message will be sent back 119 * to Flutter.</p> 120 * 121 * <p>Any uncaught exception thrown during encoding a reply message submitted to the {@link Reply} 122 * is treated similarly: the exception is logged, and a null reply is sent to Flutter.</p> 123 * 124 * @param message the message, possibly null. 125 * @param reply a {@link Reply} for sending a single message reply back to Flutter. 126 */ onMessage(@ullable T message, @NonNull Reply<T> reply)127 void onMessage(@Nullable T message, @NonNull Reply<T> reply); 128 } 129 130 /** 131 * Message reply callback. Used to submit a reply to an incoming 132 * message from Flutter. Also used in the dual capacity to handle a reply 133 * received from Flutter after sending a message. 134 */ 135 public interface Reply<T> { 136 /** 137 * Handles the specified message reply. 138 * 139 * @param reply the reply, possibly null. 140 */ reply(@ullable T reply)141 void reply(@Nullable T reply); 142 } 143 144 private final class IncomingReplyHandler implements BinaryReply { 145 private final Reply<T> callback; 146 IncomingReplyHandler(@onNull Reply<T> callback)147 private IncomingReplyHandler(@NonNull Reply<T> callback) { 148 this.callback = callback; 149 } 150 151 @Override reply(@ullable ByteBuffer reply)152 public void reply(@Nullable ByteBuffer reply) { 153 try { 154 callback.reply(codec.decodeMessage(reply)); 155 } catch (RuntimeException e) { 156 Log.e(TAG + name, "Failed to handle message reply", e); 157 } 158 } 159 } 160 161 private final class IncomingMessageHandler implements BinaryMessageHandler { 162 private final MessageHandler<T> handler; 163 IncomingMessageHandler(@onNull MessageHandler<T> handler)164 private IncomingMessageHandler(@NonNull MessageHandler<T> handler) { 165 this.handler = handler; 166 } 167 168 @Override onMessage(@ullable ByteBuffer message, @NonNull final BinaryReply callback)169 public void onMessage(@Nullable ByteBuffer message, @NonNull final BinaryReply callback) { 170 try { 171 handler.onMessage(codec.decodeMessage(message), new Reply<T>() { 172 @Override 173 public void reply(T reply) { 174 callback.reply(codec.encodeMessage(reply)); 175 } 176 }); 177 } catch (RuntimeException e) { 178 Log.e(TAG + name, "Failed to handle message", e); 179 callback.reply(null); 180 } 181 } 182 } 183 } 184