preface

The resources

Add Flutter to an existing Android project – website

Write dual-end platform code (plug-in written implementation)

Principle of reuse of the Flutter mixed stack

The implementation and principle analysis of Flutter startup page

Android Hybrid development jump Flutter black screen problem solution

directory




1. Create the Flutter Module and configure it

Create the Flutter Module in the Android project to communicate with it

1. Created the Flutter Module. The author placed the flutter_Module created in the same directory level AS the APP project created by AS.

Android Studio New Flutter Project -> Flutter ModuleCopy the code

2. Configure the official website configuration

1. Android project settings.gradle, no guide packagesetBinding(new Binding([gradle: this]))
evaluate(new File(settingsDir.parentFile, 'flutter_module/.android/include_flutter.groovy') 2. After synchronization, implement the project in the Android project app module (':flutter')Copy the code

3. Call Flutter in Java to enter the page related to Flutter

How to add FlutterActivity and FlutterFragment

Note that some FlutterActivities need to be registered in manifest files

For example, skip to the home page of Flutter from native App and pass the data to Flutter, remember the FlutterFragment lifecycle, According to the website to write to public void goToFlutterClick View (View) {startActivity (FlutterActivity. WithNewEngine () initialRoute ("Flutter Activity").build(this));
    }
​
    public void goToFlutterTarget(View view) {
        FlutterFragment flutterFragment = FlutterFragment.withNewEngine().initialRoute("Flutter Fragment").build();
        getSupportFragmentManager().beginTransaction().add(R.id.container, flutterFragment, "TAG").commit(); }}Copy the code

When you try it out, there are two problems

  1. Native APP Jump Flutter has a black screen and then display problem. Add a FlutterFragment to your native Activity. There is no black screen. Add a FlutterFragment to your native Activity. See the following example code

  2. The solution is to specify the route name to display in main.dart based on the parameters passed to determine which widget to create for runApp

    void main() => run(_widgetForRoute(window.defaultRouteName))
    Widget _widgetForRoute(String route){
        switch(route){
            case 'route1':
                 return Widget1();
            case 'route2':
                 return Widget2();
            default:
                 return Center(child:Text('Are You Ok ? ')); }}Copy the code

Tip: After executing the Flutter attach command, code changed on the Flutter side can also be hot-loaded and hot-restarted. To debug, find the Flutter Attach button in Android Studio, click it, and launch the APP.

2. Examples of communication between Flutter and Native

There are three different types of channels defined in the Flutter

  • BasicMessageChannel: Used to pass string and semi-structured information, to communicate continuously, and to reply to a message when received. For example, Native transmits the traversed file information to Dart successively, and Flutter sends the information obtained from the server to Native successively.

    The resources of the Native project are not shared with the Flutter. The ICONS and other resources of the Native project can be obtained through the BasicMessage echannel

  • MethodChannel: Used for passing method calls, one-time communication, such as the Flutter call Native to get system power and make Toast calls.

  • EventChannel: It is used for event streams communication and continuous communication. After receiving a message, the message cannot be replied. It is usually used for communication from Native to Dart, such as mobile phone power change, network connection change, sensor, etc.

1, BasicMessageChannel

Effect of simple


code

Native client

//1
import io.flutter.embedding.android.FlutterFragment;
// import xxxxxxx
public class FlutterTestActivity extends AppCompatActivity implements IShowMessage, View.OnClickListener {
​
    private EditText mInput;
    private Button mSend;
 
    private BasicMessageChannelPlugin basicMessageChannelPlugin;
    private FlutterFragment mFlutterfragment;
    private EventChannelPlugin eventChannelPlugin;
​
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.test);
        mInput = findViewById(R.id.input);
        mSend = findViewById(R.id.send);
     
​
        mFlutterfragment = FlutterFragment.withNewEngine().initialRoute("Flutter Fragment").build();
        getSupportFragmentManager().beginTransaction().add(R.id.flutter_test_fragment, mFlutterfragment, "TAG").commit(); } public void basicChannelClick(View view) { basicMessageChannelPlugin = BasicMessageChannelPlugin.registerWith(this,mFlutterfragment.getFlutterEngine().getDartExecutor().getBinaryMessenger());  } public void methodChannelClick(View view) { MethodChannelPlugin.registerWith(this,mFlutterfragment.getFlutterEngine().getDartExecutor().getBinaryMessenger()); } public void eventChannelClick(View view) { eventChannelPlugin = EventChannelPlugin.registerWith(mFlutterfragment.getFlutterEngine().getDartExecutor().getBinaryMessenger()); } @Override public void onShowMessage(String message) { Toast.makeText(this,"I hava receive " + message, Toast.LENGTH_SHORT).show();
    }
