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;
}
複製程式碼
看下效果圖:
接下來看下Flutter
和Native
通訊的原理,主要是上面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和可選的MethodCodec
,name
就是MethodChannel
的標識,MethodCodec
是編解碼器,決定了我們能傳遞什麼型別的資料。看下官網提供的資料
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
);
}
複製程式碼
其中Delegate
在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;
};
複製程式碼
還是熟悉的純虛擬函式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
下面的platformMessageHandler
和FlutterNativeView
有關係嗎?
看下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
,然後會push
到delayed_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;
}
複製程式碼
具體邏輯步驟:
-
- 設定執行緒名
這個邏輯就是在
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.獲取
loop
和runner
### 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()
方法,但是很不幸的是在MessageLoopImpl
中Run()
是純虛擬函式:
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: