Flutter和Android原生之间如何通信

Flutter使用了一个灵活的系统,允许您调用特定平台的API,Flutter平台特定的API支持不依赖于代码生成,而是依赖于灵活的消息传递的方式:

  • 应用的Flutter部分通过平台通道(platform channel)将消息发送到其应用程序的所在的宿主(iOS或Android)。
  • 宿主监听的平台通道,并接收该消息。然后它会调用特定于该平台的API(使用原生编程语言) - 并将响应发送回客户端,即应用程序的Flutter部分。

平台通道(platform channel)

既然Flutter是通过平台通道(platform channel)实现Flutter和原生端的数据传递的。那么先看下官网的架构图

在这里插入图片描述

由图可以看出,在客户端,MethodChannel (API)可以发送与方法调用相对应的消息。 在宿主平台上MethodChannel 在Android((API) 和 FlutterMethodChannel iOS (API) 可以接收方法调用并返回结果。

注意:使用平台通道在客户端(Flutter UI)和宿主(平台)之间传递消息,消息和响应是异步传递的,以确保用户界面保持响应(不会挂起)。

Flutter中定义了几种不同的channel:

在这里插入图片描述


根据上图可以看出几种channel 之间的区别
BasicMessageChannel:
通过异步传递message与平台进行通信
/// A named channel for communicating with platform plugins using asynchronous
/// message passing.

EventChannel:
通过流的方式与平台进行通信
/// A named channel for communicating with platform plugins using event streams.

MethodChannel:
通过异步方法调用的方式与平台进行通信
/// A named channel for communicating with platform plugins using asynchronous
/// method calls.

OptionalMethodChannel:
继承于MethodChannel 忽略了平台
/// A [MethodChannel] that ignores missing platform plugins.

暂时拿MethodChannel进行深入分析

在这里插入图片描述


根据MethodChannel构造方法知道,MethodChannel对象的创建需要两个参数 name跟MethodCodec。

name是MethodChannel的唯一标识,可以理解成channel名字。
codec是用于方法调用和封装结果的编解码器,决定了我们能传递什么类型的数据。
标准的编解码器有如下规则:

在这里插入图片描述


接下来先学习下MethodChannel的使用,边用边分析,Flutter和native间的通信,分为 Flutter主动发送 和 native主动发送 两种情况。

Flutter主动发送给原生

官网例子:https://github.com/flutter/flutter/tree/master/examples/platform_channel

在dart文件中的实现:

  static const MethodChannel methodChannel =
  MethodChannel('samples.flutter.io/battery');
  Future<void> _getBatteryLevel() async {
    String batteryLevel;
    try {
      final int result = await methodChannel.invokeMethod('getBatteryLevel');
      batteryLevel = 'Battery level: $result%.';
    } on PlatformException {
      batteryLevel = 'Failed to get battery level.';
    }
    setState(() {
      _batteryLevel = batteryLevel;
    });
  }

在native端

// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package com.example.platformchannel;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.BatteryManager;
import android.os.Build.VERSION;
import android.os.Build.VERSION_CODES;
import android.os.Bundle;

import io.flutter.app.FlutterActivity;
import io.flutter.plugin.common.EventChannel;
import io.flutter.plugin.common.EventChannel.EventSink;
import io.flutter.plugin.common.EventChannel.StreamHandler;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugin.common.MethodChannel.MethodCallHandler;
import io.flutter.plugin.common.MethodChannel.Result;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugins.GeneratedPluginRegistrant;

public class MainActivity extends FlutterActivity {
  private static final String BATTERY_CHANNEL = "samples.flutter.io/battery";
  private static final String CHARGING_CHANNEL = "samples.flutter.io/charging";

  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    GeneratedPluginRegistrant.registerWith(this);

    new MethodChannel(getFlutterView(), BATTERY_CHANNEL).setMethodCallHandler(
        new MethodCallHandler() {
          @Override
          public void onMethodCall(MethodCall call, Result result) {
            if (call.method.equals("getBatteryLevel")) {
              int batteryLevel = getBatteryLevel();

              if (batteryLevel != -1) {
                result.success(batteryLevel);
              } else {
                result.error("UNAVAILABLE", "Battery level not available.", null);
              }
            } else {
              result.notImplemented();
            }
          }
        }
    );
  }

  private int getBatteryLevel() {
    if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
      BatteryManager batteryManager = (BatteryManager) getSystemService(BATTERY_SERVICE);
      return batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY);
    } else {
      Intent intent = new ContextWrapper(getApplicationContext()).
          registerReceiver(null, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
      return (intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1) * 100) /
          intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
    }
  }
}

以上就是从Flutter端主动发起与native端的通信,dart通过await methodChannel.invokeMethod(‘getBatteryLevel’)发送消息,native端通过MethodChannel中MethodCallHandler的onMethodCall进行接收flutter的通信,并通过result.success进行回传信息。

