Flutter與Native通訊示例及原始碼分析

CodeOver發表於2020-06-14

前言

參考資料

將Flutter新增到現有Android專案-官網

撰寫雙端平臺程式碼(外掛編寫實現)

Flutter 混合棧複用原理

Flutter啟動頁(閃屏頁)具體實現和原理分析

Android混合開發跳轉Flutter黑屏問題解決方法

目錄

Flutter與Native通訊示例及原始碼分析


一、建立Flutter Module並進行相關配置

Android專案中建立Flutter Module 與其通訊互動

1、建立Flutter Module,筆者將所建立的flutter_module放在跟AS建立的APP專案同一目錄層級上。

flutter create -t module flutter_module
//或者
// Android Studio new Flutter Project -> flutter module複製程式碼

2、配置 官網配置

1.android專案的settings.gradle,不用導包
setBinding(new Binding([gradle: this]))
evaluate(new File(settingsDir.parentFile, 'flutter_module/.android/include_flutter.groovy'))
2.同步後,再在android專案的app模組中即可
implementation project(':flutter')複製程式碼

3、Java中呼叫Flutter 進入Flutter相關頁面

官網介紹 Flutter新增到現有Android專案 裡面有關於如何新增FlutterActivity和FlutterFragment的

注意一些FlutterActivity需要註冊到清單檔案裡

例如以下從native app 跳轉到Flutter的主頁面,並傳遞資料給Flutter,記得FlutterFragment的生命週期,按照官網寫即可
    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();
    }
}
   複製程式碼

當你試過後,會發現兩個問題

  1. Native APP跳轉Flutter出現先黑屏再顯示問題 , 解決方案可以檢視  Android混合開發跳轉Flutter黑屏問題解決方法   Flutter啟動頁(閃屏頁)具體實現和原理分析  個人解決方法是在原生Activity裡面新增一個FlutterFragment就好了,沒有出現黑屏,可以看接下來的示例程式碼

  2. 這樣寫,每次都重啟了新的一摸一樣的Flutter APP,解決方法就是通過再main.dart中,根據傳遞的引數來指定要顯示的路由名,以確定要建立哪個視窗小部件給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 ?'));
        }
    }複製程式碼

小技巧:執行flutter attach命令後,在flutter端更改的程式碼也可以使用熱載入、熱重啟功能。除錯的時候,在Androd Studio上找到Flutter Attach按鈕,點選,然後啟動APP即可。

二、Flutter與Native通訊示例

Flutter中定義了三種不同型別的Channel

  • BasicMessageChannel : 用於傳遞字串和半結構的資訊,持續通訊,收到訊息後可以回覆此訊息。如Native將遍歷到的檔案資訊陸續傳遞到Dart,Flutter將從服務端陸續獲取的資訊給Native。

    Flutter與原生專案的資源是不共享的,可以通過BasicMessageChannel來獲取Native專案的圖示等資源

  • MethodChannel : 用於傳遞方法呼叫, 一次性通訊 ,比如Flutter呼叫Native獲取系統電量,發起Toast呼叫。

  • EventChannel : 用於資料流(event streams)的通訊,持續通訊,收到訊息後無法回覆此次訊息,通常用於Native向Dart的通訊,如:手機電量變化、網路連線變化、感測器等。

1、BasicMessageChannel

簡單效果

Flutter與Native通訊示例及原始碼分析Flutter與Native通訊示例及原始碼分析


程式碼

Native端

