关于flutter:腾讯位置服务Flutter业务实践地图SDK-Flutter插件实现一

6次阅读

共计 6958 个字符,预计需要花费 18 分钟才能阅读完成。

前言

Flutter 作为目前通用的业界跨平台解决方案,开拓了一套全新的设计理念,通过自研的 UI 框架,反对高效构建多端平台上的利用,同时放弃着原生利用一样的高性能。在 Flutter 我的项目开发过程中,对插件的开发和复用可能进步开发效率,升高工程的耦合度。Flutter 开发者能够引入对应插件就能够为我的项目疾速集成相干能力,从而专一于具体业务性能的实现。而在 Flutter 我的项目开发过程中面对通用业务逻辑拆分、或者须要对原生能力封装等场景时,开发者须要开发新的组件。

为缩小开发者同时开发 Android 和 iOS 利用的老本,晋升开发效率,升高集成地图 SDK 的门槛,腾讯位置服务团队也打算于业务实际中基于原生地图 SDK 能力封装一套地图 Flutter 插件,反对 Flutter 开发者跨平台调用地图 SDK 接口。笔者在 2019 年实习期间,曾基于过后的最新版本 4.2.4 的 Android 地图 SDK,将地图 SDK 中一些罕用的根底的地图操作性能封装,构建了一套 Android 端的地图 SDK Flutter 插件。

现如今,地图 SDK 曾经迭代到了 4.4.0 版本,笔者也将地图 Flutter 插件进行了一次相干版本升级。本篇文章将介绍地图 Flutter 插件我的项目的构建、地图实例的加载以及 demo 示例出现。对于地图根底操作的性能封装细节将在后续文章中进行具体解说阐明。

地图 Flutter 插件我的项目的构建

地图 Flutter 插件我的项目构造

地图 Flutter 插件我的项目构架的整体构造如下图所示:

android/ios 目录:原生代码。对应为 Android/iOS Flutter 插件目录。
lib 目录:Dart 代码。Flutter 开发者将会应用这里的 Flutter 插件实现的接口。
example 目录:地图 SDK 的 demo 程序。用于验证 Flutter 插件的可用性的应用示例。

地图 Flutter 插件依赖配置项

Android 端的 Flutter 插件配置项与官网对于 Android 地图 SDK 的配置阐明相似,须要配置 android 目录下的两个文件:build.gradle、AndroidManifest.xml。
其中 Android 端的 Flutter 插件的包名为 com.tencent.tencentmap,AndroidManifest.xml 文件配置如下:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
  package="com.tencent.tencentmap">
    <!-- 腾讯地图 sdk 要求的权限 (开始) -->
    <!-- 拜访网络获取地图服务 -->
    <uses-permission android:name="android.permission.INTERNET" />
    <!-- 查看网络可用性 -->
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <!-- 拜访 WiFi 状态 -->
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
    <!-- 须要内部存储写权限用于保留地图缓存 -->
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <!-- 获取 device id 分别设施 -->
    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
    <!-- 获取日志读取权限,帮忙咱们进步地图 sdk 稳定性 -->
    <uses-permission android:name="android.permission.READ_LOGS" />
    <!-- 腾讯地图 sdk 要求的权限 (完结) -->


    <!-- 腾讯定位 sdk 要求的权限  (开始) -->
    <!-- 通过 GPS 失去准确地位 -->
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <!-- 通过网络失去粗略地位 -->
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <!-- 拜访网络. 某些地位信息须要从网络服务器获取 -->
    <uses-permission android:name="android.permission.INTERNET" />
    <!-- 拜访 WiFi 状态. 须要 WiFi 信息用于网络定位 -->
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
    <!-- 批改 WiFi 状态. 发动 WiFi 扫描, 须要 WiFi 信息用于网络定位 -->
    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
    <!-- 拜访网络状态, 检测网络的可用性. 须要网络运营商相干信息用于网络定位 -->
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <!-- 拜访网络的变动, 须要某些信息用于网络定位 -->
    <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
    <!-- 拜访手机以后状态, 须要 device id 用于网络定位 -->
    <uses-permission android:name="android.permission.READ_PHONE_STATE" />

    <!-- 腾讯定位 sdk 要求的权限 (完结) -->
    <application>

        <!-- 如果您 key 确认无误,却仍然受权没有通过,请查看您的 key 的白名单配置 -->
        <meta-data
            android:name="TencentMapSDK"
            android:value="Your key"/>
    </application>
</manifest>

本文应用的 Android 端地图 SDK 版本为 4.4.0。同时,本文 Flutter 插件的实现语言是基于 Kotlin 实现。build.gradle 的依赖配置项如下:

dependencies {
    implementation 'com.android.support:appcompat-v7:27.1.1'
    implementation 'com.tencent.map:tencent-map-vector-sdk:4.4.0'
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
    compile "org.jetbrains.kotlin:kotlin-script-runtime:1.2.71"
}

地图 Flutter 插件加载地图实例

Flutter 插件在下层 UI Dart 端与底层 Native SDK 端之间起到了一层桥接的作用。
Flutter 端与 Native 端之间通信的流程如下图所示:

Flutter 跟 Native 代码能够通过 MethodChannel 进行通信。客户端通过 MethodChannel 将办法调用和参数产生给服务端,服务端也通过 MethodChannel 接管相干的数据。
因而,在 Flutter 插件开发中,MethodChannel 与 EventChannel 是两个不可避免用到的类。
用比拟艰深的语言来解释这两个类的性能:

MethodChannel 的作用是传递办法调用,例如在 flutter 端调用 native 端的办法或 native 端调用 flutter 端的办法。MethodChannel 次要用于办法调用。

