Flutter uses a flexible system that allows developers to call platform-specific apis, whether in Java or Kotlin code on Android or in ObjectiveC or Swift code on iOS.

The Flutter platform-specific API support does not rely on code generation, but on a flexible way of messaging:

  • The Flutter part of the application sends messages to its app host (iOS or Android) via platform channels.
  • The platform channel on which the host listens and receives the message. It then invokes the platform-specific API (using the native programming language) — and sends the response back to the client, the Flutter part of the application.

Get your phone’s battery power from Flutter on Android.

1.0 Obtaining battery power

First look at the code of the Flutter layer, need to take a Channel only one name in the whole application, such as samples. The Flutter. IO/’.

import 'dart:async';

import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; . class _MyHomePageState extends State<MyHomePage> { static const platform = const MethodChannel('samples.flutter.io/battery');
String _batteryLevel = 'Unknown battery level.';

  // Get battery level.
}
Copy the code

The method on the channel is then called with a string identifying the called method, such as getBatteryLevel, which may also fail and can be wrapped with a try-catch.

The returned result updates the user interface state with setState,_batteryLevel, where await will return the Future and will be assigned to Result only after receiving the battery result returned by the Android platform. Then the code goes down and assigns batteryLevel and updates the state.

Future<Null> _getBatteryLevel() async {
    String batteryLevel;

    try {
      final int result = await platform.invokeMethod('getBatteryLevel');
      batteryLevel = 'Battery level at $result % .';
    } on PlatformException catch (e) {
      batteryLevel = "Failed to get battery level: '${e.message}'.";
    }

    setState(() {
      _batteryLevel = batteryLevel;
    });
  }
Copy the code

Finally, create a button in Build to refresh the value and a Text to display the power value.

  @override
  Widget build(BuildContext context) {
    // This method is rerun every time setState is called, for instance as done// by the _incrementCounter method above. // // The Flutter framework has been optimized to make rerunning build methods  // fast, so that you can just rebuild anything that needs updating rather // than having to individually change instances of widgets.return Material(
      child: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.spaceEvenly,
          children: <Widget>[
            new RaisedButton(
              child: Text('Get Battery Level'),
              onPressed: _getBatteryLevel,
            ),
            Text(_batteryLevel),
          ],
        ),
      ),
    );
  }
Copy the code

This is the code for the Flutter layer. Let’s take an example of how to implement the Platform layer on Android. In MainActivity, we need to define a channel name that is the same as the Flutter layer and return the Result to the Flutter as Result:

private static final String CHANNEL = "samples.flutter.io/battery";

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    new MethodChannel(getFlutterView(), CHANNEL).setMethodCallHandler((call, result) -> {
      if (call.method.equals("getBatteryLevel")) {
        int batteryLevel = getBatteryLevel();

        if(batteryLevel ! = -1) { result.success(batteryLevel); }else {
          result.error("UNAVAILABLE"."Battery level not available.", null); }}else{ result.notImplemented(); }}); GeneratedPluginRegistrant.registerWith(this); }Copy the code

Next add Java code to get the battery power using the Android battery API:

  private int getBatteryLevel() {
    int batterylevel = -1;
    if (VERSION.SDK_INT > VERSION_CODES.LOLLIPOP) {
      BatteryManager batteryManager = (BatteryManager) getSystemService(BATTERY_SERVICE);
      batterylevel = batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY);
    } else {
      Intent intent = new ContextWrapper(getApplicationContext()).registerReceiver(null,
          new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
      batterylevel = (intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1) * 100)
          / intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
    }

    return batterylevel;
  }
Copy the code

Take a look at the rendering:

Next, let’s look at the principle of Flutter and Native communication, mainly the principle of the upper Flutter active emission.

2.0 FlutterLayer principle

Take a look at one of the official communications:

2.1 MethodChannel

The entry is MethodChannel, so let’s start with this code:

### platform_channel.dart

class MethodChannel {
const MethodChannel(this.name, [this.codec = const StandardMethodCodec()]);

  /// The logical channel on whichcommunication happens, not null. final String name; /// The message codec used by this channel, not null. final MethodCodec codec; @optionalTypeArgs Future<T> invokeMethod<T>(String method, [dynamic arguments]) async { assert(method ! = null); final ByteData result = await BinaryMessages.send( name, codec.encodeMethodCall(MethodCall(method, arguments)), );if (result == null) {
      throw MissingPluginException('No implementation found for method $method on channel $name');
    }
    final T typedResult = codec.decodeEnvelope(result);
    return typedResult;
  }
Copy the code

The constructor requires a name and optional MethodCodec, which is the identity of the MethodChannel, and MethodCodec, which is the codec that determines what type of data we can pass. Take a look at the data provided on the official website

MethodCodec is optional, default is StandardMethodCodec, which is used when you initiate a method call invokeMethod. StandardMethodCodec serializes the method names and parameters into binary, receives the result returned by the platform as binary data, and deserializes it into the Dart data type.

2.2 invokeMethod

In addition, invokeMethod is an asynchronous method that returns a Future

type and must be await when receiving the return value of this method. To call with await, you must run in a function with the async flag.

Then send the binarymessages.send message

2.3 BinaryMessages.send

### platform_messages.dart

static Future<ByteData> send(String channel, ByteData message) {
    final _MessageHandler handler = _mockHandlers[channel];
    if(handler ! = null)return handler(message);
    return _sendPlatformMessage(channel, message);
  }
Copy the code

Send is used to send binary messages to the channel corresponding to the platform plug-in.

So _mockHandlers is used for debugging, so if you look at the data structure, if you set up a debugging Handler it just returns, it doesn’t talk to the platform.

### platform_messages.dart

