androidflutterdartjava-native-interfaceflutter-method-channel

How Flutter MethodChannel works, and how it is different from Interop?


Note: This question is specifically for `flutter.android.dart`.

How does the MethodChannel of Flutter works internally, like:

Also, how is a MethodChannel different, from the Kotlin/Java interop with dart (which is an incoming feature, coming with dart 3, next year), like:

How is the following code expected to be changed to, to use kotlin interop, both at dart, and kotlin side:

package com.exa.mple

import android.net.Uri
import android.os.Build
import android.os.ParcelFileDescriptor
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
import java.io.File

class MainActivity : FlutterActivity() {
    private val METHOD_CHANNEL: String = "com.example/method-channel";

    override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
        super.configureFlutterEngine(flutterEngine)
        MethodChannel(flutterEngine.dartExecutor.binaryMessenger, METHOD_CHANNEL).setMethodCallHandler { request: MethodCall, response: MethodChannel.Result ->
            if (request.method == "getPlatformSDKVersion") {
                response.success(Build.VERSION.SDK_INT.toString());
            } else if (request.method == "getPrivateFilesDirPath") {
                response.success(context.filesDir.absolutePath);
            } else {
                response.notImplemented();
            }
        }
    }
}

I searched google, reddit, stack-overflow, even quora, thoroughly, tought found some info, but nothing satisfactory

Thanking you...


Solution

  • Method channels use a "method codec" and "message codec" to serialize the messages. There are a few flavours, but start by looking at StandardMessageCodec and friends. The standard codec handles a broad range of types.

    In terms of message passing, first take a look at the thread model diagram. The "Platform thread" is the main native thread vs the "Dart UI thread" is the main thread of the Dart VM. These two need to pass messages between themselves - using the codecs above. Essentially the mechanism is to drop a numbered message into a queue, which is processed periodically by the other thread. The other thread unmarshalls the request and dispatches it to the relevant registered handler (which are identified by their name, e.g. "com.somewhere.someplugin/somename" (which is just a naming convention)). The handler is passed the argument (one of the above supported types - often a map of key value pairs) and some form of a 'result' which allows the return value to be passed (which can again be any supported type).

    That result (for example, generated by calling result.success(123.45) in Android) creates a reply message with the serialized response (in this example a single double) and the original request message number which is added to the queue going the other way. When it is collected on the other thread the message number allows the caller to match up the result with the original request and (as example in a Dart->native call) completes the completer that allows the await channel.invokeMethod('someMethod'); future to complete.

    In terms of performance, FFI is typically much more performant as there is no/minimal marshalling. It's reasonable to expect jnigen to be too, but as it is highly experimental, you will need to wait and see.