//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
/**封裝收發訊息*/
public class BasicMessageChannelPlugin implements BasicMessageChannel.MessageHandler<String>, BasicMessageChannel.Reply<String> {
    private final Activity activity;
    private final BasicMessageChannel<String> messageChannel;
​
    /**
     * 提供給外部呼叫註冊
     *
     * @param binaryMessenger
     * @return
     */
    public static BasicMessageChannelPlugin registerWith(Activity activity, BinaryMessenger binaryMessenger) {
        return new BasicMessageChannelPlugin(activity, binaryMessenger);
    }
​
    private BasicMessageChannelPlugin(Activity activity, BinaryMessenger binaryMessenger) {
        this.activity = activity;
        // name 作為通道唯一標識,Dart端也需要一樣
        this.messageChannel = new BasicMessageChannel<>(binaryMessenger, "BasicMessageChannelPlugin", StringCodec.INSTANCE);
        //設定訊息處理器,處理來自Dart的訊息
        messageChannel.setMessageHandler(this);
    }
​
    /**
     * 接收到訊息後的處理
     *
     * @param message
     * @param reply
     */
    @Override
    public void onMessage(@Nullable String message, @NonNull BasicMessageChannel.Reply<String> reply) {
        Log.i("Basic", "收到Dart Send -> " + message);
        //通過replay回覆Dart
        reply.reply("Native回覆 -> BasicMessageChannel 收到 " + message);
        if (activity instanceof IShowMessage) {
            //IShowMessage為Activity實現的介面,這裡主要用來處理收到的訊息
            ((IShowMessage) activity).onShowMessage(message);
        }
        Toast.makeText(activity, message, Toast.LENGTH_SHORT).show();
    }
​
    /**
     * 傳送訊息
     *
     * @param message  傳送的訊息內容
     * @param callback 傳送訊息後,Dart端的反饋
     */
    void 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) {
        //用於Send中接收Dart的反饋
        if (activity instanceof IShowMessage) {
            //IShowMessage為Activity實現的介面,這裡主要用來處理收到的訊息
            ((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="傳送" />
    </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>
複製程式碼

Dart端

//修改建立的預設檔案
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;
      });
      //收到訊息後通知Native端
      return "收到Native 的訊息 : "+ message;
    }));
    //向Native傳送訊息不等待回覆
    _basicMessageChannel.send("Flutter Init Ok !");
  }
  //向Native傳送訊息並接收回復
  _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),
      ),
    );
  }
}
​複製程式碼

2、MethodChannel

官網MethodChannel獲取電量示例

簡單效果

Flutter與Native通訊示例及原始碼分析Flutter與Native通訊示例及原始碼分析



程式碼

Native端

Activity程式碼上面分析BasicMessageChannel已經貼出來的,註冊一下即可
​
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) {
        //處理來自Dart的方法呼叫
        switch (call.method) {
            case "show":
                Log.i("MethodChannel","show方法 "+call.arguments());
                showMessage(call.arguments());
                //將返回結果給Dart
                result.success("MethodChannelPlugin收到 " + call.arguments);
                break;
            default:
                result.notImplemented();
                break;
        }
    }
​
    private void showMessage(String arguments) {
        if (activity instanceof IShowMessage) {
            ((IShowMessage) activity).onShowMessage(arguments);
        }
​
    }
}
​複製程式碼

Dart端

以下程式碼新增到上面分析BasicChannel的_MyHomePageState 即可,類似一樣寫 
​
static const MethodChannel _methodChannel = MethodChannel("MethodChannelPlugin");
​
floatingActionButton: FloatingActionButton(
        onPressed: _sendToDart,
        child: Icon(Icons.add),
      ),
      
 _sendToDart() {
    _methodChannel?.invokeMethod("show", 'Dart問候')?.then((value) {
      setState(() {
        this.receiveMessage = value;
      });
    })?.catchError((e) {
      print(e);
    });
  }複製程式碼

3、EventChannel

簡單效果

Flutter與Native通訊示例及原始碼分析Flutter與Native通訊示例及原始碼分析



程式碼

Native端

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監聽事件時回撥
     * @param arguments 傳遞的引數
     * @param events  Native回撥Dart時的回撥函式
     */
    @Override
    public void onListen(Object arguments, EventChannel.EventSink events) {
        Log.i("EventChannel","onListen " +arguments);
        this.eventSink = events;
    }
​
    /**
     * Flutter取消監聽時的回撥
     * @param arguments
     */
    @Override
    public void onCancel(Object arguments) {
        eventSink = null;
    }
}複製程式碼

Dart端

以下程式碼新增到上面分析BasicChannel的_MyHomePageState 即可,類似一樣寫,記得註冊的事件在頁面銷燬時需要取消監聽
static const 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;
    }
  }複製程式碼

三、Flutter與Native通訊原始碼分析

幾個Channel方法的使用都很簡單,這裡主要介紹一下MethodChannel

