Flutter插件原理浅谈

Platform Channel

  1. Platform Channel是Flutter端与Platform端指定的通信机制,它分为三种:
    (1) BasicMessageChannel: 用于传递字符串和半结构化的信息(在大内存数据块传递的情况下使用)。
    (2) MethodChannel: 用于传递方法的调用(method invocation)。
    (3) EventChannel:用于数据流(event streams)的通信。

消息传递与编解码器

avatar
从图中可以看出,不同的Handler对应不同的消息通道。
其中,中间通信的消息都是编码的二进制格式形式,需要通过消息解码器进行处理。
编码分为两种:MessageCodec 和MethodCodec。其中,MessageCodec包括BinaryCodec, StringCodec,JSONMesageCodec和 StandardMessageCodec,
MethodCodec包括JSONMethodCodec 和 StandardMethodCodec。

Plaffrom 数据类型支持

Flutter默认的消息编码器(StandardMessageCodec),目前支持的数据类型如下:

Dart Android IOS
null null nil(NSNull when nested)
bool Java.lang.Boolean NSNumber numberWithBool:
int Java.lang.Integer NSNumber numberWithInt:
int Java.lang.Long NSNumber numberWithLong:
double Java.lang.Double NSNumber numberWithDouble:
String Java.lang.String NSSttring
Unit8List byte[] FlutterStandardTypedData typedDataWithBytes:
int32List int[] FlutterStandardTypedData typedDataWithInt32:
int64List long[] FlutterStandardTypedData typedDataWithInt64:
Float64List double[] FlutterStandardTypedData typedDataWithFloat64:
List java.util.ArrayList NSArray
Map java.util.HashMap NSDictionary

Flutter插件调用流程图

avatar
备注:Android侧没有画出,是由于StartUML工具在画时序图时,无法调整序号。

Flutter插件原理分析

  1. 下面针对PlatfromChannel进行说明,为了更好的举例,下面先展出示例代码。

    • Dart端代码
      avatar

    • android端原生代码
      avatar

  2. 下面先从Flutter端开始说起

flutter插件先调用了MethodChannel.invokeMethod方法,在MethodChannel类中,此方法的定义如下:

