Flutter和原生之间的平台通道实践与原理

本文已授权「玉刚说」微信公众号独家发布

Flutter使用了一个灵活的系统,允许开发者调用特定平台的API,无论在Android上的Java或Kotlin代码中,还是iOS上的ObjectiveC或Swift代码中均可用。

Flutter平台特定的API支持不依赖于代码生成,而是依赖于灵活的消息传递的方式:

  • 应用的Flutter部分通过平台通道(platform channel)将消息发送到其应用程序的所在的宿主(iOS或Android)。
  • 宿主监听的平台通道,并接收该消息。然后它会调用特定于该平台的API(使用原生编程语言) - 并将响应发送回客户端,即应用程序的Flutter部分。

以Android平台上通过Flutter获取手机电池电量为栗子,栗子参考自看Flutter中文网下调用代码怎么写。

1.获取电池电量

首先看下Flutter层的代码,需要给Channel取一个整个应用中唯一的名称,比如samples.flutter.io/battery

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.
}

接下来调用通道上的方法,通过字符串标识调用的方法,比如getBatteryLevel,调用也可能失败,可以通过try-catch包装。

返回的结果通过setState更新用户界面状态,_batteryLevel,其中await会返回Future,在得到Android平台返回的电量结果后才会赋值给result,然后代码接着往下走赋值给batteryLevel,然后更新状态.

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;
    });
  }

最后,在build创建一个按钮用于刷新值,一个Text用于显示电量值。

  @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),
          ],
        ),
      ),
    );
  }

上面就是Flutter层的代码,接下来以Android平台为例看下平台层怎么实现。在MainActivity中需要定义一个和Flutter层相同的通道名称,通过Result返回结果给Flutter

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);
  }

接下来添加Java代码,使用Android电池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;
  }

看下效果图:

1.png

接下来看下FlutterNative通信的原理,主要是上面Flutter主动发送的原理。

2.Flutter层原理

先看下官方放出来的一场通信图:

通信原理

2.1 MethodChannel

入口就是MethodChannel,首先看下这个代码:

### platform_channel.dart

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

  /// The logical channel on which communication 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;
  }

构造函数需要一个name和可选的MethodCodecname就是MethodChannel的标识,MethodCodec是编解码器,决定了我们能传递什么类型的数据。看下官网提供的数据

datatype.png

MethodCodec是可选的,不传默认是StandardMethodCodec,这个在发起方法调用invokeMethod的时候会用到。StandardMethodCodec会将方法名称和参数序列化成二进制,收到平台返回的结果也是二进制数据,然后反序列化成Dart数据类型。

2.2 invokeMethod

另外invokeMethod是异步方法,返回Future<dynamic>类型,在接受这个方法的返回值的时候,就必须要使用 await进行修饰。要调用使用 await,必须在有 async标记的函数中运行。

接着发送消息BinaryMessages.send

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);
  }

send就是用来发送二进制消息给平台插件对应的通道。

其中_mockHandlers是用来调试用的,看下数据结构,如果设置了调试用的Handler就会直接返回,不会和平台通信。

### 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;
  }

接下来看下send中调用到_sendPlatformMessage

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;
  }

上面的Completer是一个用于可以控制Future的对象,因为正常的Future其实创建后就执行了或者放到队列中去等待执行了,用户只能被动接收到Future的回调。Completer就可以通过completer.complete去执行完成Future或者completer.error报错。

会调用到window下的sendPlatformMessage

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';

这里调用了一个native方法,那么我们就需要去找这个native方法。需要先下载Engine层的代码,地址: Engine.

3.0 Engine层原理

3.1 _SendPlatformMessage

根据上面的标识符Window_sendPlatformMessage去Engine层找到注册的方法:

### 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);
}

上面会调到SendPlatformMessage方法。

3.2 window--SendPlatformMessage

看下面代码有个有个报错的地方Platform messages can only be sent from the main isolate,只能在主线程main isolate发起MethodChannel调用。

### 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();
}

接着主要的一行代码,最终会调用到WindowClient中的虚方法HandlePlatformMessage

dart_state->window()->client()->HandlePlatformMessage

