当前位置:网站首页>Methodchannel of flutter

Methodchannel of flutter

2022-06-25 09:51:00 sunny_ yin8899

Preface

Flutter yes Google Use Dart Language development of a set of mobile application development framework . It is different from other development frameworks :

  1. because Flutter Use AOT Precompiled code is machine code , So it runs more efficiently .

  2. Flutter Of UI Control does not use the underlying native control , But use Skia Rendered by rendering engine , Because it doesn't depend on the underlying controls , So the multi terminal consistency is very good .

  3. Flutter The scalability of the system is also very strong , Developers can use Plugin And Native communicate .

brief introduction

Flutter Using a flexible system , Allows you to invoke platform specific API, No matter in Android Upper Java or Kotlin In the code , still iOS Upper ObjectiveC or Swift Available in code .

  1. Flutter Platform specific API Support does not depend on code generation , It relies on flexible messaging :

  2. Applied Flutter Partly through the platform channel (platform channel) Send the message to the host of its application (iOS or Android).

The platform channel the host is listening to , And receive the message . It then calls the platform specific API( Using native programming languages ) - And send the response back to the client , That is, the application's Flutter part .

The framework outlined : Platform channel

Use the platform channel on the client (Flutter UI) And hosts ( platform ) Passing messages between , As shown in the figure below :https://i.imgur.com/ZfAk0ST.png)]