在之前分析的MethodChannel中,可以看出,只要兩端註冊同一個name的Channel就可以讓Flutter端通過invokeMethod呼叫,返回一個Future物件,用於接收Native端返回的資訊,Native層是通過onMethodCall中的Result類來處理相關事件的。

1、我們先分析Native端 MethodChannel,主要是MethodChannel的建構函式和設定訊息處理的setMethoCallHandler方法

1.建構函式
 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 中 會將name和handler傳遞給messenger
   @UiThread
  public void setMethodCallHandler(final @Nullable MethodCallHandler handler) {
    messenger.setMessageHandler(
        name, handler == null ? null : new IncomingMethodCallHandler(handler));
  }
  複製程式碼

從上面的setMethodCallHandler可以看出關鍵點在於messenger和IncomingMethodCallHandler

messenger 這裡的messenger其實就是BinaryMessenger,FlutterView和DartExecutor 等都實現了該介面,好多地方都使用FlutterView,因為這裡Flutter版本是1.7的,不能通過老版本FlutterView去建立一個FlutterFragment,除非你Activity整合FlutterActivity,預設的getFlutterView()方法會返回FlutterView。應該是這樣的。

程式碼中通過mFlutterfragment.getFlutterEngine().getDartExecutor().getBinaryMessenger()得到BinaryMessenger,所以setMethodCallHandler呼叫的是這個BinaryMessenger的setMethodCallHandler方法,而這個BinaryMessenger其實就是DartExceutor的dartMessenger物件

繼續跟蹤DartExecutor的dartMessenger物件

  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);
  }複製程式碼

可以看到dartMessenger是在這裡初始化的,這個建構函式的執行在FlutterEngine裡和FlutterNativeView裡,至於哪個方法裡執行,以後再分析,可以理解為在啟動FlutterFragment的時候會執行。

所以我們只需要看DartMessenger的setMessageHandler方法

  @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);
    }
  }複製程式碼

從上述程式碼可以看出, 將name作為key,IncomingMethodCallHandler作為value,存放到了HashMap型別的messageHandlers中,通過key就可以找出對應的Handerl,然後執行相關方法(onMessage方法)

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);
     //.......
    }
  }複製程式碼

2、我們再來看看Flutter端

主要程式碼如下

static const MethodChannel _methodChannel = MethodChannel("MethodChannelPlugin");
    _methodChannel?.invokeMethod("show", 'Dart問候')?.then((value) {
      setState(() {
        this.receiveMessage = value;
      });
    })?.catchError((e) {
      print(e);
    });複製程式碼

MethodChannel建構函式

  const MethodChannel(this.name, [this.codec = const StandardMethodCodec(), BinaryMessenger binaryMessenger ])
      : assert(name != null),
        assert(codec != null),
        _binaryMessenger = binaryMessenger;複製程式碼

invokeMethod方法

 @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;
  }複製程式碼

可以看到_invokeMethod方法內,主要把方法明和引數通過codec轉換為二進位制資料,通過BinaryMessages send出去。

這裡的binaryMessenger沒有設定,所以是預設的defaultBinaryMessenger,跟蹤發現是_DefaultBinaryMessenger型別物件,它是繼成BinaryMessenger的。繼續看它的send方法

//1 
@override
  Future<ByteData> send(String channel, ByteData message) {
    final MessageHandler handler = _mockHandlers[channel];
    if (handler != null)
      //A
      return 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';複製程式碼

上面註釋A處,

mockHandlers是一個<String,
MessageHandler>的Map,可以在Flutter中mock住某個channelName,這樣的話,傳送這類訊息就會走自己在Flutter寫的handler。我們需要和Native通訊,自然這裡沒有mock住。

註釋B處,從方法名就可以看出,這裡是和平臺通訊的相關邏輯,主要是呼叫了ui.window.sendPlatformMessage方法,可以看後面 3 和 4,會發現呼叫的都是FlutterEngine的庫方法,然後會呼叫dart的native方法 Window_sendPlatformMessage()。

Flutter Engine部分邏輯

這塊先總結以下,Dart的Native機制會事先註冊一個對native方法的對映表,上面那個dart的native方法會通過JNI層找到對應的java native層方法,呼叫其方法回到java native層,回到java層。

Dart的Native機制是先註冊一個對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},
  });
}複製程式碼