1
2
3
4
5
6
7
8
9
10
11
12
Future<T> invokeMethod<T>(String method, [ dynamic arguments ]) async {
assert(method != null);
final ByteData result = await binaryMessenger.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;
}
此类方法定义在BasicMessageChannel.dart文件中,binaryMessenger对象在此类默认使用ServicesBinding.defaultBinaryMessenger codec对象是消息解码器,在此类中,使用StandardMethodCodec。 下面我们看一下ServicesBinding.defaultBinaryMessenger.send方法的实现逻辑。 其实最终ServicesBinding.defaultBinaryMessenger类映射到binding.dart文件中_DefaultBinaryMessenger类,此类继承于BinaryMessenger类,BinaryMessenger类是个抽象类,类的定义形式如下: ![avatar](/image/flutter_plugin3.jpg) _DefaultBinaryMessenger类send方法实现如下:
1
2
3
4
5
6
Future<ByteData> send(String channel, ByteData message) {
final MessageHandler handler = _mockHandlers[channel];
if (handler != null)
return handler(message);
return _sendPlatformMessage(channel, message);
}
可以看到,如果在MethodChannel时调用setMethodCallHandler方法时,设置了回调,优先调用了这个回调。前面我们没有调用setMethodCallHandler方法,所以直接走_sendPlatformMessage方法。下面是_sendPlatformMessage方法实现
_sendPlatformMessage(String channel, ByteData message) {
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
  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: ErrorDescription('during a platform message response callback'),
));
}
});
return completer.future;
}
在此方法中,有ui.window对象,ui引用于 'dart:ui' 库,ui.window最后映射到window.dart。在window.dart中,有window.dart中,定义了Window类,其中sendPlatformMessage方法,实现如下 ![avatar](/image/flutter_plugin4.jpg) 从此方法中,可以最终是调用到了Native层的_sendPlatformMessage方法。Native层是C++实现的。此方法在window.cc文件中,最终映射到下面的方法
SendPlatformMessage(Dart_Handle window,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
                                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();
}
该方法主要功能: - 该方法是发送平台消息,则只允许从主isolate中发出,否则会跑出异常 - 该SendPlatformMessage方法的参数name代表是channel名,data_handle是记录待执行的方法名和参数,callback是执行后回调反馈结果数据的方法 - 创建PlatformMessageResponseDart对象,保存callback方法 - 调用RuntimeController的HandlePlatformMessage来处理平台消息 RuntimeController类定义在flutter/runtime/runtime_controller.cc
RuntimeController::HandlePlatformMessage(
1
2
3
    fml::RefPtr<PlatformMessage> message) {
client_.HandlePlatformMessage(std::move(message));
}
上面的类最终调用到了Engine::HandlePlatformMessage[-> flutter/shell/common/engine.cc]
constexpr char kAssetChannel[]
1
2
3
4
5
6
7
void Engine::HandlePlatformMessage(fml::RefPtr<PlatformMessage> message) {
if (message->channel() == kAssetChannel) {
HandleAssetPlatformMessage(std::move(message));
} else {
delegate_.OnEngineHandlePlatformMessage(std::move(message));
}
}
下一步调用了Shell类OnEngineHandlePlatformMessage方法[-> flutter/shell/common/shell.cc]
Shell::OnEngineHandlePlatformMessage(
1
2
3
4
5
6
7
8
9
10
11
12
13
14
  fml::RefPtr<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));
}
});
}
接下来将HandlePlatformMessage的工作交给主线程的PlatformTaskRunner来处理,对于PlatformView在Android平台的实例为PlatformViewAndroid。 [-> flutter/shell/platform/android/platform_view_android.cc]
PlatformViewAndroid::HandlePlatformMessage(
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
    fml::RefPtr<flutter::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);
}
}
[-> flutter/shell/platform/android/platform_view_android_jni.cc]
FlutterViewHandlePlatformMessage(JNIEnv* env, jobject obj,
1
2
3
4
                                     jstring channel, jobject message,
jint responseId) {
env->CallVoidMethod(obj, g_handle_platform_message_method, channel, message, responseId);
}
接下来,会调用到FlutterJNI类的handlePlatformMessage方法中,此方法[->flutter/shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java] 终于调用到Java代码了。
TODO(mattcarroll): determine if message is nonull or nullable
1
2
3
4
5
6
7
@SuppressWarnings("unused")
private void handlePlatformMessage(@NonNull 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)
}
3.从宿主端android开始 - 3.1 FlutterView 在android原生页面,每个activity都继承于FlutterActivity。在FlutterActivity中有个代理类FlutterActivityDelegate类,此类代理了Activity类的几个生命周期方法,下面先从此类的onCreate说明,在此方法中定义了FlutterView,Activity的根view就是FlutterView,FlutterView继承SurfaceView,另外实现了BinaryMessenger,TextureRegistry两个接口,这个BinaryMessage接口很重要,后面会讲到。下面是FlutterActivityDelegate类中onCreate方法的定义:
class FlutterView extends SurfaceView implements BinaryMessenger, TextureRegistry {
1
2
3
4
5
6
7
8
9
10
11
12
13
14
 	......
public void onCreate(Bundle savedInstanceState) {
......
this.flutterView = this.viewFactory.createFlutterView(this.activity);
if (this.flutterView == null) {
FlutterNativeView nativeView = this.viewFactory.createFlutterNativeView();
this.flutterView = new FlutterView(this.activity, (AttributeSet)null, nativeView);
this.flutterView.setLayoutParams(matchParent);
this.activity.setContentView(this.flutterView);
......
}
......
}
}
创建FlutterView时,传入了FlutterNativeView。 - 3.2 FlutterNativeView FlutterNativeView构造器定义如下:
FlutterNativeView(@NonNull Context context, boolean isBackgroundView) {
1
2
3
4
5
6
7
8
9
    this.mContext = context;
this.mPluginRegistry = new FlutterPluginRegistry(this, context);
this.mFlutterJNI = new FlutterJNI();
this.mFlutterJNI.setRenderSurface(new FlutterNativeView.RenderSurfaceImpl());
this.dartExecutor = new DartExecutor(this.mFlutterJNI, context.getAssets());
this.mFlutterJNI.addEngineLifecycleListener(new FlutterNativeView.EngineLifecycleListenerImpl());
this.attach(this, isBackgroundView);
this.assertAttached();
}
其中FlutterJNI类提前我们提到过,最后dart插件的方法,最后调用了FlutterJNI.handlePlatformMessage方法,那么现在就需要找到在何处调用了FlutterJNI.setPlatformMessageHandler方法。 从上面的代码中,FlutterJNI类传给了DartExecutor类。
  • 3.3 DartExecutor
    DartExecutor类的构造类说明如下:

    DartExecutor(@NonNull FlutterJNI flutterJNI, @NonNull AssetManager assetManager) {
    1
    2
    3
    4
    5
        this.flutterJNI = flutterJNI;
    this.assetManager = assetManager;
    this.messenger = new DartMessenger(flutterJNI);
    this.messenger.setMessageHandler("flutter/isolate", this.isolateChannelMessageHandler);
    }
    - 3.4 DartMessenger

    DartMessenger类的构造器类说明如下:

    class DartMessenger implements BinaryMessenger, PlatformMessageHandler
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
      DartMessenger(@NonNull FlutterJNI flutterJNI) {
    this.flutterJNI = flutterJNI;
    this.messageHandlers = new HashMap();
    this.pendingReplies = new HashMap();
    }
    public void setMessageHandler(@NonNull String channel, @Nullable BinaryMessageHandler handler) {
    if (handler == null) {
    Log.v("DartMessenger", "Removing handler for channel '" + channel + "'");
    this.messageHandlers.remove(channel);
    } else {
    Log.v("DartMessenger", "Setting handler for channel '" + channel + "'");
    this.messageHandlers.put(channel, handler);
    }
    }
    @UiThread
    public void send(@NonNull String channel, @NonNull ByteBuffer message) {
    Log.v("DartMessenger", "Sending message over channel '" + channel + "'");
    this.send(channel, message, (BinaryReply)null);
    }
    public void handleMessageFromDart(@NonNull String channel, @Nullable byte[] message, int replyId) {
    Log.v("DartMessenger", "Received message from Dart over channel '" + channel + "'");
    BinaryMessageHandler handler = (BinaryMessageHandler)this.messageHandlers.get(channel);
    if (handler != null) {
    try {
    Log.v("DartMessenger", "Deferring to registered handler to process message.");
    ByteBuffer buffer = message == null ? null : ByteBuffer.wrap(message);
    handler.onMessage(buffer, new DartMessenger.Reply(this.flutterJNI, replyId));
    } catch (Exception var6) {
    Log.e("DartMessenger", "Uncaught exception in binary message listener", var6);
    this.flutterJNI.invokePlatformMessageEmptyResponseCallback(replyId);
    }
    } else {
    Log.v("DartMessenger", "No registered handler for message. Responding to Dart with empty reply message.");
    this.flutterJNI.invokePlatformMessageEmptyResponseCallback(replyId);
    }
    }
    }

    DartMessenger类实现了platfromMessageHandler接口中的handleMessageFromDart方法,最终FlutterJNI是调用到了DartMessenger类handleMessageFromDart方法。下面就需要找FlutterJNI调用setPlatformMessageHandler(new DartMessager())的地方,我们往回看,发现DartExecutor类onAttachedToJNI方法正好实现了。

    void onAttachedToJNI() {
    1
    2
    3
        Log.v("DartExecutor", "Attached to JNI. Registering the platform message handler for this Dart execution context.");
    this.flutterJNI.setPlatformMessageHandler(this.messenger);
    }

    接着再往下看,又回到FlutterNativeView类,发现实现了FlutterNativeView调用了DartExecutor类的onAttachedToJNI方法,此方法定义如下:

    void attach(FlutterNativeView view, boolean isBackgroundView) {
    1
    2
    3
        this.mFlutterJNI.attachToNative(isBackgroundView);
    this.dartExecutor.onAttachedToJNI();
    }

    attach方法在FlutterNativeView类的构造器中调用了。这样知道FlutterJNI类调用setPlatformMessageHandler方法了。
    下面就看DartMessenger类的setMessageHandler方法在什么地方调用了。一直往回看,最终调用到了FlutterView.setMessageHandler方法,