Messages and responses are delivered asynchronously , To ensure that the user interface remains responsive ( It won't hang ).

On the client side ,MethodChannel (API) You can send messages corresponding to method calls . On the host platform ,MethodChannel stay Android((API) and FlutterMethodChannel iOS (API) You can receive method calls and return results . These classes allow you to use very little “ The scaffold ” Code development platform plug-ins .

Platform channel data type support and decoder

Standard platform channels use standard message codecs , To support simple things like JSON Efficient binary serialization of values , for example booleans,numbers, Strings, byte buffers, List, Maps( see also StandardMessageCodec Learn more ). When you send and receive values , The serialization and deserialization of these values in the message are automatic .
 Insert picture description here

Flutter Three different types of Channel, They are

  1. BasicMessageChannel: Used to pass strings and semi-structured information .
  2. MethodChannel: Used to pass method calls (method invocation).
  3. EventChannel: For data flow (event streams) Communication for .

BasicMessageChannel Androd End :
 Insert picture description here

BasicMessageChannel Flutter End :

 Insert picture description here

EventChannel Androd End :
 Insert picture description here
EventChannel Flutter End :

 Insert picture description here

Three Channel Independent of each other , Have different purposes , But they are very similar in design . Each of these Channel There are three important member variables :

  1. name: String type , representative Channel Name , It is also its unique identifier .
  2. messager:BinaryMessenger type , On behalf of the messenger , It is a tool for sending and receiving messages .
  3. codec: MessageCodec Type or MethodCodec type , Represents the codec of the message .

Channel name

​ One Flutter There may be multiple in the application Channel, Every Channel A unique... Must be specified when creating name,Channel Used between name To distinguish each other . When there is news from Flutter End sent to Platform End time , It will be passed on according to it channel name To find the Channel Corresponding Handler( Message handler )

BinaryMessenger yes Platform End and Flutter Tools for end-to-end communication , The message format used for communication is binary format data . When we initialize a Channel, And to the Channel Register to process messages Handler when , In fact, a corresponding BinaryMessageHandler, And channel name by key, Sign up to BinaryMessenger in . When Flutter Send message to BinaryMessenger when ,BinaryMessenger According to its input channel Find the corresponding BinaryMessageHandler, And let them deal with it .

Binarymessenger stay Android The end is an interface , Its concrete realization is FlutterNativeView.

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

 }

Binarymessenger Don't know Channel The existence of , It's only with BinaryMessageHandler Dealing with . and Channel and BinaryMessageHandler Is a one-to-one correspondence . because Channel from BinaryMessageHandler The received message is in binary format , Cannot be used directly , so Channel The binary message will be passed through Codec( Message codec ) Decode into a recognizable message and deliver it to Handler To deal with .

​ When Handler After processing the message , It will return... Through the callback function result, And will result It is encoded into binary format data by codec , adopt BinaryMessenger Send back Flutter End .

 private void handlePlatformMessage(final String channel, byte[] message, final int replyId) {
    this.assertAttached();
    BinaryMessageHandler handler = (BinaryMessageHandler)this.mMessageHandlers.get(channel);
	...
	if(reply == null) {
                            		FlutterNativeView.nativeInvokePlatformMessageEmptyResponseCallbac	(FlutterNativeView.this.mNativePlatformView, replyId);
    } else {
                            		FlutterNativeView.nativeInvokePlatformMessageResponseCallback	(FlutterNativeView.this.mNativePlatformView, replyId, reply, reply.position());
    }
	...
}

Message codec :Codec

Message codec Codec It is mainly used to convert binary format data into Handler Identifiable data ,Flutter It defines two kinds of Codec:MessageCodec and MethodCodec.

MessageCodec

It's an interface , Two methods are defined :encodeMessage Receive a specific data type T, And encode it into binary data ByteBuffer, and decodeMessage Receive binary data ByteBuffer, Decode it into a specific data type T.

MessageCodec There are many different implementations :

  • BinaryCodec
  • StringCodec
  • JSONMessageCodec
  • StandardMessageCodec

MethodCodec

MethodCodec For binary data and method calls (MethodCall) Codec between and the returned result .MethodChannel and EventChannel The codecs used are MethodCodec.

And MessageCodec The difference is ,MethodCodec be used for MethodCall Object encoding and decoding , One MethodCall Object represents a time from Flutter End initiated method calls .MethodCall Yes 2 Member variables :String Type of method Represents the name of the method to be called , Generic type (Android In Chinese, it means Object,iOS In Chinese, it means id) Of arguments Represents the method to be called .

​ Because it deals with method calls , So compared to MessageCodec,MethodCodec More processing of the call results . When the method call succeeds , Use encodeSuccessEnvelope take result Encoded as binary data , And when the method call fails , Then use encodeErrorEnvelope take error Of code、message、detail Encoded as binary data

MethodCodec There are two realizations :

  • JSONMethodCodec
  • StandardMethodCodec

Message handler :Handler

When we receive binary format messages and use Codec Decode it to Handler After the message that can be processed , We should Handler Come on. .Flutter Three types of Handler, And Channel The types correspond one by one . We ask Channel Sign up for a Handler when , In fact, it is to BinaryMessager Register a corresponding BinaryMessageHandler. When news is sent to BinaryMessageHandler after ,Channel Will pass Codec Decode the message , And pass it on to Handler Handle .

  • MessageHandler

    MessageHandler Users process strings or semi-structured messages , Its onMessage Method to receive a T Type of message , And asynchronously return the same type result.MessageHandler The function of is quite basic , Less use scenarios , But its cooperation BinaryCodec When using , It can easily transfer binary data messages .

  • MethodHandler

    MethodHandler For handling method calls , Its onMessage Method to receive a MethodCall Type message , And according to MethodCall Member variables of method To call the corresponding API, When it's done , Depending on whether the method call succeeds or fails , Return the corresponding result .

  • StreamHandler

    StreamHandler Slightly different from the first two , Communication for event flow , The most common use is Platform To the end Flutter Send event messages on the client . When we achieve one StreamHandler when , Need to implement its onListen and onCancel Method . And in the onListen Methods in the reference , There is one EventSink( Its presence Android It's an object ). We hold EventSink after , You can pass EventSink towards Flutter Send event messages on the client .

Understand the message encoding and decoding process

Flutter Official documents say ,standard platform channels Use standard messsage codec Yes message and response Serialization and deserialization ,message And response It can be booleans, numbers, Strings, byte buffers,List, Maps wait , The serialized data is in binary format .

So in the example mentioned above ,java.lang.Integer The return value of type is first serialized into a piece of data in binary format , The data is then passed to flutter Lateral posterior , Is deserialized to dart In language int Data of type .

When message or response Need to be encoded as binary data , Would call StandardMessageCodec Of writeValue Method , This method receives a file named value Parameters of , And according to its type , To the binary data container (ByteArrayOutputStream) Write the... Corresponding to this type type value , And then convert the data into binary representation , And write to the binary data container .

​ and message perhaps response When it needs to be decoded , It uses StandardMessageCodec Of readValue Method , The method receives binary format data , Will first read a byte Indicates its type, And according to it type Convert binary data to corresponding data type .

for example 100, When this value is converted to binary data , Will first write to the binary data container int The type corresponds to type value :3, Then write the charge value 100 Converted 4 individual byte. And when Flutter When the end receives the binary data , First read the first byte value , And according to its value, the data is int type , next , Read the following 4 individual byte, And turn it into dart Type of int.

 Insert picture description here

Understand the messaging process

The following is an example to understand the message passing process .

The messaging : from Flutter To Platform
Interaction diagram
 Insert picture description here

Flutter End

We are Flutter End use MethodChannel Of invokeMethod Method initiates a method call ,invokeMethod Method will put it into the parameter message and arguments Encapsulate into a MethodCall object , And use MethodCodec Encode it into binary format data , Re pass BinaryMessages Send a message

Future<String> getString(String key,String value) async {
String result;
try {
  	final Map<String, dynamic> params = <String, dynamic>{
    	'key': '$key',
  		};
  		params['value'] = value;
  		result = await _sChannel
      	.invokeMethod('getString', params);

	} on PlatformException catch (e) {
  print(e.toString());
	}
	return result;
}

call methodchannel obtain getString Value . Let's follow the code execution process in turn .invokeMethod The method is executed as follows :

 Future<dynamic> invokeMethod(String method, [dynamic arguments]) async {
	assert(method != null);
		final dynamic result = await BinaryMessages.send(
  		name,
  		codec.encodeMethodCall(new MethodCall(method, arguments)),
	);
	if (result == null)
  		throw new MissingPluginException('No implementation found for method $method on channel $name');
	return codec.decodeEnvelope(result);
}

Finally, it will be implemented to :

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

The native method is called here , Again flutter engine Source code window.cc You can find :

void Window::RegisterNatives(tonic::DartLibraryNatives* natives) {
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},
	});
}