我們可以找到對應的方法是_SendPlatformMessage,這個方法會調到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();
}複製程式碼

可以看 註釋 A 處,最後都是會調到WindowClient的HandlePlatformMessage方法,WindowClient的具體實現是RuntimeController,然後RuntimeController會將方法交給RuntimeDelegate來實現,而RuntimeDelegate的具體實現則是Engine類,這個類中的方法實現如下

void Engine::HandlePlatformMessage(
    fml::RefPtr<blink::PlatformMessage> message) {
  if (message->channel() == kAssetChannel) {
    HandleAssetPlatformMessage(std::move(message));
  } else {
    delegate_.OnEngineHandlePlatformMessage(std::move(message));
  }
}複製程式碼

檢視 HandleAssetPlatformMessage(std::move(message)); 方法

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 in InvokePlatformMessageXxxResponseCallback.
    FlutterViewHandlePlatformMessage(env, view.obj(), java_channel.obj(),
                                     nullptr, response_id);             
  }
}複製程式碼

上面這個方法最後呼叫了 FlutterViewHandlePlatformMessage()方法發起呼叫。這個方法指定了Java類和方法,呼叫了FlutterNativeView的handlePlatformMessage方法。

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)
  // ...
}複製程式碼

我們回到Native端繼續看FlutterNativeView的handlePlatformMessage方法

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)
}複製程式碼

這個platformMessageHandler其實就是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);
  }
}複製程式碼

從這個方法可以看出messageHandlers和之前的Handler是同一個物件,取出對應的ChannelName後,呼叫對應的Handler的onMessage處理訊息。

3、總結

在Native端,先註冊名為channelName的MethodChannel,並設定處理訊息的MethodCallHandler,在Flutter端,同樣,構建一個名為channelName的MethodChannel,然後呼叫相關方法時,會攜帶引數,通過dart native方法到jni層,然後到Native端,獲取指定的channelName,呼叫其onMessageChannel方法。其它兩種通訊也是類似。

四、陣列的全排列

思路一:

全排列有 A(n,n) 種

給定一個n個元素組,其全排列過程可以理解為

  1. 任意取一個元素方法第一個位置,有n種方法

  2. 再從剩下的n-1個元素種選擇一個放到第二個位置有 n -1 種,此時可以看成是對 n - 1個元素進行全排列

  3. 重複第二步,到最後一個元素

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++) {
                //將第i個元素交換到當前index處
                swap(data, index, i);
                //對剩下的元素進行全排列
                permutation(data, len, index+1);
                //將第i個元素交換回原位
                swap(data, index, i);
            }
        }
    }
}複製程式碼

但是上面的遞迴有兩個問題,沒有考慮到重複元素和棧空間,主要是棧空間不夠的話,程式會崩潰。

重複元素可以在交換的時候判斷當前元素和當前位置之後的元素是否還有相同的,沒有則互動,然後遞迴。

思路二:

  1. 快速排序,對陣列進行排序

  2. 每次從後尋找第一個data[i] < data[i+1]的下標pos,然後再從後向前(pos+1)找第一個data[i] > data[pos]的下標,互動位置,陣列 pos + 1 到 length - 1反轉顛倒 。做法意義是,每次固定最前面的數,不斷交換排列後面的數(字典序最小狀態)

  3. 反轉意義 對於a[k+1,n-1],反轉該區間內元素的順序,即a[k+1]與a[n]交換,a[k+2]與a[n-1]交換,……,這樣就得到了a[1…n]在字典序中的下一個排

  4. 例如 {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;
        }
        //1.排序
        Arrays.sort(data);
        println(data, len);
        while(true) {
            int pos = -1;
            //從後往前找第一個替換點
            for(int i = len -2;i>=0;i--) {
                if(data[i]< data[i+1] ) {
                    pos = i;
                    break;
                }
            }
                if(pos == -1) {
                    break;
                }
                //從後往前尋找第一個大於替換點的元素
                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);
            
        }
    }複製程式碼


筆記五


相關文章