​
    @Override
    public void onClick(View view) {
        if(view == mSend){
            if(! TextUtils.isEmpty(mInput.getText().toString().trim())){ basicMessageChannelPlugin.send(mInput.getText().toString().trim()); // eventChannelPlugin.send(mInput.getText().toString().trim()); }}}} / / 2 / packaging to send and receive message * * * / public class BasicMessageChannelPlugin implements BasicMessageChannel. The MessageHandler < String >. BasicMessageChannel.Reply<String> { private final Activity activity; private final BasicMessageChannel<String> messageChannel; /** * Provide for external call registration ** @param binaryMessenger * @return
     */
    public static BasicMessageChannelPlugin registerWith(Activity activity, BinaryMessenger binaryMessenger) {
        returnnew BasicMessageChannelPlugin(activity, binaryMessenger); } private BasicMessageChannelPlugin(Activity activity, BinaryMessenger binaryMessenger) { this.activity = activity; This. MessageChannel = new BasicMessageChannel<>(binaryMessenger,"BasicMessageChannelPlugin", StringCodec.INSTANCE); / / set the message handler, processing the message from the Dart messageChannel. SetMessageHandler (this); } /** * @param reply */ @override public void onMessage(@nullable String message, @override public void onMessage(@nullable String message, @override public void onMessage(@nullable String message, @override public void onMessage) @NonNull BasicMessageChannel.Reply<String> reply) { Log.i("Basic"."Dart Send ->"+ message); Dart reply.reply()"Native reply -> BasicMessageChannel received" + message);
        if(activity instanceof IShowMessage) {//IShowMessage is the interface implemented by the activity. It is used to process received messages ((IShowMessage)). activity).onShowMessage(message); } Toast.makeText(activity, message, Toast.LENGTH_SHORT).show(); } /** * Send the message ** @param message message content * @param callback After sending the message, Dart end feedback * / void the send (String message, BasicMessageChannel. Reply < String > callback) {messageChannel. Send (message, callback); } public void send(String message) { send(message, this::reply); } @override public void reply(@nullable String Reply)if(activity instanceof IShowMessage) {//IShowMessage is the interface implemented by the activity. It is used to process received messages ((IShowMessage)). activity).onShowMessage(reply); } } } //3 test.xml <? xml version="1.0" encoding="utf-8"? > <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:onClick="basicChannelClick"
        android:text="BasicMessageChannel"
        android:textAllCaps="false" />

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:onClick="methodChannelClick"
        android:text="MethodChannel"
        android:textAllCaps="false" />

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:onClick="eventChannelClick"
        android:text="EventChannel"
        android:textAllCaps="false" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <EditText
            android:id="@+id/input"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1" />

        <Button
            android:id="@+id/send"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Send" />
    </LinearLayout>

    <TextView
        android:id="@+id/show"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

    <FrameLayout
        android:id="@+id/flutter_test_fragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</LinearLayout>
Copy the code

The Dart end

Class _MyHomePageState extends State<MyHomePage> {static const BasicMessageChannel _basicMessageChannel = const BasicMessageChannel("BasicMessageChannelPlugin", StringCodec());
​
  String receiveMessage = "";
  @override
  void initState() {
    // TODO: implement initState
    super.initState();
    _basicMessageChannel.setMessageHandler((message) => Future<String>((){
      setState(() { receiveMessage = message; }); // The Native end is notified after receiving the messagereturn "Receive a message from Native:"+ message; })); // Send a message to Native without waiting for a reply _basicMessagechannel. send("Flutter Init Ok !"); } // Send a message to Native and receive a reply _send(message) async{String response = await _basicMessagechannel. send(message);print('native reply '+ response);
  }
​
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
      
        child: Column(
 
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '$receiveMessage', style: Theme.of(context).textTheme.headline4, ), ], ), ), floatingActionButton: FloatingActionButton( //onPressed: _sendToDart, child: Icon(Icons.add), ), ); }}Copy the code

2, MethodChannel

MethodChannel gets a battery example

Effect of simple



code

Native client

The Activity code above analyzes the BasicMessageChannel already posted, Register can be public class MethodChannelPlugin implements MethodChannel. MethodCallHandler {private final Activity Activity; public static void registerWith(Activity activity, BinaryMessenger binaryMessenger) { MethodChannel channel = new MethodChannel(binaryMessenger,"MethodChannelPlugin"); MethodChannelPlugin instance = new MethodChannelPlugin(activity); channel.setMethodCallHandler(instance); } private MethodChannelPlugin(Activity activity) { this.activity = activity; } @Override public void onMethodCall(@NonNull MethodCall call, @nonnull methodchannel. Result Result) {// Handle method calls from Dart switch (call.method) {case "show":
                Log.i("MethodChannel"."Show method"+call.arguments()); showMessage(call.arguments()); // Return the result to Dart result.success("MethodChannelPlugin received" + call.arguments);
                break;
            default:
                result.notImplemented();
                break;
        }
    }
​
    private void showMessage(String arguments) {
        if(activity instanceof IShowMessage) { ((IShowMessage) activity).onShowMessage(arguments); }}}Copy the code

The Dart end

Static const MethodChannel _MyHomePageState = MethodChannel();"MethodChannelPlugin");
​
floatingActionButton: FloatingActionButton(
        onPressed: _sendToDart,
        child: Icon(Icons.add),
      ),
      
 _sendToDart() { _methodChannel? .invokeMethod("show".'Dart greetings')? .then((value) {setState(() { this.receiveMessage = value; }); })? .catchError((e) {print(e);
    });
  }Copy the code

3, the EventChannel

Effect of simple



code

Native client

public class EventChannelPlugin implements EventChannel.StreamHandler {
​
    private  EventChannel.EventSink eventSink   ;
​
    public static EventChannelPlugin registerWith(BinaryMessenger binaryMessenger) {
        EventChannelPlugin plugin = new EventChannelPlugin();
        new EventChannel(binaryMessenger, "EventChannelPlugin").setStreamHandler(plugin);
        return plugin;
    }
​
    public void send(Object params){
        if(eventSink == null){
            return; } eventSink.success(params); } /** * Native callback for events * @param Arguments * @param Events Native callback for events */ @override public void onListen(Object arguments, EventChannel.EventSink events) { Log.i("EventChannel"."onListen "+arguments); this.eventSink = events; } /** * Override public void onCancel(Object arguments) {eventSink = null; }}Copy the code

The Dart end

Add the following code to _MyHomePageState to parse BasicChannel Static const EventChannel = EventChannel(EventChannel = EventChannel)"EventChannelPlugin");
​
floatingActionButton: FloatingActionButton(
        onPressed: _sendToDart,
        child: Icon(Icons.add),
      ),
      
 _sendToDart() {
    _streamSubscription = _eventChannel
        .receiveBroadcastStream("123")
        .listen(_onToDart, onError: _onToDartError);
  }
    _onToDart(message) {
    setState(() {
      this.receiveMessage = "EventChannel " + message;
    });
  }
​
  _onToDartError(error) {
    print(error);
  }
@override
  void dispose() {
    super.dispose();
    if (_streamSubscription != null) {
      _streamSubscription.cancel();
      _streamSubscription = null;
    }
  }Copy the code

Analysis of communication source code between Flutter and Native

Several Channel methods are simple to use, but this one focuses on MethodChannel

In the previous analysis of the MethodChannel, it can be seen that as long as both sides register a Channel with the same name, the Flutter end can call through invokeMethod and return a Future object to receive the information returned by the Native end. The Native layer handles events using the Result class in onMethodCall.

1. We first analyze the Native MethodChannel, mainly the constructor of MethodChannel and the setMethoCallHandler method that sets the message processing

1. The constructor public MethodChannel (BinaryMessenger messenger, String name) {this (messenger, name, StandardMethodCodec.INSTANCE); } public MethodChannel(BinaryMessenger messenger, String name, MethodCodec codec) {if (BuildConfig.DEBUG) {
      if (messenger == null) {
        Log.e(TAG, "Parameter messenger must not be null.");
      }
      if (name == null) {
        Log.e(TAG, "Parameter name must not be null.");
      }
      if (codec == null) {
        Log.e(TAG, "Parameter codec must not be null."); } } this.messenger = messenger; this.name = name; this.codec = codec; } 2. SetMethodCallHandler passes name and handler to Messenger @uithRead public voidsetMethodCallHandler(final @Nullable MethodCallHandler handler) {
    messenger.setMessageHandler(
        name, handler == null ? null : new IncomingMethodCallHandler(handler));
  }
  Copy the code

Can be seen from the above setMethodCallHandler key point lies in messenger and IncomingMethodCallHandler

Messenger is actually BinaryMessenger. FlutterView, DartExecutor and others implement this interface. Many places use FlutterView because the Flutter version is 1.7. You cannot create a FlutterFragment using an older version of FlutterView unless your Activity integrates with FlutterActivity. The default getFlutterView() method returns FlutterView. It should look something like this.

Code by mFlutterfragment. GetFlutterEngine (.) getDartExecutor () getBinaryMessenger BinaryMessenger (), So setMethodCallHandler calls the setMethodCallHandler method of this BinaryMessenger, which is actually the dartMessenger object of DartExceutor

Continue tracking DartExecutor’s dartMessenger object

  public DartExecutor(@NonNull FlutterJNI flutterJNI, @NonNull AssetManager assetManager) {
    this.flutterJNI = flutterJNI;
    this.assetManager = assetManager;
    this.dartMessenger = new DartMessenger(flutterJNI);
    dartMessenger.setMessageHandler("flutter/isolate", isolateChannelMessageHandler);
    this.binaryMessenger = new DefaultBinaryMessenger(dartMessenger);
  }Copy the code

As you can see, dartMessenger is initialized here, and the execution of this constructor is in FlutterEngine and FlutterNativeView. As to which method is executed, we will analyze later, which can be understood to be executed when FlutterFragment is started.

So we just need to look at DartMessenger’s setMessageHandler method

  @Override
  public void setMessageHandler(
      @NonNull String channel, @Nullable BinaryMessenger.BinaryMessageHandler handler) {
    if (handler == null) {
      Log.v(TAG, "Removing handler for channel '" + channel + "'");
      messageHandlers.remove(channel);
    } else {
      Log.v(TAG, "Setting handler for channel '" + channel + "'"); messageHandlers.put(channel, handler); }}Copy the code

Can be seen from the above code, the name as the key, IncomingMethodCallHandler as value, deposited in the HashMap messageHandlers type, through the key can find corresponding Handerl, The associated method (onMessage method) is then executed.

IncomingMethodCallHandler

private final class IncomingMethodCallHandler implements BinaryMessageHandler { private final MethodCallHandler handler;  IncomingMethodCallHandler(MethodCallHandler handler) { this.handler = handler; } @Override @UiThread public void onMessage(ByteBuffer message, final BinaryReply reply) { final MethodCall call = codec.decodeMethodCall(message); / /... }}Copy the code

2. Let’s look at the Flutter end again

The main code is as follows

static const MethodChannel _methodChannel = MethodChannel("MethodChannelPlugin"); _methodChannel? .invokeMethod("show".'Dart greetings')? .then((value) {setState(() { this.receiveMessage = value; }); })? .catchError((e) {print(e);
    });Copy the code

MethodChannel constructor

const MethodChannel(this.name, [this.codec = const StandardMethodCodec(), BinaryMessenger binaryMessenger ]) : assert(name ! = null), assert(codec ! = null), _binaryMessenger = binaryMessenger;Copy the code

The invokeMethod method

 @optionalTypeArgs
  Future<T> invokeMethod<T>(String method, [ dynamic arguments ]) {
    return _invokeMethod<T>(method, missingOk: false, arguments: arguments); } @optionalTypeArgs Future<T> _invokeMethod<T>(String method, { bool missingOk, dynamic arguments }) async { assert(method ! = null); final ByteData result = await binaryMessenger.send( name, codec.encodeMethodCall(MethodCall(method, arguments)), );if (result == null) {
      if (missingOk) {
        return null;
      }
      throw MissingPluginException('No implementation found for method $method on channel $name');
    }
    return codec.decodeEnvelope(result) as T;
  }Copy the code

As you can see in the _invokeMethod method, the main method and parameters are converted to binary data via codec and sent through BinaryMessages.

BinaryMessenger is not set here, so it is the default defaultBinaryMessenger. The trace finds an object of type _DefaultBinaryMessenger, which is inherited to binaryMessenger. Let’s look at the send method

//1 
@override
  Future<ByteData> send(String channel, ByteData message) {
    final MessageHandler handler = _mockHandlers[channel];
    if(handler ! = null) //Areturn handler(message);
      //B
    return _sendPlatformMessage(channel, message);
  }
  
//2
    Future<ByteData> _sendPlatformMessage(String channel, ByteData message) {
    final Completer<ByteData> completer = Completer<ByteData>();
    ui.window.sendPlatformMessage(channel, message, (ByteData reply) {
      //....
    });
    return completer.future;
  }
  
  //3
    void sendPlatformMessage(String name,
                           ByteData data,
                           PlatformMessageResponseCallback callback) {
    final String error =
        _sendPlatformMessage(name, _zonedPlatformMessageResponseCallback(callback), data);
    if(error ! = null) throw Exception(error); } //4 window.dart String _sendPlatformMessage(String name, PlatformMessageResponseCallback callback, ByteData data) native'Window_sendPlatformMessage';Copy the code

At comment A above,

MockHandlers is a

Annotation B, may see from the method name, here is the related logic and platform of communication, mainly is to call the UI. Window. SendPlatformMessage method, we can see the back, 3 and 4 will find calls are FlutterEngine library method, Dart’s native method Window_sendPlatformMessage() is then called.

Flutter Engine part logic

The Dart Native mechanism will register a mapping table of Native methods in advance, and the Dart Native method above will find the corresponding Java Native layer method through the JNI layer, and then call its method back to the Java Native layer and back to the Java layer.

Dart’s Native mechanism is to register a method mapping table to Native

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}}); }Copy the code

We can see that the corresponding method is _SendPlatformMessage, which will call SendPlatformMessage

  Dart_Handle SendPlatformMessage(Dart_Handle window,
                                const std::string& name,
                                Dart_Handle callback,
                                const tonic::DartByteData& data) {
  // ...
  if (Dart_IsNull(data.dart_handle())) {
    dart_state->window()->client()->HandlePlatformMessage( // A
        fml::MakeRefCounted<PlatformMessage>(name, response));
  } else {
    const uint8_t* buffer = static_cast<const uint8_t*>(data.data());
​
    dart_state->window()->client()->HandlePlatformMessage( // A
        fml::MakeRefCounted<PlatformMessage>(
            name, std::vector<uint8_t>(buffer, buffer + data.length_in_bytes()),
            response));
  }
​
  return Dart_Null();
}Copy the code

If you look at comment A, it all ends up calling the HandlePlatformMessage method of WindowClient, whose implementation is RuntimeController, The RuntimeController then passes the method to the RuntimeDelegate to be implemented by the Engine class, which implements the method as follows

void Engine::HandlePlatformMessage(
    fml::RefPtr<blink::PlatformMessage> message) {
  if (message->channel() == kAssetChannel) {
    HandleAssetPlatformMessage(std::move(message));
  } else{ delegate_.OnEngineHandlePlatformMessage(std::move(message)); }}Copy the code

See HandleAssetPlatformMessage (STD: : move (the message)); methods

void PlatformViewAndroid::HandlePlatformMessage(
    fml::RefPtr<blink::PlatformMessage> message) {
  JNIEnv* env = fml::jni::AttachCurrentThread();
  fml::jni::ScopedJavaLocalRef<jobject> view = java_object_.get(env);
  // ...
  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 inInvokePlatformMessageXxxResponseCallback. FlutterViewHandlePlatformMessage(env, view.obj(), java_channel.obj(), nullptr, response_id); }}Copy the code

The above method is the last call FlutterViewHandlePlatformMessage initiated () method call. This method specifies the Java class and method that calls the handlePlatformMessage method of the FlutterNativeView.

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); // (1)
  FML_CHECK(CheckException(env));
}
// ...
bool PlatformViewAndroid::Register(JNIEnv* env) {
  // ...
  g_flutter_native_view_class = new fml::jni::ScopedJavaGlobalRef<jclass>(
      env, env->FindClass("io/flutter/view/FlutterNativeView")); / / (2) / /... g_handle_platform_message_method = env->GetMethodID(g_flutter_native_view_class->obj(),"handlePlatformMessage"."(Ljava/lang/String; [BI)V"); / / (3) / /... }Copy the code

Let’s go back to the Native side and continue looking at the handlePlatformMessage method of the FlutterNativeView

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)
}Copy the code

This platformMessageHandler is essentially DartMessenger.

@Override
public void handleMessageFromDart(
    @NonNull final String channel, @Nullable byte[] message, final int replyId) {
  Log.v(TAG, "Received message from Dart over channel '" + channel + "'");
  BinaryMessenger.BinaryMessageHandler handler = messageHandlers.get(channel);
  if(handler ! = null) { try { Log.v(TAG,"Deferring to registered handler to process message.");
      final ByteBuffer buffer = (message == null ? null : ByteBuffer.wrap(message));
      handler.onMessage(buffer, new Reply(flutterJNI, replyId));
    } catch (Exception ex) {
      Log.e(TAG, "Uncaught exception in binary message listener", ex); flutterJNI.invokePlatformMessageEmptyResponseCallback(replyId); }}else {
    Log.v(TAG, "No registered handler for message. Responding to Dart with empty reply message."); flutterJNI.invokePlatformMessageEmptyResponseCallback(replyId); }}Copy the code

Because the Handler is the same as the other Handler, the Handler’s onMessage is called, and the Handler’s onMessage is called.

3, summarize

On the Native side, you register a MethodChannel named channelName and set up a MethodCallHandler that handles messages. On the Flutter side, you also build a MethodChannel named channelName. Then, when the related method is called, it will carry the parameters to the JNI layer through the Dart Native method, and then go to the native side, get the specified channelName, and call its onMessageChannel method. The other two types of communication are similar.

Four, the full array array

Idea 1:

The total permutation has A(n, N) species

Given a group of N elements, the total permutation process can be understood as

  1. If I take any element method first position, there are n ways to do it

  2. I’m going to take one of the remaining n minus 1 and put it in the second position and I’m going to have n minus 1, so you can kind of view it as a complete permutation of n minus 1

  3. Repeat step 2 to the last element

public class One {
    private void println(int[] data,int len) {
        System.out.print("{");
        for(int i = 0; i<len; i++) { System.out.print(data[i] +"");
        }
        System.out.println("}");
    }
    private void swap(int[] data,int i ,int j) {
        int temp = data[i];
        data[i] = data[j];
        data[j]= temp; 
    }
    public void permutation(int[] data,int len,int index) {
        if(index == len) {// println(data, len); }else {
            for(int i = index; i < len; I ++) {// swap the ith element to the current index; swap(data, index, I); // Permutation (data, len, index+1); Swap (data, index, I); // Swap (data, index, I); }}}}Copy the code

But there are two problems with the above recursion. It does not take into account the repeated elements and the stack space. The main problem is that if the stack space is insufficient, the program will crash.

Repeated elements can be exchanged to determine whether the current element and the element after the current position are the same, no interaction, and then recursion.

Idea 2:

  1. Quick sort, sort the array

  2. Data [I] < data[I +1], data[I +1], data[I +1], data[I +1], data[I +1], data[I +1], data[I +1], data[I +1] What it means is that each time you fix the first number, you keep swapping the next number (lexicographically ordered minimum state).

  3. For a[k+1, n-1], the order of the elements in the region is reversed, that is, a[k+1] swaps with a[n], and a[k+2] swaps with a[n-1]… This gives the next row of a[1… n] in the lexicographical order

  4. Such as {1, 2, 3, 4} = = > >

    {1234}

    {1243} {1324} {1342} {1423} {1432}…….

    private void reverse(int[] data,int s,int e) {
        while(s < e) {
            swap(data, s, e);
            s++;
            e--;
        }
    }
​
    public void permutation(int[] data, int len, int index) {
        if (data == null) {
            return; } arrays.sort (data);} println(data, len);while(true) { int pos = -1; // Select the first substitution point from the backfor(int i = len -2; i>=0; i--) {if(data[i]< data[i+1] ) {
                    pos = i;
                    break; }}if(pos == -1) {
                    break; Int sencondIndex = -1; int sencondIndex = -1;for(int i= len -1; i> pos; i--) {if(data[i]> data[pos] ) {
                        sencondIndex = i;
                        break; }} // Swap (data, pos, sencondIndex); // Reverse (data, pos+1, len-1); println(data, len); }}Copy the code



Note 5