3.3 window--WindowClient

看下client定义的地方:

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

  ~Window();

  WindowClient* client() const { return client_; }
...
}

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();
};

遇到纯虚函数了只能去找找看有什么类继承了WindowClient了.

3.4 RuntimeController

找到只有一个地方继承了:

class RuntimeController final : public WindowClient

到这个类里面:

### RuntimeController.cc

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

最后是通过client_来调用,看着就是代理模式。接着找client_,看到构造函数中:

### 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};
  });

可以看到是通过client_其实就是RuntimeDelegate,

3.5 RuntimeDelegate

那就接着看RuntimeDelegate了,不巧又遇到了纯虚函数。

### 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

那就只能再看看哪个类继承了RuntimeDelegate, 只有一个类Engine继承了.

3.6 Engine

### engine.h

class Engine final : public blink::RuntimeDelegate

看到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));
  }
}

Engine偷懒又把工作转交给delegate_, 看到Engine构造函数里:

###  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
  );
}

其中Delegateengine.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;
  };

还是熟悉的纯虚函数OnEngineHandlePlatformMessage,回到Engine,看看哪边构造了这个对象就可以找到 delegate_了,最终找到shell.

3.7 Shell

在shell中有个字段属性就是Engine,构造的时候是将shell传递进去,所以上面的delegate_就是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();
      }));

shell中找到OnEngineHandlePlatformMessage方法:

### 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));
        }
      });
}

先看到第一个if判断,如果等于kSkiaChannel就会直接return,看下kSkiaChannel的值

constexpr char kSkiaChannel[] = "flutter/skia";

很明显我们自定义的channel不会走到这个判断逻辑里面,往下走,就是往PlatformTaskRunner里面抛一个task,
关于线程的详细说明,感兴趣的可以看到最后一个小结,因为比较长,为了调用逻辑的连贯性先放到最后了。

3.8 PlatformView --- PlatformViewAndroid

跟到PlatformView中,但是是虚函数:

### platform_view.h

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

接着找到它的继承类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 in InvokePlatformMessageXxxResponseCallback.
    FlutterViewHandlePlatformMessage(env, view.obj(), java_channel.obj(),
                                     nullptr, response_id);
  }
}

在这个方法里面已经可以看到Java的一点味道了:

  • 首先记录一个回调的id response_id,会把回调放到pending_responses_
 std::unordered_map<int, fml::RefPtr<blink::PlatformMessageResponse>>
      pending_responses_;
  • 调用jni下面的StringToJavaString转化channel name为Java字符串;

  • 如果调用携带参数,会调用 jni:: env->NewByteArray转为为JNI 数组

  • 最后调用FlutterViewHandlePlatformMessage

而这个函数是定义在platform_view_android_jni.cc中的

3.9 FlutterViewHandlePlatformMessage

可以看到这里已经开始通过JNIEnv调用虚拟机里面注册的JNI函数表了。

### 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));
}

关键就是找到g_handle_platform_message_method符号表对应的方法了,

### platform_view_android_jni.cc

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

做过JNI的小伙伴们都知道,通过handlePlatformMessage就可以找到在Android中对应的方法了。

3.10 FlutterJNI.java

终于来到了Java层,突然觉得Java这么亲切呢。

### 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)
  }

接下来我们从Android层写的代码入手

4.0 Java层原理

4.1 MethodChannel.java

再回头看下第一节的内容,

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

### 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));
    }

4.2 MethodChannel.java

BinaryMessenger是个接口,实现类有多个,在我们构造函数里面传入的是FlutterView

最终会调用到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);
        }

    }

那么前面FlutterJNI下面的platformMessageHandlerFlutterNativeView有关系吗?

看下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();
    }

可以看到FlutterNativeView持有FlutterJNI,PlatformMessageHandlerImpl也是它的内部类:

### 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, new BinaryReply() {
                        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);
            }
        }

  ...
}
  • 首先会从mMessageHandlers中取出前面注册的MethodChannel,然后调用onMessage方法,有的小伙伴发现了我们通过setMethodCallHandler设置的是MethodCallHandler,没有onMessage方法