// Mock handlers that intercept and respond to outgoing messages.
  static final Map<String, _MessageHandler> _mockHandlers =
      <String, _MessageHandler>{};

static void setMockMessageHandler(String channel, Future<ByteData> handler(ByteData message)) {
    if (handler == null)
      _mockHandlers.remove(channel);
    else
      _mockHandlers[channel] = handler;
  }
Copy the code

Now let’s look at the call to _sendPlatformMessage in send

2.4 _sendPlatformMessage

### platform_messages.dart

  static Future<ByteData> _sendPlatformMessage(String channel, ByteData message) {
    final Completer<ByteData> completer = Completer<ByteData>();
    // ui.window is accessed directly instead of using ServicesBinding.instance.window
    // because this method might be invoked before any binding is initialized.
    // This issue was reported in #27541. It is not ideal to statically access
    // ui.window because the Window may be dependency injected elsewhere with
    // a different instance. However, static access at this location seems to be
    // the least bad option.
    ui.window.sendPlatformMessage(channel, message, (ByteData reply) {
      try {
        completer.complete(reply);
      } catch (exception, stack) {
        FlutterError.reportError(FlutterErrorDetails(
          exception: exception,
          stack: stack,
          library: 'services library',
          context: 'during a platform message response callback')); }});return completer.future;
  }
Copy the code

The Completer above is an object that can be used to control the Future, because normal futures are actually created and executed or queued up for execution, and the user can only passively receive the Future’s callback. Completer can execute the Future with completer.plete or Completer. Error.

SendPlatformMessage is called under the window

2.5 sendPlatformMessage

  void sendPlatformMessage(String name,
                           ByteData data,
                           PlatformMessageResponseCallback callback) {
    final String error =
        _sendPlatformMessage(name, _zonedPlatformMessageResponseCallback(callback), data);
    if(error ! = null) throw new Exception(error); } String _sendPlatformMessage(String name, PlatformMessageResponseCallback callback, ByteData data) native'Window_sendPlatformMessage';
Copy the code

There is a native method called, so we need to find this native method. You need to download the Engine layer code at: Engine.

3.0 EngineLayer principle

3.1 _SendPlatformMessage

Use the identifier Window_sendPlatformMessage above to go to the Engine layer to find the registration method:

### window.cc

  natives->Register({
      {"Window_defaultRouteName", DefaultRouteName, 1, true},
      {"Window_scheduleFrame", ScheduleFrame, 1, true},
      {"Window_sendPlatformMessage", _SendPlatformMessage, 4, true},
      {"Window_respondToPlatformMessage", _RespondToPlatformMessage, 3, true},
      {"Window_render", Render, 2, true},
      {"Window_updateSemantics", UpdateSemantics, 2, true},
      {"Window_setIsolateDebugName", SetIsolateDebugName, 2, true},
      {"Window_reportUnhandledException", ReportUnhandledException, 2, true}}); } void _SendPlatformMessage(Dart_NativeArguments args) { tonic::DartCallStatic(&SendPlatformMessage, args); }Copy the code

It calls the SendPlatformMessage method.

3.2 windowSendPlatformMessage

Platform messages can only be sent from the main ISOLATE. Call to MethodChannel can only be sent from the main ISOLATE.

### window.cc

Dart_Handle SendPlatformMessage(Dart_Handle window,
                                const std::string& name,
                                Dart_Handle callback,
                                Dart_Handle data_handle) {
  UIDartState* dart_state = UIDartState::Current();

  if(! dart_state->window()) {return tonic::ToDart(
        "Platform messages can only be sent from the main isolate");
  }

  fml::RefPtr<PlatformMessageResponse> response;
  if(! Dart_IsNull(callback)) { response = fml::MakeRefCounted<PlatformMessageResponseDart>( tonic::DartPersistentValue(dart_state, callback), dart_state->GetTaskRunners().GetUITaskRunner()); }if (Dart_IsNull(data_handle)) {
    dart_state->window()->client()->HandlePlatformMessage(
        fml::MakeRefCounted<PlatformMessage>(name, response));
  } else {
    tonic::DartByteData data(data_handle);
    const uint8_t* buffer = static_cast<const uint8_t*>(data.data());
    dart_state->window()->client()->HandlePlatformMessage(
        fml::MakeRefCounted<PlatformMessage>(
            name, std::vector<uint8_t>(buffer, buffer + data.length_in_bytes()),
            response));
  }

  return Dart_Null();
}
Copy the code

Then the main line of code eventually calls the virtual method HandlePlatformMessage in The WindowClient

dart_state->window()->client()->HandlePlatformMessage
Copy the code

3.3 windowWindowClient

Take a look at the client definition:

### window.h
class Window final {
 public:
  explicit Window(WindowClient* client);

  ~Window();

  WindowClient* client() const { returnclient_; }... } class WindowClient { public: virtual std::string DefaultRouteName() = 0; virtual void ScheduleFrame() = 0; virtual void Render(Scene* scene) = 0; virtual void UpdateSemantics(SemanticsUpdate* update) = 0; virtual void HandlePlatformMessage(fml::RefPtr<PlatformMessage> message) = 0; virtual FontCollection& GetFontCollection() = 0; virtual void UpdateIsolateDescription(const std::string isolate_name, int64_t isolate_port) = 0; protected: virtual ~WindowClient(); };Copy the code

If you run into a pure virtual function, you have to find a class that inherits from Windows Client.

3.4 RuntimeController

Find only one place to inherit:

class RuntimeController final : public WindowClient
Copy the code

Inside this class:

### RuntimeController.cc

void RuntimeController::HandlePlatformMessage(
    fml::RefPtr<PlatformMessage> message) {
  client_.HandlePlatformMessage(std::move(message));
}
Copy the code

