• 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.embedding.engine.dart;
6 
7 import android.support.annotation.NonNull;
8 import android.support.annotation.Nullable;
9 import android.support.annotation.UiThread;
10 
11 import java.nio.ByteBuffer;
12 import java.util.HashMap;
13 import java.util.Map;
14 import java.util.concurrent.atomic.AtomicBoolean;
15 
16 import io.flutter.Log;
17 import io.flutter.embedding.engine.FlutterJNI;
18 import io.flutter.plugin.common.BinaryMessenger;
19 
20 /**
21  * Message conduit for 2-way communication between Android and Dart.
22  * <p>
23  * WARNING: THIS CLASS IS EXPERIMENTAL. DO NOT SHIP A DEPENDENCY ON THIS CODE.
24  * IF YOU USE IT, WE WILL BREAK YOU.
25  * <p>
26  * See {@link BinaryMessenger}, which sends messages from Android to Dart
27  * <p>
28  * See {@link PlatformMessageHandler}, which handles messages to Android from Dart
29  */
30 class DartMessenger implements BinaryMessenger, PlatformMessageHandler {
31   private static final String TAG = "DartMessenger";
32 
33   @NonNull
34   private final FlutterJNI flutterJNI;
35   @NonNull
36   private final Map<String, BinaryMessenger.BinaryMessageHandler> messageHandlers;
37   @NonNull
38   private final Map<Integer, BinaryMessenger.BinaryReply> pendingReplies;
39   private int nextReplyId = 1;
40 
DartMessenger(@onNull FlutterJNI flutterJNI)41   DartMessenger(@NonNull FlutterJNI flutterJNI) {
42     this.flutterJNI = flutterJNI;
43     this.messageHandlers = new HashMap<>();
44     this.pendingReplies = new HashMap<>();
45   }
46 
47   @Override
setMessageHandler(@onNull String channel, @Nullable BinaryMessenger.BinaryMessageHandler handler)48   public void setMessageHandler(@NonNull String channel, @Nullable BinaryMessenger.BinaryMessageHandler handler) {
49     if (handler == null) {
50       Log.v(TAG, "Removing handler for channel '" + channel + "'");
51       messageHandlers.remove(channel);
52     } else {
53       Log.v(TAG, "Setting handler for channel '" + channel + "'");
54       messageHandlers.put(channel, handler);
55     }
56   }
57 
58   @Override
59   @UiThread
send(@onNull String channel, @NonNull ByteBuffer message)60   public void send(@NonNull String channel, @NonNull ByteBuffer message) {
61     Log.v(TAG, "Sending message over channel '" + channel + "'");
62     send(channel, message, null);
63   }
64 
65   @Override
send( @onNull String channel, @Nullable ByteBuffer message, @Nullable BinaryMessenger.BinaryReply callback )66   public void send(
67       @NonNull String channel,
68       @Nullable ByteBuffer message,
69       @Nullable BinaryMessenger.BinaryReply callback
70   ) {
71     Log.v(TAG, "Sending message with callback over channel '" + channel + "'");
72     int replyId = 0;
73     if (callback != null) {
74       replyId = nextReplyId++;
75       pendingReplies.put(replyId, callback);
76     }
77     if (message == null) {
78       flutterJNI.dispatchEmptyPlatformMessage(channel, replyId);
79     } else {
80       flutterJNI.dispatchPlatformMessage(channel, message, message.position(), replyId);
81     }
82   }
83 
84   @Override
handleMessageFromDart( @onNull final String channel, @Nullable byte[] message, final int replyId )85   public void handleMessageFromDart(
86       @NonNull final String channel,
87       @Nullable byte[] message,
88       final int replyId
89   ) {
90     Log.v(TAG, "Received message from Dart over channel '" + channel + "'");
91     BinaryMessenger.BinaryMessageHandler handler = messageHandlers.get(channel);
92     if (handler != null) {
93       try {
94         Log.v(TAG, "Deferring to registered handler to process message.");
95         final ByteBuffer buffer = (message == null ? null : ByteBuffer.wrap(message));
96         handler.onMessage(buffer, new Reply(flutterJNI, replyId));
97       } catch (Exception ex) {
98         Log.e(TAG, "Uncaught exception in binary message listener", ex);
99         flutterJNI.invokePlatformMessageEmptyResponseCallback(replyId);
100       }
101     } else {
102       Log.v(TAG, "No registered handler for message. Responding to Dart with empty reply message.");
103       flutterJNI.invokePlatformMessageEmptyResponseCallback(replyId);
104     }
105   }
106 
107   @Override
handlePlatformMessageResponse(int replyId, @Nullable byte[] reply)108   public void handlePlatformMessageResponse(int replyId, @Nullable byte[] reply) {
109     Log.v(TAG, "Received message reply from Dart.");
110     BinaryMessenger.BinaryReply callback = pendingReplies.remove(replyId);
111     if (callback != null) {
112       try {
113         Log.v(TAG, "Invoking registered callback for reply from Dart.");
114         callback.reply(reply == null ? null : ByteBuffer.wrap(reply));
115       } catch (Exception ex) {
116         Log.e(TAG, "Uncaught exception in binary message reply handler", ex);
117       }
118     }
119   }
120 
121   /**
122    * Returns the number of pending channel callback replies.
123    *
124    * <p>When sending messages to the Flutter application using {@link BinaryMessenger#send(String,
125    * ByteBuffer, io.flutter.plugin.common.BinaryMessenger.BinaryReply)}, developers can optionally
126    * specify a reply callback if they expect a reply from the Flutter application.
127    *
128    * <p>This method tracks all the pending callbacks that are waiting for response, and is supposed
129    * to be called from the main thread (as other methods). Calling from a different thread could
130    * possibly capture an indeterministic internal state, so don't do it.
131    */
132   @UiThread
getPendingChannelResponseCount()133   public int getPendingChannelResponseCount() {
134     return pendingReplies.size();
135   }
136 
137   private static class Reply implements BinaryMessenger.BinaryReply {
138     @NonNull
139     private final FlutterJNI flutterJNI;
140     private final int replyId;
141     private final AtomicBoolean done = new AtomicBoolean(false);
142 
Reply(@onNull FlutterJNI flutterJNI, int replyId)143     Reply(@NonNull FlutterJNI flutterJNI, int replyId) {
144       this.flutterJNI = flutterJNI;
145       this.replyId = replyId;
146     }
147 
148     @Override
reply(@ullable ByteBuffer reply)149     public void reply(@Nullable ByteBuffer reply) {
150       if (done.getAndSet(true)) {
151         throw new IllegalStateException("Reply already submitted");
152       }
153       if (reply == null) {
154         flutterJNI.invokePlatformMessageEmptyResponseCallback(replyId);
155       } else {
156         flutterJNI.invokePlatformMessageResponseCallback(replyId, reply, reply.position());
157       }
158     }
159   }
160 }
161