public interface MethodCallHandler {
        void onMethodCall(MethodCall var1, MethodChannel.Result var2);
    }

其实有个细节没说,传进去的MethodCallHandler会构造成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 void notImplemented() {
                        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));
            }

}
  • 接着会通过invokePlatformMessageResponseCallback回调回去
FlutterNativeView.this.mFlutterJNI.invokePlatformMessageResponseCallback(replyId, reply, reply.position());

5.0 回调

上面通过replyId可以在Engine层找到回调, 其实就是沿着上面的反路径往回调。

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);

再通过native JNI回调到Engine

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     //
      );
}

5.3 platform_view_android.cc

在这里主要是找到前面在HandlePlatformMessage中登记的回调,然后进行调用:

### 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)));
}

最后是调用Complete()方法:

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});
      }));
}

有一个点需要注意的就是回调时在UI线程回调的,回调的数据通过WrapByteData序列化后进行传递。

这个callback应该就是之前在window.dart中注册的callback:

### 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);
    };
  }

最后体现在dart层面就是通过Completer进行Future的控制回调。

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',
        ));
      }
    });

6.0 线程

6.1 TaskRunner --- MessageLoop

看下TaskRunner,构造的时候会传一个 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);
};

} 

看到MessageLoop, 会持有一个task_runner_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);
};

}

再看构造函数里面给上面两个变量的赋值,可以看到会先构造一个MessageLoopImpl实例,然后传给TaskRunner

### message_loop.cc

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

再回头看下TaskRunner.PostTask

### task_runner.cc

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

最终会通过loop_也就是MessageLoopImpl.PostTask,然后会pushdelayed_tasks_中,

### message_loop_impl.cc

void 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);
}

delayed_tasks_是一个优先级队列:

### message_loop_impl.h

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

DelayedTaskQueue delayed_tasks_;

总结一下,TaskRunner.PostTask最终是将Task放到MessageLoop中的队列中。

6.2 Thread -- ThreadHost

问题来了,放到队列里面的Task谁负责取出来运行呢?按照尝试这时候应该是线程要出来了,我们找到Shell中看下线程创建:

### shell_benchmarks.cc

namespace 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) {
          return std::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);
}

...

}

taskRunner都是从ThreadHost中拿到,再看看ThreadHost:

### 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();
};

} 

主要有四个线程,分别是platform_thread, ui_thread, gpu_thread, io_thread.,再看看线程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;
}

具体逻辑步骤:

    1. 设置线程名
      这个逻辑就是在ThreadHost中,其中name_prefix就是在shell_benchmarks中定义的io.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");
  }
}
  • 2.初始化MessageLoop
### thread.cc

fml::MessageLoop::EnsureInitializedForCurrentThread()
### 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()));
}

会有一个ThreadLocal保存MessageLoop,避免重复创建。

  • 3.获取looprunner
### thread.cc

auto& loop = MessageLoop::GetCurrent();
runner = loop.GetTaskRunner();
### 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_;
}
  • 4.run messageloop

最后调用

### thread.cc
loop.Run();
### message_loop.cc

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

上面已经分析到loop_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_ = {};
}

最关键的就是Run()方法,但是很不幸的是在MessageLoopImplRun()是纯虚函数:

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();

  ...
}

其实根据平台有不同的实现:

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

Android平台下我们看看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;
    }
  }
}

到这里就是一个死循环了,ALooper_pollOnce已经跟不进去了,猜测里面逻辑应该就是循环从优先级队列中拿出Task执行。

是不是看到了Android开发很熟悉的loop

  • TaskRunner类似于Handler,可以通过它向队列中抛消息;
  • MessageLooperImpl就是类似于MessageQueue
  • MessageLoopAndroid就类似于Looper,循环从队列中取出消息

Refs:

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,265评论 6 490
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,078评论 2 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,852评论 0 347
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,408评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,445评论 5 384
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,772评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,921评论 3 406
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,688评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,130评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,467评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,617评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,276评论 4 329
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,882评论 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,740评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,967评论 1 265
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,315评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,486评论 2 348

推荐阅读更多精彩内容