有几点需要注意

  • dart中methodChannel 声明的时候的 name要保证与native端的一致,且唯一。
  • dart中methodChannel.invokeMethod(‘getBatteryLevel’),在native端要进行判断,只有方法名匹配才给予响应, if (call.method.equals(“getBatteryLevel”))。

这里flutter通过 invokeMethod与native进行通信

在这里插入图片描述


查看invokeMethod()方法, method为 MethodCall的标识, arguments为参数,注意这里的参数必须要遵守上面的规则(默认情况下不能直接传自定义类,但是我们可以将其转为Json之后再传递,也可以去改造 MethodCode,以此进行复杂数据结构的传递)。

注:可以看到,其是一个 async标记的方法,返回值为Future。那么我们在接受这个方法的返回值的时候,就必须要使用 await进行修饰。要调用使用 await,必须在有 async标记的函数中运行。具体调用和使用的方式可以看官网的例子。在这我们先继续深入。

BinaryMessages.send()

在这里插入图片描述


接下来_sendPlatformMessage()——window.sendPlatformMessage()

在这里插入图片描述


在这里插入图片描述

最终调用native方法与原生进行通信的。
在flutter engine中查看源码

在这里插入图片描述


在这里插入图片描述

看最关键的方法: dart_state->window()->client()->HandlePlatformMessage()

在这里插入图片描述