1
2
3
4
@UiThread
public void setMessageHandler(String channel, BinaryMessageHandler handler) {
this.mNativeView.setMessageHandler(channel, handler);
}
- 3.5 MethodChannel 下面我们从MethodChannel类看起,看看自定义的插件,是如何在被调用的。在自定义的Activity中,如果要让插件使用,需要在onCreate方法中,调用如下语句:
class MainActivity extends FlutterActivity {
1
2
3
4
5
6
  @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
GeneratedPluginRegistrant.registerWith(this);
}
}

最终调用到了我的自定义插件JdmaFlutterPlugin,在此插件中创建了MethodChannel对象,并且此对象调用了setMethodCallHandler方法,代码如下:

class JdmaFlutterPlugin implements MethodChannel.MethodCallHandler {
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
private static final String TAG = "JDMAPlugin";
private String account;
private Context context;
private boolean isDebug = false;
private static final String CHANNEL_NAME = "xxx";
public static void registerWith(PluginRegistry.Registrar registrar) {
final MethodChannel channel = new MethodChannel(registrar.messenger(), CHANNEL_NAME);
channel.setMethodCallHandler(new JdmaFlutterPlugin(registrar.context()));
}
private JdmaFlutterPlugin(Context context) {
this.context = context;
}
@Override
public void onMethodCall(MethodCall methodCall, MethodChannel.Result result) {
switch (methodCall.method) {
case "setDebug":
isDebug = (Boolean) methodCall.arguments;
JDMaInterface.setShowLog(isDebug);
result.success(null);
break;
在接着往下看,MethodChannel类定义如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public final class MethodChannel {
private static final String TAG = "MethodChannel#";
private final BinaryMessenger messenger;
private final String name;
private final MethodCodec codec;
public MethodChannel(BinaryMessenger messenger, String name) {
this(messenger, name, StandardMethodCodec.INSTANCE);
}
......
@UiThread
public void setMethodCallHandler(@Nullable MethodChannel.MethodCallHandler handler) {
this.messenger.setMessageHandler(this.name, handler == null ? null : new MethodChannel.IncomingMethodCallHandler(handler));
}
......
现在重点是BinaryMessenger对象,最终实现PluginRegistry接口的类发现是FlutterView类,FlutterView类实现了BinaryMessenger接口,这恰好找到了3.4节最后FlutterView调用setMessageHandler的地方。

总结