Finally, it is called through client_, which looks like proxy mode. Then look for client_ and see in the constructor:

### RuntimeController.cc

RuntimeController::RuntimeController(
    RuntimeDelegate& p_client,
    DartVM* p_vm,
    fml::RefPtr<const DartSnapshot> p_isolate_snapshot,
    fml::RefPtr<const DartSnapshot> p_shared_snapshot,
    TaskRunners p_task_runners,
    fml::WeakPtr<SnapshotDelegate> p_snapshot_delegate,
    fml::WeakPtr<IOManager> p_io_manager,
    std::string p_advisory_script_uri,
    std::string p_advisory_script_entrypoint,
    std::function<void(int64_t)> idle_notification_callback,
    WindowData p_window_data)
    : client_(p_client),
      vm_(p_vm),
      isolate_snapshot_(std::move(p_isolate_snapshot)),
      shared_snapshot_(std::move(p_shared_snapshot)),
      task_runners_(p_task_runners),
      snapshot_delegate_(p_snapshot_delegate),
      io_manager_(p_io_manager),
      advisory_script_uri_(p_advisory_script_uri),
      advisory_script_entrypoint_(p_advisory_script_entrypoint),
      idle_notification_callback_(idle_notification_callback),
      window_data_(std::move(p_window_data)),
      root_isolate_(
          DartIsolate::CreateRootIsolate(vm_->GetVMData()->GetSettings(),
                                         isolate_snapshot_,
                                         shared_snapshot_,
                                         task_runners_,
                                         std::make_unique<Window>(this),
                                         snapshot_delegate_,
                                         io_manager_,
                                         p_advisory_script_uri,
                                         p_advisory_script_entrypoint)) {
  std::shared_ptr<DartIsolate> root_isolate = root_isolate_.lock();
  root_isolate->SetReturnCodeCallback([this](uint32_t code) {
    root_isolate_return_code_ = {true, code};
  });
Copy the code

As you can see, with client_ which is actually RuntimeDelegate,

3.5 RuntimeDelegate

So let’s move on to the RuntimeDelegate, and then we run into pure virtual functions.

### runtime_delegate.h

namespace blink {

class RuntimeDelegate {
 public:
  virtual std::string DefaultRouteName() = 0;

  virtual void ScheduleFrame(bool regenerate_layer_tree = true) = 0;

  virtual void Render(std::unique_ptr<flow::LayerTree> layer_tree) = 0;

  virtual void UpdateSemantics(
      blink::SemanticsNodeUpdates update,
      blink::CustomAccessibilityActionUpdates actions) = 0;

  virtual void HandlePlatformMessage(fml::RefPtr<PlatformMessage> message) = 0;

  virtual FontCollection& GetFontCollection() = 0;

  virtual void UpdateIsolateDescription(const std::string isolate_name,
                                        int64_t isolate_port) = 0;

 protected:
  virtual ~RuntimeDelegate();
};

}  // namespace blink
Copy the code

It’s just a matter of seeing which class inherits RuntimeDelegate, and only one class, Engine, inherits.

3.6 Engine

### engine.h

class Engine final : public blink::RuntimeDelegate
Copy the code

See the code in engine.cc:

### engine.cc

void Engine::HandlePlatformMessage(
    fml::RefPtr<blink::PlatformMessage> message) {
  if (message->channel() == kAssetChannel) {
    HandleAssetPlatformMessage(std::move(message));
  } else{ delegate_.OnEngineHandlePlatformMessage(std::move(message)); }}Copy the code

Engine lazy delegate_, see Engine constructor:

### engine.cc

Engine::Engine(Delegate& delegate,
               blink::DartVM& vm,
               fml::RefPtr<const blink::DartSnapshot> isolate_snapshot,
               fml::RefPtr<const blink::DartSnapshot> shared_snapshot,
               blink::TaskRunners task_runners,
               blink::Settings settings,
               std::unique_ptr<Animator> animator,
               fml::WeakPtr<blink::SnapshotDelegate> snapshot_delegate,
               fml::WeakPtr<blink::IOManager> io_manager)
    : delegate_(delegate),
      settings_(std::move(settings)),
      animator_(std::move(animator)),
      activity_running_(false),
      have_surface_(false),
      weak_factory_(this) {
  // Runtime controller is initialized here because it takes a reference to this
  // object as its delegate. The delegate may be called in the constructor and
  // we want to be fully initilazed by that point.
  runtime_controller_ = std::make_unique<blink::RuntimeController>(
      *this,                                 // runtime delegate
      &vm,                                   // VM
      std::move(isolate_snapshot),           // isolate snapshot
      std::move(shared_snapshot),            // shared snapshot
      std::move(task_runners),               // task runners
      std::move(snapshot_delegate),          // snapshot delegate
      std::move(io_manager),                 // io manager
      settings_.advisory_script_uri,         // advisory script uri
      settings_.advisory_script_entrypoint,  // advisory script entrypoint
      settings_.idle_notification_callback   // idle notification callback
  );
}
Copy the code

The Delegate is defined in engine.h:

### engine.h

class Delegate {
   public:
    virtual void OnEngineUpdateSemantics(
        blink::SemanticsNodeUpdates update,
        blink::CustomAccessibilityActionUpdates actions) = 0;

    virtual void OnEngineHandlePlatformMessage(
        fml::RefPtr<blink::PlatformMessage> message) = 0;

    virtual void OnPreEngineRestart() = 0;

    virtual void UpdateIsolateDescription(const std::string isolate_name,
                                          int64_t isolate_port) = 0;
  };
Copy the code

Is familiar with pure virtual functions OnEngineHandlePlatformMessage, back to the Engine, and see which side is constructed this object can find delegate_, finally find the shell.

3.7 Shell

In the shell, there is a field attribute called Engine that is constructed by passing the shell into it, so the top delegate_ is the shell

### shell.cc

  std::unique_ptr<Engine> engine;
  fml::TaskRunner::RunNowOrPostTask(
      shell->GetTaskRunners().GetUITaskRunner(),
      fml::MakeCopyable([&ui_latch,                                         //
                         &engine,                                           //
                         shell = shell.get(),                               //
                         isolate_snapshot = std::move(isolate_snapshot),    //
                         shared_snapshot = std::move(shared_snapshot),      //
                         vsync_waiter = std::move(vsync_waiter),            //
                         snapshot_delegate = std::move(snapshot_delegate),  //
                         io_manager = io_manager->GetWeakPtr()              //
  ]() mutable {
        TRACE_EVENT0("flutter"."ShellSetupUISubsystem");
        const auto& task_runners = shell->GetTaskRunners();

        // The animator is owned by the UI thread but it gets its vsync pulses
        // from the platform.
        auto animator = std::make_unique<Animator>(*shell, task_runners,
                                                   std::move(vsync_waiter));

        engine = std::make_unique<Engine>(*shell,                        //
                                          *shell->GetDartVM(),           //
                                          std::move(isolate_snapshot),   //
                                          std::move(shared_snapshot),    //
                                          task_runners,                  //
                                          shell->GetSettings(),          //
                                          std::move(animator),           //
                                          std::move(snapshot_delegate),  //
                                          std::move(io_manager)          //
        );
        ui_latch.Signal();
      }));
Copy the code

Found in the shell OnEngineHandlePlatformMessage method:

### shell.cc

// |shell::Engine::Delegate|
void Shell::OnEngineHandlePlatformMessage(
    fml::RefPtr<blink::PlatformMessage> message) {
  FML_DCHECK(is_setup_);
  FML_DCHECK(task_runners_.GetUITaskRunner()->RunsTasksOnCurrentThread());

  if (message->channel() == kSkiaChannel) {
    HandleEngineSkiaMessage(std::move(message));
    return;
  }

  task_runners_.GetPlatformTaskRunner()->PostTask(
      [view = platform_view_->GetWeakPtr(), message = std::move(message)]() {
        if(view) { view->HandlePlatformMessage(std::move(message)); }}); }Copy the code

So let’s look at the first if judgment, if it equals kSkiaChannel it’s going to return, so let’s look at the value of kSkiaChannel

constexpr char kSkiaChannel[] = "flutter/skia";
Copy the code

It is obvious that our custom channel will not go into the judgment logic, which is to throw a task to PlatformTaskRunner. The detailed description of thread can be seen in the last summary, because it is long, in order to call the coherence of the logic.

3.8 PlatformViewPlatformViewAndroid

PlatformView, but it’s a virtual function:

### platform_view.h

virtual void HandlePlatformMessage(
      fml::RefPtr<blink::PlatformMessage> message);
Copy the code

Then find its descendant class PlatformViewAndroid

### platform_view_android.cc// |shell::PlatformView| void PlatformViewAndroid::HandlePlatformMessage( fml::RefPtr<blink::PlatformMessage> message) {  JNIEnv* env = fml::jni::AttachCurrentThread(); fml::jni::ScopedJavaLocalRef<jobject> view = java_object_.get(env);if (view.is_null())
    return;

  int response_id = 0;
  if (auto response = message->response()) {
    response_id = next_response_id_++;
    pending_responses_[response_id] = response;
  }
  auto java_channel = fml::jni::StringToJavaString(env, message->channel());
  if (message->hasData()) {
    fml::jni::ScopedJavaLocalRef<jbyteArray> message_array(
        env, env->NewByteArray(message->data().size()));
    env->SetByteArrayRegion(
        message_array.obj(), 0, message->data().size(),
        reinterpret_cast<const jbyte*>(message->data().data()));
    message = nullptr;

    // This call can re-enter in InvokePlatformMessageXxxResponseCallback.
    FlutterViewHandlePlatformMessage(env, view.obj(), java_channel.obj(),
                                     message_array.obj(), response_id);
  } else {
    message = nullptr;

    // This call can re-enter inInvokePlatformMessageXxxResponseCallback. FlutterViewHandlePlatformMessage(env, view.obj(), java_channel.obj(), nullptr, response_id); }}Copy the code

You can already see a taste of Java in this method:

  • First, record the ID of a callbackresponse_id, will put the callback intopending_responses_In the
 std::unordered_map<int, fml::RefPtr<blink::PlatformMessageResponse>>
      pending_responses_;
Copy the code
  • Call StringToJavaString below JNI to convert Channel name to Java string;

  • If the call takes parameters, jni:: env->NewByteArray is called to turn into a JNI array

  • The last call FlutterViewHandlePlatformMessage

This function is defined in platform_view_android_jni.cc

3.9 FlutterViewHandlePlatformMessage

You can see that the JNI env has started calling the JNI function table registered in the virtual machine.

### platform_view_android_jni.cc

static jmethodID g_handle_platform_message_method = nullptr;
void FlutterViewHandlePlatformMessage(JNIEnv* env,
                                      jobject obj,
                                      jstring channel,
                                      jobject message,
                                      jint responseId) {
  env->CallVoidMethod(obj, g_handle_platform_message_method, channel, message,
                      responseId);
  FML_CHECK(CheckException(env));
}
Copy the code

The key is to find the corresponding method for the g_handle_platform_message_method symbol table,

### platform_view_android_jni.cc

g_handle_platform_message_method =
      env->GetMethodID(g_flutter_jni_class->obj(), "handlePlatformMessage"."(Ljava/lang/String; [BI)V");
Copy the code

Those of you who have done JNI know that handlePlatformMessage is the Android equivalent.

3.10 FlutterJNI.java

Finally came to the Java layer, suddenly feel Java so friendly.

### FlutterJNI.java

  // Called by native.
  @SuppressWarnings("unused")
  private void handlePlatformMessage(final String channel, byte[] message, final int replyId) {
    if(platformMessageHandler ! = null) { platformMessageHandler.handleMessageFromDart(channel, message, replyId); } // TODO(mattcarroll):log dropped messages when in debug mode (https://github.com/flutter/flutter/issues/25391)
  }
Copy the code

Next, let’s start with the code written on the Android layer

4.0 JavaLayer principle

4.1 MethodChannel.java

Going back to the first verse,

### MainActivity.java
new MethodChannel(getFlutterView(), CHANNEL).setMethodCallHandler((call, result) -> {})

Copy the code
### MethodChannel.java

private final BinaryMessenger messenger;

public MethodChannel(BinaryMessenger messenger, String name) {
        this(messenger, name, StandardMethodCodec.INSTANCE);
    }

    public void setMethodCallHandler(@Nullable MethodChannel.MethodCallHandler handler) {
        this.messenger.setMessageHandler(this.name, handler == null ? null : new MethodChannel.IncomingMethodCallHandler(handler));
    }

Copy the code

4.2 MethodChannel.java

BinaryMessenger is an interface that implements multiple classes. In our constructor we pass in FlutterView,

It will eventually be called under the FlutterNativeView

### FlutterNativeView.java

private final Map<String, BinaryMessageHandler> mMessageHandlers;

public void setMessageHandler(String channel, BinaryMessageHandler handler) {
        if (handler == null) {
            this.mMessageHandlers.remove(channel);
        } else{ this.mMessageHandlers.put(channel, handler); }}Copy the code

Is the platformMessageHandler under FlutterJNI related to the FlutterNativeView?

Look at the constructor of the FlutterNativeView:

### FlutterNativeView.java

    public FlutterNativeView(Context context, boolean isBackgroundView) {
        this.mNextReplyId = 1;
        this.mPendingReplies = new HashMap();
        this.mContext = context;
        this.mPluginRegistry = new FlutterPluginRegistry(this, context);
        this.mFlutterJNI = new FlutterJNI();
        this.mFlutterJNI.setRenderSurface(new FlutterNativeView.RenderSurfaceImpl());
        this.mFlutterJNI.setPlatformMessageHandler(new FlutterNativeView.PlatformMessageHandlerImpl());
        this.mFlutterJNI.addEngineLifecycleListener(new FlutterNativeView.EngineLifecycleListenerImpl());
        this.attach(this, isBackgroundView);
        this.assertAttached();
        this.mMessageHandlers = new HashMap();
    }

Copy the code

Can see FlutterNativeView holds FlutterJNI PlatformMessageHandlerImpl is also its inner class:

### FlutterNativeView.java

private final class PlatformMessageHandlerImpl implements PlatformMessageHandler {
        private PlatformMessageHandlerImpl() {
        }

        public void handleMessageFromDart(final String channel, byte[] message, final int replyId) {
            FlutterNativeView.this.assertAttached();
            BinaryMessageHandler handler = (BinaryMessageHandler)FlutterNativeView.this.mMessageHandlers.get(channel);
            if(handler ! = null) { try { ByteBuffer buffer = message == null ? null : ByteBuffer.wrap(message); handler.onMessage(buffer, newBinaryReply() {
                        private final AtomicBoolean done = new AtomicBoolean(false);

                        public void reply(ByteBuffer reply) {
                            if(! FlutterNativeView.this.isAttached()) { Log.d("FlutterNativeView"."handleMessageFromDart replying ot a detached view, channel=" + channel);
                            } else if (this.done.getAndSet(true)) {
                                throw new IllegalStateException("Reply already submitted");
                            } else {
                                if (reply == null) {
                                    FlutterNativeView.this.mFlutterJNI.invokePlatformMessageEmptyResponseCallback(replyId);
                                } else{ FlutterNativeView.this.mFlutterJNI.invokePlatformMessageResponseCallback(replyId, reply, reply.position()); }}}}); } catch (Exception var6) { Log.e("FlutterNativeView"."Uncaught exception in binary message listener", var6); FlutterNativeView.this.mFlutterJNI.invokePlatformMessageEmptyResponseCallback(replyId); }}else{ FlutterNativeView.this.mFlutterJNI.invokePlatformMessageEmptyResponseCallback(replyId); }}... }Copy the code
  • The previously registered ones are first fetched from mMessageHandlersMethodChannelAnd then callonMessageHow? Some of you found out that we passedsetMethodCallHandlerSet up theMethodCallHandler, there is noonMessagemethods
public interface MethodCallHandler {
        void onMethodCall(MethodCall var1, MethodChannel.Result var2);
    }
Copy the code

Actually there is a detail didn’t say, pass in MethodCallHandler will construct into IncomingMethodCallHandler

### MethodChannel.java

IncomingMethodCallHandler(MethodChannel.MethodCallHandler handler) {
            this.handler = handler;
}

public void onMessage(ByteBuffer message, final BinaryReply reply) {
            MethodCall call = MethodChannel.this.codec.decodeMethodCall(message);

            try {
                this.handler.onMethodCall(call, new MethodChannel.Result() { public void success(Object result) { reply.reply(MethodChannel.this.codec.encodeSuccessEnvelope(result)); } public void error(String errorCode, String errorMessage, Object errorDetails) { reply.reply(MethodChannel.this.codec.encodeErrorEnvelope(errorCode, errorMessage, errorDetails));  } public voidnotImplemented() { reply.reply((ByteBuffer)null); }}); } catch (RuntimeException var5) { Log.e("MethodChannel#" + MethodChannel.this.name, "Failed to handle method call", var5);
                reply.reply(MethodChannel.this.codec.encodeErrorEnvelope("error", var5.getMessage(), (Object)null)); }}Copy the code
  • And then it goes throughinvokePlatformMessageResponseCallbackThe callback back
FlutterNativeView.this.mFlutterJNI.invokePlatformMessageResponseCallback(replyId, reply, reply.position());

Copy the code

5.0 the callback

The callback can be found in the Engine layer using replyId above, which is essentially a callback back along the reverse path above.

5.1 FlutterJNI.java

@UiThread
    public void invokePlatformMessageResponseCallback(int responseId, ByteBuffer message, int position) {
        this.ensureAttachedToNative();
        this.nativeInvokePlatformMessageResponseCallback(this.nativePlatformViewId, responseId, message, position);
    }

    private native void nativeInvokePlatformMessageResponseCallback(long var1, int var3, ByteBuffer var4, int var5);
Copy the code

The Engine layer is called back through native JNI

5.2 platform_view_android_jni.cc

### platform_view_android_jni.cc

static void InvokePlatformMessageResponseCallback(JNIEnv* env,
                                                  jobject jcaller,
                                                  jlong shell_holder,
                                                  jint responseId,
                                                  jobject message,
                                                  jint position) {
  ANDROID_SHELL_HOLDER->GetPlatformView()
      ->InvokePlatformMessageResponseCallback(env,         //
                                              responseId,  //
                                              message,     //
                                              position     //
      );
}
Copy the code

5.3 platform_view_android.cc

The main thing here is to find the callback that was previously registered in HandlePlatformMessage and call it:

### platform_view_android.cc

void PlatformViewAndroid::InvokePlatformMessageResponseCallback(
    JNIEnv* env,
    jint response_id,
    jobject java_response_data,
    jint java_response_position) {
  if(! response_id)return;
  auto it = pending_responses_.find(response_id);
  if (it == pending_responses_.end())
    return;
  uint8_t* response_data =
      static_cast<uint8_t*>(env->GetDirectBufferAddress(java_response_data));
  std::vector<uint8_t> response = std::vector<uint8_t>(
      response_data, response_data + java_response_position);
  auto message_response = std::move(it->second);
  pending_responses_.erase(it);
  message_response->Complete(
      std::make_unique<fml::DataMapping>(std::move(response)));
}
Copy the code

Finally, call the Complete() method:

5.4 platform_message_response_dart

### platform_message_response_dart.cc

void PlatformMessageResponseDart::Complete(std::unique_ptr<fml::Mapping> data) {
  if (callback_.is_empty())
    return; FML_DCHECK(! is_complete_); is_complete_ =true;
  ui_task_runner_->PostTask(fml::MakeCopyable(
      [callback = std::move(callback_), data = std::move(data)]() mutable {
        std::shared_ptr<tonic::DartState> dart_state =
            callback.dart_state().lock();
        if(! dart_state)return;
        tonic::DartState::Scope scope(dart_state);

        Dart_Handle byte_buffer = WrapByteData(std::move(data));
        tonic::DartInvoke(callback.Release(), {byte_buffer});
      }));
}
Copy the code

One thing to note is that the callback is called in the UI thread, and the callback data is passed through WrapByteData serialization.

Dart this callback should be the same callback registered in window.dart:

### window.dart
void sendPlatformMessage(String name,
                           ByteData data,
                           PlatformMessageResponseCallback callback) {
    final String error =
        _sendPlatformMessage(name, _zonedPlatformMessageResponseCallback(callback), data);
    if(error ! = null) throw new Exception(error); } /// Wraps the given [callback]in another callback that ensures that the
  /// original callback is called in the zone it was registered in.
  static PlatformMessageResponseCallback _zonedPlatformMessageResponseCallback(PlatformMessageResponseCallback callback) {
    if (callback == null)
      return null;

    // Store the zone in which the callback is being registered.
    final Zone registrationZone = Zone.current;

    return (ByteData data) {
      registrationZone.runUnaryGuarded(callback, data);
    };
  }
Copy the code

The final reflection at the Dart level is the control callback of the Future through the Completer.

ui.window.sendPlatformMessage(channel, message, (ByteData reply) {
      try {
        completer.complete(reply);
      } catch (exception, stack) {
        FlutterError.reportError(FlutterErrorDetails(
          exception: exception,
          stack: stack,
          library: 'services library',
          context: 'during a platform message response callback')); }});Copy the code

6.0 the thread

6.1 TaskRunnerMessageLoop

If you look at TaskRunner, when you construct it, you pass in a MessageLoopImpl,

### task_runner.h
namespace fml {

class MessageLoopImpl;

class TaskRunner : public fml::RefCountedThreadSafe<TaskRunner> {
 public:
  virtual ~TaskRunner();

  virtual void PostTask(fml::closure task);

  virtual void PostTaskForTime(fml::closure task, fml::TimePoint target_time);

  virtual void PostDelayedTask(fml::closure task, fml::TimeDelta delay);

  virtual bool RunsTasksOnCurrentThread();

  static void RunNowOrPostTask(fml::RefPtr<fml::TaskRunner> runner,
                               fml::closure task);

 protected:
  TaskRunner(fml::RefPtr<MessageLoopImpl> loop);

 private:
  fml::RefPtr<MessageLoopImpl> loop_;

  FML_FRIEND_MAKE_REF_COUNTED(TaskRunner);
  FML_FRIEND_REF_COUNTED_THREAD_SAFE(TaskRunner);
  FML_DISALLOW_COPY_AND_ASSIGN(TaskRunner);
};

} 

Copy the code

When you see MessageLoop, you hold a task_runner_ and loop_,

### message_loop.h
class MessageLoop {
 public:
  FML_EMBEDDER_ONLY
  static MessageLoop& GetCurrent();

  bool IsValid() const;

  void Run();

  void Terminate();

  void AddTaskObserver(intptr_t key, fml::closure callback);

  void RemoveTaskObserver(intptr_t key);

  fml::RefPtr<fml::TaskRunner> GetTaskRunner() const;

  // Exposed for the embedder shell which allows clients to poll for events
  // instead of dedicating a thread to the message loop.
  void RunExpiredTasksNow();

  static void EnsureInitializedForCurrentThread();

  static bool IsInitializedForCurrentThread();

  ~MessageLoop();

 private:
  friend class TaskRunner;
  friend class MessageLoopImpl;

  fml::RefPtr<MessageLoopImpl> loop_;
  fml::RefPtr<fml::TaskRunner> task_runner_;

  MessageLoop();

  fml::RefPtr<MessageLoopImpl> GetLoopImpl() const;

  FML_DISALLOW_COPY_AND_ASSIGN(MessageLoop);
};

}
Copy the code

If you look at the assignment to the above two variables in the constructor, you can see that an instance of MessageLoopImpl is constructed and passed to the TaskRunner

### message_loop.cc

MessageLoop::MessageLoop()
    : loop_(MessageLoopImpl::Create()),
      task_runner_(fml::MakeRefCounted<fml::TaskRunner>(loop_)) {
  FML_CHECK(loop_);
  FML_CHECK(task_runner_);
}
Copy the code

Go back to TaskRunner.PostTask

### task_runner.cc

void TaskRunner::PostTask(fml::closure task) {
  loop_->PostTask(std::move(task), fml::TimePoint::Now());
}
Copy the code

Will eventually through loop_ namely MessageLoopImpl PostTask, then push to the delayed_tasks_,

### message_loop_impl.ccvoid MessageLoopImpl::PostTask(fml::closure task, fml::TimePoint target_time) { FML_DCHECK(task ! = nullptr); RegisterTask(task, target_time); } void MessageLoopImpl::RegisterTask(fml::closure task, fml::TimePoint target_time) { FML_DCHECK(task ! = nullptr);if (terminated_) {
    // If the message loop has already been terminated, PostTask should destruct
    // |task| synchronously within this function.
    return;
  }
  std::lock_guard<std::mutex> lock(delayed_tasks_mutex_);
  delayed_tasks_.push({++order_, std::move(task), target_time});
  WakeUp(delayed_tasks_.top().target_time);
}
Copy the code

And delayed_tasks_ is a priority queue:

### message_loop_impl.h

using DelayedTaskQueue = std::
      priority_queue<DelayedTask, std::deque<DelayedTask>, DelayedTaskCompare>;

DelayedTaskQueue delayed_tasks_;
Copy the code

To summarize, TaskRunner.PostTask ultimately puts tasks into queues in MessageLoop.

6.2 ThreadThreadHost

Who is responsible for fetching and running tasks that are put in queues? It’s time for the thread to come out. Let’s go to the Shell and look at the thread creation:

### shell_benchmarks.ccnamespace shell { static void StartupAndShutdownShell(benchmark::State& state, bool measure_startup, bool measure_shutdown) { std::unique_ptr<Shell> shell; std::unique_ptr<ThreadHost> thread_host; { benchmarking::ScopedPauseTiming pause(state, ! measure_startup); blink::Settings settings = {}; settings.task_observer_add = [](intptr_t, fml::closure) {}; settings.task_observer_remove = [](intptr_t) {}; // Measure the time it takes to setup the threads as well. thread_host = std::make_unique<ThreadHost>("io.flutter.bench.", ThreadHost::Type::Platform |
                                 ThreadHost::Type::GPU | ThreadHost::Type::IO |
                                 ThreadHost::Type::UI);

    blink::TaskRunners task_runners(
        "test", thread_host->platform_thread->GetTaskRunner(),
        thread_host->gpu_thread->GetTaskRunner(),
        thread_host->ui_thread->GetTaskRunner(),
        thread_host->io_thread->GetTaskRunner());

    shell = Shell::Create(
        std::move(task_runners), settings,
        [](Shell& shell) {
          return std::make_unique<PlatformView>(shell, shell.GetTaskRunners());
        },
        [](Shell& shell) {
          returnstd::make_unique<Rasterizer>(shell.GetTaskRunners()); }); } FML_CHECK(shell); { benchmarking::ScopedPauseTiming pause(state, ! measure_shutdown); shell.reset(); // Shutdown is synchronous. thread_host.reset(); } FML_CHECK(! shell); }... }Copy the code

Taskrunners are drawn from threadhosts.

### thread_host.h

namespace shell {

struct ThreadHost {
  enum Type {
    Platform = 1 << 0,
    UI = 1 << 1,
    GPU = 1 << 2,
    IO = 1 << 3,
  };

  std::unique_ptr<fml::Thread> platform_thread;
  std::unique_ptr<fml::Thread> ui_thread;
  std::unique_ptr<fml::Thread> gpu_thread;
  std::unique_ptr<fml::Thread> io_thread;

  ThreadHost();

  ThreadHost(ThreadHost&&);

  ThreadHost& operator=(ThreadHost&&) = default;

  ThreadHost(std::string name_prefix, uint64_t type_mask);

  ~ThreadHost();

  void Reset();
};

} 
Copy the code

There are four main threads: platform_thread, UI_thread, gpu_thread, io_thread.

### thread.cc

Thread::Thread(const std::string& name) : joined_(false) {
  fml::AutoResetWaitableEvent latch;
  fml::RefPtr<fml::TaskRunner> runner;
  thread_ = std::make_unique<std::thread>([&latch, &runner, name]() -> void {
    SetCurrentThreadName(name);
    fml::MessageLoop::EnsureInitializedForCurrentThread();
    auto& loop = MessageLoop::GetCurrent();
    runner = loop.GetTaskRunner();
    latch.Signal();
    loop.Run();
  });
  latch.Wait();
  task_runner_ = runner;
}

Copy the code

Specific logical steps:

    1. The logic to set the thread name is inThreadHostIn whichname_prefixIs in theshell_benchmarksDefined in theio.flutter.bench.
ThreadHost::ThreadHost(std::string name_prefix, uint64_t mask) {
  if (mask & ThreadHost::Type::Platform) {
    platform_thread = std::make_unique<fml::Thread>(name_prefix + ".platform");
  }

  if (mask & ThreadHost::Type::UI) {
    ui_thread = std::make_unique<fml::Thread>(name_prefix + ".ui");
  }

  if (mask & ThreadHost::Type::GPU) {
    gpu_thread = std::make_unique<fml::Thread>(name_prefix + ".gpu");
  }

  if (mask & ThreadHost::Type::IO) {
    io_thread = std::make_unique<fml::Thread>(name_prefix + ".io"); }}Copy the code
  • 2. The initializationMessageLoop
### thread.cc

fml::MessageLoop::EnsureInitializedForCurrentThread()
Copy the code
### message_loop.cc

FML_THREAD_LOCAL ThreadLocal tls_message_loop([](intptr_t value) {
  delete reinterpret_cast<MessageLoop*>(value);
});

void MessageLoop::EnsureInitializedForCurrentThread() {
  if(tls_message_loop.Get() ! = 0) { // Already initialized.return;
  }
  tls_message_loop.Set(reinterpret_cast<intptr_t>(new MessageLoop()));
}
Copy the code

A ThreadLocal holds MessageLoop to avoid repeated creation.

  • 3. Getloopandrunner
### thread.cc

auto& loop = MessageLoop::GetCurrent();
runner = loop.GetTaskRunner();
Copy the code
### message_loop.cc

MessageLoop& MessageLoop::GetCurrent() { auto* loop = reinterpret_cast<MessageLoop*>(tls_message_loop.Get()); FML_CHECK(loop ! = nullptr) <<"MessageLoop::EnsureInitializedForCurrentThread was not called on "
         "this thread prior to message loop use.";
  return *loop;
}

fml::RefPtr<fml::TaskRunner> MessageLoop::GetTaskRunner() const {
  return task_runner_;
}
Copy the code
  • 4.run messageloop

The last call

### thread.cc
loop.Run();
Copy the code
### message_loop.cc

void MessageLoop::Run() {
  loop_->DoRun();
}
Copy the code

Loop_ is MessageLoopImpl:

### message_loop_impl.cc

void MessageLoopImpl::DoRun() {
  if (terminated_) {
    // Message loops may be run only once.
    return;
  }

  // Allow the implementation to do its thing.
  Run();

  // The loop may have been implicitly terminated. This can happen if the
  // implementation supports termination via platform specific APIs or just
  // error conditions. Set the terminated flag manually.
  terminated_ = true;

  // The message loop is shutting down. Check if there are expired tasks. This
  // is the last chance for expired tasks to be serviced. Make sure the
  // terminated flag is already set so we don't accrue additional tasks now. RunExpiredTasksNow(); // When the message loop is in the process of shutting down, pending tasks // should be destructed on the message loop's thread. We have just returned
  // from the implementations |Run| method which we know is on the correct
  // thread. Drop all pending tasks on the floor.
  std::lock_guard<std::mutex> lock(delayed_tasks_mutex_);
  delayed_tasks_ = {};
}
Copy the code

The key is the Run() method, but unfortunately in MessageLoopImpl Run() is pure virtual:

class MessageLoopImpl : public fml::RefCountedThreadSafe<MessageLoopImpl> { public: static fml::RefPtr<MessageLoopImpl> Create(); virtual ~MessageLoopImpl(); virtual void Run() = 0; void PostTask(fml::closure task, fml::TimePoint target_time); void DoRun(); . }Copy the code

There are different implementations depending on the platform:

fml::RefPtr<MessageLoopImpl> MessageLoopImpl::Create() {
#if OS_MACOSX
  return fml::MakeRefCounted<MessageLoopDarwin>();
#elif OS_ANDROID
  return fml::MakeRefCounted<MessageLoopAndroid>();
#elif OS_LINUX
  return fml::MakeRefCounted<MessageLoopLinux>();
#elif OS_WIN
  return fml::MakeRefCounted<MessageLoopWin> ();#else
  return nullptr;
#endif
Copy the code

MessageLoopAndroid:

### message_loop_android.cc

void MessageLoopAndroid::Run() {
  FML_DCHECK(looper_.get() == ALooper_forThread());

  running_ = true;

  while (running_) {
    int result = ::ALooper_pollOnce(-1,       // infinite timeout
                                    nullptr,  // out fd,
                                    nullptr,  // out events,
                                    nullptr   // out data
    );
    if (result == ALOOPER_POLL_TIMEOUT || result == ALOOPER_POLL_ERROR) {
      // This handles the case where the loop is terminated using ALooper APIs.
      running_ = false; }}}Copy the code

At this point, ALooper_pollOnce is no longer in the loop, so the logic is to loop tasks out of the priority queue.

If you see the loop that Android developers are familiar with,

  • TaskRunnerSimilar to theHandler, which can be used to throw messages to the queue;
  • MessageLooperImplIt’s sort of likeMessageQueue;
  • whileMessageLoopAndroidIs similar to theLooper, loop to retrieve messages from the queue

Refs:

  • Because Chinese website
  • Asynchronism in Flutter/Dart
  • The secret between Flutter and primal