![在这里插入图片描述](https://www.icode9.com/i/ll/?i=20190413170758725.?,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly94dmlkZW9zLmJsb2cuY3Nkbi5uZXQ=,size_16,color_FFFFFF,t_70


一步一步跟下来 到了 这里g_handle_platform_message_method

在这里插入图片描述


在这里插入图片描述


接下来就是 在FlutterJNI 与 FlutterNativeView 之间进行绑定了

在这里插入图片描述


在这里插入图片描述


这里就可以通过JNI调用Android 端了。

原生主动发送给Flutter

   /**
     * native data to  dart
     */
    private void native2Dart() {
        /**
         * 数据流的通信(event streams)
         */
        EventChannel eventChannel = new EventChannel(getFlutterView(), EVENT_CHANNEL);
        EventChannel.StreamHandler streamHandler = new EventChannel.StreamHandler() {

            @Override
            public void onListen(Object arguments, EventSink eventSink) {
                Log.e("plateform_channel", "arguments: " + arguments.toString());
                eventSink.success(data);
            }

            @Override
            public void onCancel(Object arguments) {
                Log.e("plateform_channel", "arguments: " + arguments.toString());
            }
        };
        eventChannel.setStreamHandler(streamHandler);
    }

Flutter端:

  static const EventChannel eventChannel =
  EventChannel(FlutterChannel.CHANNEL_RECEIVE_DATA);
  @override
  void initState() {
    super.initState();

    eventChannel.receiveBroadcastStream(['arg1', 'arg2']).listen(_onEvent,
        one rror: _onError);
  }

  void _onEvent(Object event) {
    var animal = json.decode(event);
    print(AnimalsRoot.fromJson(animal).animals.cat.name);
    setState(() {
      _receive_data = '$event';
    });
  }

  void _onError(Object error) {
    setState(() {
      _receive_data = 'Receive  failed';
    });
  }

用图表示其中的关联就是

在这里插入图片描述

这里有我学习Flutter过程中的一个Demo,会持续更新Demo的内容,对Flutter 有兴趣的朋友可以一起学习。
https://github.com/CodingForAndroid/flutter_new

欢迎爱学习的小伙伴加群一起进步:230274309

原文地址:https://blog.csdn.net/u011733020/article/details/89262132

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


这篇文章主要讲解了“FlutterComponent动画的显和隐怎么实现”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究...
这篇文章主要讲解了“flutter微信聊天输入框功能如何实现”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“f...
本篇内容介绍了“Flutter之Navigator的高级用法有哪些”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处...
这篇文章主要介绍“Flutter怎么使用Android原生播放器”,在日常操作中,相信很多人在Flutter怎么使用Android原生播放器问题上存在疑惑,小编查阅了各式资料,整...
Flutter开发的android端如何修改APP名称,logo,版本号,具体的操作步骤:修改APP名称找到文件:android\\app\\src\\main\\AndroidManifest.xml
Flutter路由管理初识路由概念一.路由管理1.1.Route1.2.MaterialPageRoute1.3.Navigator1.4.路由传值1.5 命名路由1.6.命名路由参数传递1.7.适配二、路由钩子三、onUnknownRoute四、结尾初识路由概念路由的概念由来已久,包括网络路由、后端路由,到现在广为流行的前端路由。无论路由的概念如何应用,它的核心是一个路由映射表。比如:名字 detail 映射到 DetailPage 页面等。有了这个映射表之后,我们就可以方便的根据名字来完成路由的转发
前提:针对Android开发者(windows系统下),已安装Git,AndroidStudio(建议4.0+版本)一.下载Flutter SDK地址:https://flutter.dev/docs/development/tools/sdk/releases,在 Stable channel (Windows)里面下最新版本即可。Flutter的渠道版本会不停变动,请以Flutter官网为准。在中国,要想正常获取安装包列表或下载安装包,可能需要翻墙,也可以去Flutter github项目下去下载安
一、变量变量是一个引用,根据Dart中“万物皆对象”原则,即变量存储的都是对象的引用,或者说它们都是指向对象。1.1.声明变量://1.不指定类型var name = 'aaa';//2.明确指定类型String name = 'aaa';因为有类型推导,所以两种实现效果一样,官方推荐在函数内的本地变量尽量使用var声明。在变量类型并不明确的情况下,可以使用dynamic关键字//3.使用dynamic关键字dynamic name = 'aaa';1.2.默认值未初始化的变量
前言Flutter2.0发布不久,对web的支持刚刚进入stable阶段。初学几天,构建web应用时候碰到一些问题,比如中文显示成乱码,然后加载图片出现图片跨域问题:Failed to load network image...Trying to load an image from another domain?1.开启web端构建:使用下面这个命令才可以开启Web端构建的支持flutter config --enable-web提示我们:重新启动编辑器,以便它们读取新设置。2.重
一.Flutter打Android release包的步骤:1.为项目创建一个.jks签名文件(很简单,跳过)2.创建一个文件key.properties,直接复制下面key.properties位置如图:在里面输入一下内容:storePassword=iflytekkeyPassword=iflytekkeyAlias=teachingmachinestoreFile=E:/teacher/app/keys/TeachingMachine.jks输入你自己的passwork以及
1 问题Android原生向js发消息,并且可以携带数据2 实现原理Android原生可以使用RCTEventEmitter来注册事件,然后这里需要指定事件的名字,然后在js那端进行监听同样事件的名字监听,就可以收到消息得到数据Android注册关键代码reactContext.getJSModule(DeviceEventManagerModule.RCT...
1 Flexbox布局1) flexDirection 可以决定布局的主轴,子元素是应该沿着水平轴(row)方向排列,还是沿着竖直轴(column)方向排列2) justifyContent 决定其子元素沿着次轴(与主轴垂直的轴,比如若主轴方向为row,则次轴方向为column)的排列方式 有flex-start、center、flex-end、space-around...
1 实现的功能在网上看React Native文档,我特码就想实现一个页面到另外一个页面的跳转,然后另外一个页面怎么获取参数,特么没找到一个说清楚的,要么太复杂,要么说了不理解,下面是我自己写的一个App.js文件,实现一个Home页面跳到另外Details页面,并且携带了参数怎么在Details页面获取,就是这么简单粗暴.2 测试DemoApp.js文件如下...
1 问题在一个文件构建一个对象,然后在另外一个文件里面new这个对象,通过构造方法传递参数,然后再获取这个参数2 测试代码Student.js文件如下'use strict';import React from 'react'import {NativeModules, NativeEventEmitter, DeviceEventEmitter,Ale...
1 简单部分代码export default class App extends Component&amp;lt;Props&amp;gt; { render() { return ( &amp;lt;View {styles.container}&amp;gt; &amp;lt;View {styles.welcome}&amp;gt; &amp;l...
1 怎么实现发送和接收事件理论上封装了Android原生广播的代码,需要注册和反注册,这里用DeviceEventEmitter实现//增加监听DeviceEventEmitter.addListener//取消监听//this.emitter.remove();这里可也可以通过安卓原生向页面js发送消息,可以参考我的这篇博客React Native之Android原生通过Dev...
1、Component介绍一般Component需要被其它类进行继承,Component和Android一样,也有生命周期英文图片如下2 具体说明1)、挂载阶段constructor()//构造函数,声明之前先调用super(props)componentWillMount()//因为它发生在render()方法前,因此在该方法内同步设置状态...
1 触摸事件普通点击我们可以使用onPress方法,我们可以使用Touchable 系列控件设计我们的按钮TouchableHighlight 背景会在用户手指按下时变暗TouchableNativeFeedback用户手指按下时形成类似墨水涟漪的视觉效果TouchableOpacity指按下时降低按钮的透明度,而不会改变背景的颜色TouchableWithoutFeedbac...
1 问题部分代码如下class HomeScreen extends React.Component { render() { return ( &amp;lt;View {{ flex: 1, alignItems: 'center', justifyContent: 'center' }}&amp;gt; &amp;lt;Text&amp;gt;Home Scre...
1 Props(属性)和State(状态)和简单样式简单使用App.js代码如下/** * Sample React Native App * https://github.com/facebook/react-native * * @format * @flow */import React, {Component} from 'react';import {Pla...