Flutter和原生之間的平臺通道實踐與原理

juexingzhe發表於2019-04-04

Flutter使用了一個靈活的系統,允許開發者呼叫特定平臺的API,無論在Android上的Java或Kotlin程式碼中,還是iOS上的ObjectiveC或Swift程式碼中均可用。

Flutter平臺特定的API支援不依賴於程式碼生成,而是依賴於靈活的訊息傳遞的方式:

  • 應用的Flutter部分通過平臺通道(platform channel)將訊息傳送到其應用程式的所在的宿主(iOS或Android)。
  • 宿主監聽的平臺通道,並接收該訊息。然後它會呼叫特定於該平臺的API(使用原生程式語言) - 並將響應傳送回客戶端,即應用程式的Flutter部分。

以Android平臺上通過Flutter獲取手機電池電量為栗子,栗子參考自看Flutter中文網下呼叫程式碼怎麼寫。

1.0 獲取電池電量

首先看下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.0 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:

相關文章