EventChannel 的作用是发送音讯,当 native 层想告诉 flutter 层一些音讯的时候,native 层发送音讯,Flutter 接管音讯。EventChannel 通常用于数据流通信。

后续文章将具体解说 MethodChannel 与 EventChannel 在地图 SDK 插件中的应用。
言归正传,本文重点要解说应用 PlatformView 对地图实例进行加载的流程。
PlatformView 的应用形式是与 MethodChannel 的应用形式相似的,具体的加载地图实例流程如下:

(1)Native 端创立 TencentMapView

TencentMapView 继承自 PlatformView。
PlatformView 为 Flutter 1.0 版本中的通用组件,辨别为 Android 和 iOS。在 Android 平台上叫做 AndroidView 组件,在 iOS 平台,叫 UIKitView 组件。
因而利用 PlatformView 构建加载 Native SDK 中的地图实例并在 PlatformView 中保护地图实例的生命周期。
TencentMapView 中也退出了 MethodChannel 与 EventChannel 的注册逻辑,次要用于地图的接口进行双端交互,对于这两局部的阐明将在后续文章中进行具体介绍。
Android 端的 TencentMapView 实现如下:

class TencentMapView(context: Context, private val id: Int, private val activityState: AtomicInteger, tencentMapOptions: TencentMapOptions) : PlatformView, Application.ActivityLifecycleCallbacks{

    // 加载构建地图实例
    private val mapView = MapView(context, tencentMapOptions)
    private val registrarActivityHashCode: Int = TencentmapPlugin.registrar.activity().hashCode()
    
    // 保护地图实例生命周期
    fun setup(){when(activityState.get()){
            STOPPED -> {mapView.onStop()
            }
            RESUMED -> {mapView.onResume()
            }
            CREATED -> {mapView.onStart()
            }
            DESTROYED -> {mapView.onDestroy()
            }
        }

        // flutter 端调用地图 native SDK 相干性能的 MethodChannel
        val mapChannel = MethodChannel(registrar.messenger(), "$mapChannelName$id")
        mapChannel.setMethodCallHandler { methodCall, result ->
            MAP_METHOD_HANDLER[methodCall.method]
                    ?.with(mapView.map)
                    ?.onMethodCall(methodCall, result) ?: result.notImplemented()}
        
        // native SDK 告诉 flutter 层相干音讯的 EventChannel
        val mapEventChannel = EventChannel(registrar.messenger(), "$mapChannelName$id")

    }
}

(2)在插件 Native 层的入口文件 TencentmapPlugin.kt 中注册刚写好的 TencentMapView 实例 tencentMapView:

@JvmStatic
    fun registerWith(registrar: PluginRegistry.Registrar){
    // 将 TencentMapView 实例注册到插件中
    registrar.platformViewRegistry().registerViewFactory("com.tencentmap/map", tencentMapView)
    }

(3)在 Flutter 端的 dart 代码应用 AndroidView,将 AndroidView 嵌入到 TencentMapView 中:

class TencentMapView extends StatelessWidget{
  const TencentMapView({this.onTencentMapViewCreated,});
  final MapCreatedCallback onTencentMapViewCreated;
  @override
  Widget build(BuildContext context) {if (defaultTargetPlatform == TargetPlatform.android) {
      return AndroidView(
          viewType: 'com.tencentmap/map',
          onPlatformViewCreated: _onViewCreated,
          creationParams: { },
          creationParamsCodec: const StandardMessageCodec(),);
    }
  }
}

这里要留神的一点是,在 Android 端和 Flutter 端注册的 viewType 中的字符串值必须保持一致,用于惟一标识。在本文中的标识字符串为 ’com.tencentmap/map’,将 Flutter 端的 AndroidView 与 Native 端的 TencentMapView 建设了关联。

Flutter 插件对应 Demo 示例出现

Demo 示例

demo UI 采纳了 Flutter 自反对的 Material Design 格调的一套 UI 组件。
Flutter demo 调用地图 SDK 展现地图实例的界面如图所示:

demo 中还实现了地图根底操作的相干功能性接口,例如相干覆盖物的绘制等,示例如下图所示:

版本升级过程中遇到的小坑

在理论版本升级过程中,原有我的项目的 demo 运行起来是白屏,控制台打印出如下信息:

[VERBOSE-2:ui_dart_state.cc(157)] Unhandled Exception: ServicesBinding.defaultBinaryMessenger was accessed before the binding was initialized.
If you're running an application and need to access the binary messenger before `runApp()` has been called (for example, during plugin initialization), then you need to explicitly call the `WidgetsFlutterBinding.ensureInitialized()` first.
If you're running a test, you can call the `TestWidgetsFlutterBinding.ensureInitialized()` as the first line in your test's `main()` method to initialize the binding.
#0      defaultBinaryMessenger.<anonymous closure> (package:flutter/src/services/binary_messenger.dart:76:7)
#1      defaultBinaryMessenger (package:flutter/src/services/binary_messenger.dart:89:4)
#2      MethodChannel.binaryMessenger (package:flutter/src/services/platform_channel.dart:140:62)
#3      MethodChannel.invokeMethod (package:flutter/src/services/platform_channel.dart:314:35)
#4      MethodChannel.invokeMapMethod (package:flutter/src/services/platfo<…>

依据控制台的输入信息,通过查阅相干材料后找到了起因:该问题由 Flutter 版本升级导致的重大更改引起的:https://groups.google.com/g/f…
具体解决办法为:在 main.dart 文件中的 main 办法中,须要在 runApp() 前显式调用如下代码:

WidgetsFlutterBinding.ensureInitialized();

总结

本文次要介绍了腾讯地图 SDK Flutter 插件我的项目的构建、地图实例加载、demo 出现,对地图根底功能性接口的封装细节,将会在后续文章继续解说。

正文完
 0