_SendPlatformMessage Function corresponds to :

void _SendPlatformMessage(Dart_NativeArguments args) {
	tonic::DartCallStatic(&SendPlatformMessage, args);
}

Called to SendPlatformMessage:

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

	if (!dart_state->window()) {
	// Must release the TypedData buffer before allocating other Dart objects.
	data.Release();
	return 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.dart_handle())) {
		dart_state->window()->client()->HandlePlatformMessage(
    		fml::MakeRefCounted<PlatformMessage>(name, response));
	} else {
		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();
}

go through , Here comes the key part dart_state->window()->client()->HandlePlatformMessage(…);
Again WindowClient Find this function in .

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

It's a pure virtual function , Find the implementation class RuntimeController, You can find RuntimeDelegate& client_, Then follow the trend to find engine.h Inherited RuntimeDelegate.

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

among OnEngineHandlePlatformMessage It's about HandlePlatformMessage Call to , So find Delegate Where this class is called, you can find HandlePlatformMessage Call to . The global search is finalized PlatformViewAndroid There are calls to Delegate

PlatformViewAndroid(PlatformView::Delegate& delegate,
                  blink::TaskRunners task_runners,
                  fml::jni::JavaObjectWeakGlobalRef java_object);

In the end in platform_view_android.cc The call :

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

Again platform_view_android_jni.cc Find :

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

Finally find :

g_flutter_native_view_class = new fml::jni::ScopedJavaGlobalRef<jclass>(
  env, env->FindClass("io/flutter/view/FlutterNativeView"));
	if (g_flutter_native_view_class->is_null()) {
	return false;
}

FlutterNativeView It's our java Code. . Here we can come to an end . The corresponding is :
 Insert picture description here
In the following calls, we can trace the code all the way to understand .

Reference material

https://www.jianshu.com/p/39575a90e820

https://www.colabug.com/3916347.html

https://www.jianshu.com/p/cb96d62f5042

https://flutter.io/platform-channels/

https://github.com/flutter/flutter

https://github.com/flutter/engine

原网站

版权声明
本文为[sunny_ yin8899]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/02/202202200548398026.html