首页 > 编程语言 >Flutter 集成 uni小程序(UniMPSDK)

Flutter 集成 uni小程序(UniMPSDK)

时间:2023-04-17 16:35:58浏览次数:49  
标签:UniMPSDK 程序 appid uni Android Flutter

原文地址 amoshk.top

又是一段成为鸽子的日子,今天我们直接来上手一把,简单聊聊如何在 Flutter 中集成并使用小程序。

在国内环境下,小程序盛行,随着功能的庞大,许多业务上也需要进行支持,帮助开发进行抽离(减少宿主 APP 频繁发版、方便形成生态、便于独立进行测试与漏洞修复等),使用者也可随需随用、用完即走。

小程序就是一些功能和场景的 “碎片”,而 APP 本身,就是支撑这些 “碎片” 运行的 “宿主”,虽然不是很喜欢小程序所带来的性能问题以及 “阉割” 版 APP 的感觉,但不可否认小程序在开发和用户的快捷场景上带来的好处。

得益于小程序的快速发展,诞生出非常多相关技术支持的公司,在 pub.dev 上其他开发者以及公司直接提供了集成插件,但为了自己能更灵活地把控程序以及担忧插件持续维护的问题,我这里选择了非常成熟的 uni-app 自己进行集成。

效果

首先我们来看看 Flutter 集成 uni小程序的效果(目前已集成 Android 和 iOS)。
源码示例工程(实验室-小程序):https://github.com/AmosHuKe/Mood-Example

实现思路

uni-app 官方文档以及其他教程都是教学如何在原生平台进行集成。
首先我们能够知道 Flutter 和 uni-app 都是属于 UI 框架,作用于原生系统上。
UI 框架之间没有办法直接通讯,但我们可以利用原生的能力将他们两位打通。
我这里利用的是 Flutter 中 MethodChannel 的能力,它能够异步地让 Flutter 与原生平台之间的方法互相调用。
使用通道在 Flutter 和原生平台之间传递消息,如下图所示:

所以,我们只需要在对应的原生平台(Android、iOS)编写业务需要的 uni-app API 方法,Flutter 再通过 MethodChannel 的通道与原生平台进行通讯即可。

具体集成实现

开发环境

基础环境:

Windows

[√] Flutter (Channel stable, 3.7.3, on Microsoft Windows [版本 10.0.22000.1455], locale zh-CN)
[√] Android toolchain - develop for Android devices (Android SDK version 33.0.1)
[√] Visual Studio - develop for Windows (Visual Studio Community 2022 17.4.0)
[√] Android Studio (version 2021.3)
[√] VS Code (version 1.70.0) 

macOS

[✓] Flutter (Channel stable, 3.7.3, on macOS 13.0 22A380 darwin-x64, locale zh-Hans-CN)
[✓] Android toolchain - develop for Android devices (Android SDK version 33.0.0)
[✓] Xcode - develop for iOS and macOS (Xcode 14.2)
[✓] Android Studio (version 2021.3)
[✓] VS Code (version 1.74.3) 

小程序 SDK :

UniMPSDK-Android: 3.4.7.V2.20220425(当前文章主要演示 Android 实现)
UniMPSDK-iOS: 3.4.7

小程序打包基座:

HBuilderX: 3.4.7(尽量与 UniMPSDK 版本一致)
JAVA 环境: jdk1.7+(最优1.8)
Android API: 最低运行版本 21

Android UniMPSDK 集成

官方原生平台教程:uni小程序SDK Android集成教程
以下为我个人集成流程。

UniMPSDK 目录说明

├── UniMPSDK
│   ├── DEMO              # uni小程序 SDK 集成示例 DEMO
│   └── SDK               # uni小程序 SDK
│   │   ├── assets        # assets 资源文件
│   │   ├── libs          # 所有依赖库
│   │   ├── res           # 资源文件
│   │   ├── src           
│   │   │   └── wxapi     # 微信分享支付需要的 activity
│   │   └── proguard.cfg  # 混淆配置 

Libs 依赖库配置

Flutter 项目中位于(没有则自行创建):项目/android/app/libs/
对应 UniMPSDK 位置:UniMPSDK/SDK/libs/

Libs 文件夹依赖库可根据功能需要进行增加或删除,除视频、地图、分享、支付、登录、直播pusher 等 SDK,只集成以下基础模块就可使用:

uniMPSDK-V2-release.aar    # 必须集成
uniapp-v8-release.aar    # 必须集成
[email protected]    # 必须集成
base_oaid_sdk.aar    # 必须集成 注意(3.3.8版本的SDK及以下版本请集成oaid_sdk_1.0.25.aar)
sqlite-release.aar
messaging-release.aar
iBeacon-release.aar
fingerprint-release.aar
contacts-release.aar
Bluetooth-release.aar 

build.gradle 配置

Flutter 项目中位于:项目/android/app/build.gradle

以下为基础配置,具体可参考 UniMPSDK 中 UniMPSDK/DEMO/app/build.gradle 文件。

android {
    defaultConfig {
        applicationId "宿主项目包名 xxx.xxx.xxxxx"
        minSdkVersion 21 // 最低支持21
        targetSdkVersion 33 // 最优26 2.8.11开始最高支持30
        ndk {
            abiFilters 'x86','x86_64','armeabi-v7a','arm64-v8a' // 不支持armeabi
        }
    }

    buildTypes {
        release {
            minifyEnabled true
            // 混淆
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard.cfg'
        }
    }

    // 此处配置必须添加 否则无法正确运行
    aaptOptions {
        additionalParameters '--auto-add-overlay'
        // noCompress 'foo', 'bar'
        ignoreAssetsPattern "!.svn:!.git:.*:!CVS:!thumbs.db:!picasa.ini:!*.scc:*~"
    }
}

flutter {
    source '../..'
}

// 导入 arr 需要的配置
repositories {
    flatDir {
        dirs 'libs'
    }
}

dependencies {
    // libs UniMPSDK
    implementation fileTree(include: ['*.jar'], dir: 'libs')
    implementation fileTree(include: ['*.aar'], dir: 'libs')
    // 必须添加的依赖
    implementation 'com.android.support:recyclerview-v7:28.0.0'
    implementation 'com.android.support:support-v4:28.0.0'
    implementation 'com.android.support:appcompat-v7:28.0.0'
    implementation 'com.alibaba:fastjson:1.1.46.android'
    implementation 'com.facebook.fresco:fresco:1.13.0'
    implementation 'com.facebook.fresco:animated-gif:1.13.0'
    implementation 'com.github.bumptech.glide:glide:4.9.0'
} 

assets 基础资源配置

1、将 UniMPSDK/SDK/assets 内的所有文件按目录放入 项目/android/app/src/main/assets 中(没有目录自行创建)。
2、创建 uni小程序资源文件夹:在 项目/android/app/src/main/assets/ 下创建 apps 文件夹 用于放置 uni小程序打包发行的程序。

proguard 混淆配置

UniMPSDK/SDK/proguard.cfg 文件放入 项目/android/app/ 中。

打包发行 uni-app 小程序

注意:仅支持 uni-app 小程序
HBuilderX 一般情况可以向下兼容 UniMPSDK 的版本,但尽量 HBuilderX 和 UniMPSDK 的版本保持一致。

打开 HBuilderX,新建一个示例项目或者自己编写。

在小程序项目的 manifest.json 内获取并使用 uni-app 应用标识(AppID),也是我们之后指定操作小程序的标识。

使用 【发行 => 原生App-本地打包 => 生成本地打包App资源】 生成本地包。

将打包好的内容放入 项目/android/app/src/main/assets/apps/ 内,文件结构必须如下:

├── apps
│   ├── __UNI__xxxxxxx  # 小程序1
│   │   └── www
│   │   │   └── ...     #小程序编码
│   ├── __UNI__xxxxxxx  # 小程序2
│   │   └── www
│   │   │   └── ...     #小程序编码 

原生实现 MethodChannel

Flutter 项目中位于:
项目\android\app\src\main\kotlin\com\example\moodexample\MainActivity.kt

Android 端需要在 FlutterActivity 的 configureFlutterEngine 方法中获取 FlutterEngine 对象。
接着再创建 MethodChannel 通道实例,最后对通道设置 MethodCallHandler 回调。
根据我们的业务(需要调用 UniMPSDK API 打开指定的 uni小程序 并监听),创建一个名为 UniMP_mini_apps 的通道以及名为 open 的方法帮助我们操作 UniMPSDK 打开小程序。

UniMPSDK API: Android API V2版本参考手册

import androidx.annotation.NonNull;
import io.flutter.embedding.android.FlutterFragmentActivity;
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugins.GeneratedPluginRegistrant
import io.flutter.Log

import io.dcloud.feature.sdk.DCUniMPSDK;
import io.dcloud.feature.sdk.Interface.IUniMP
import io.dcloud.feature.sdk.DCSDKInitConfig
import io.dcloud.feature.sdk.MenuActionSheetItem
import io.dcloud.common.adapter.util.Logger

class MainActivity: FlutterFragmentActivity() {
    override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
        GeneratedPluginRegistrant.registerWith(flutterEngine);
        val messenger = flutterEngine.dartExecutor.binaryMessenger
        // Channel 对象
        val channel = MethodChannel(messenger, "UniMP_mini_apps")
        // Channel 设置回调
        channel.setMethodCallHandler { call, res ->
            // 根据方法名,分发不同的处理
            when(call.method) {
                // 打开指定的 UniMP 小程序
                "open" -> {
                    try {
                        // 接收 Flutter 传入的参数
                        val argumentAppID = call.argument<String>("AppID")
                        // 设置右上角胶囊操作菜单
                        val item = MenuActionSheetItem("关于", "about")
                        val sheetItems: MutableList<MenuActionSheetItem> = ArrayList()
                        sheetItems.add(item)
                        // 初始化uniMPSDK
                        val config = DCSDKInitConfig.Builder()
                                .setCapsule(true)
                                .setMenuDefFontSize("16px")
                                .setMenuDefFontColor("#2D2D2D")
                                .setMenuDefFontWeight("normal")
                                .setMenuActionSheetItems(sheetItems)
                                .build()
                        DCUniMPSDK.getInstance().initialize(this, config)

                        // 打开小程序
                        val unimp: IUniMP = DCUniMPSDK.getInstance().openUniMP(this, argumentAppID)
                        // 监听胶囊菜单点击事件
                        DCUniMPSDK.getInstance().setDefMenuButtonClickCallBack { argumentAppID, id ->
                            when (id) {
                                "about" -> {
                                    Logger.e(argumentAppID + "点击了关于")
                                }
                            }
                        }
                        // 监听小程序关闭
                        DCUniMPSDK.getInstance().setUniMPOnCloseCallBack { argumentAppID -> Log.e("unimp", argumentAppID + "被关闭了") }
                    } catch (e: Exception) {
                        e.printStackTrace()
                    }
                }

                else -> {
                    // 如果有未识别的方法名,通知执行失败
                    res.error("error_code", "error_message", null)
                }
            }
        }
    }
} 

iOS UniMPSDK 集成

可按照示例项目说明进行实现[链接]

添加依赖库及资源文件

根据官方原生平台教程添加完成基础依赖库以及资源文件(uni小程序SDK iOS集成教程

原生实现 MethodChannel

iOS 端通过 MethodChannel 通道,根据我们的业务(需要调用 UniMPSDK API 打开指定的 uni小程序),创建一个名为 UniMP_mini_apps 的通道以及名为 open 的方法帮助我们操作 UniMPSDK 打开小程序(通道、方法名称与 Android 实现相同,方便统一调用)。

先在 Runner-Bridging-Header.h 中引用头文件 #import "DCUniMP.h"

Flutter 项目中位于:项目\ios\Runner\Runner-Bridging-Header.h

之后在 AppDelegate.swift 中编写实现

Flutter 项目中位于:项目\ios\Runner\AppDelegate.swift
UniMPSDK iOS API: iOS API 参考手册

import UIKit
import Flutter

@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate,DCUniMPSDKEngineDelegate {
  override func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
  ) -> Bool {
    let controller : FlutterViewController = window?.rootViewController as! FlutterViewController
    let uniMPMiniApps = FlutterMethodChannel(name: "UniMP_mini_apps",
                                             binaryMessenger: controller.binaryMessenger)
    uniMPMiniApps.setMethodCallHandler({
      [weak self] (call: FlutterMethodCall, result: FlutterResult) -> Void in
      switch(call.method) {
        case "open":
          if let arguments = call.arguments as? Dictionary<String,Any> {
            let AppID: String = arguments["AppID"] as? String ?? ""
            let options = NSMutableDictionary.init(dictionary: launchOptions ?? [:])
            options.setValue(NSNumber.init(value:true), forKey: "debug")
            DCUniMPSDKEngine.initSDKEnvironment(launchOptions: options as! [AnyHashable : Any]);
            self?.checkUniMPResoutce(appid:AppID)
            self?.openUniMP(appid:AppID)
          }
        break
        default:
          result(FlutterMethodNotImplemented)
        break
      }
    })
    GeneratedPluginRegistrant.register(with: self)
    return super.application(application, didFinishLaunchingWithOptions: launchOptions)
  }

  func checkUniMPResoutce(appid: String) -> Void {
    let wgtPath = Bundle.main.path(forResource: appid, ofType: "wgt") ?? ""
    if DCUniMPSDKEngine.isExistsUniMP(appid) {
      let version = DCUniMPSDKEngine.getUniMPVersionInfo(withAppid: appid)!
      let name = version["code"]!
      let code = version["code"]!
      print("小程序:\(appid) 资源已存在,版本信息:name:\(name) code:\(code)")
    } else {
      do {
        try DCUniMPSDKEngine.installUniMPResource(withAppid: appid, resourceFilePath: wgtPath, password: nil)
        let version = DCUniMPSDKEngine.getUniMPVersionInfo(withAppid: appid)!
        let name = version["code"]!
        let code = version["code"]!
        print("小程序:\(appid) 资源释放成功,版本信息:name:\(name) code:\(code)")
      } catch let err as NSError {
        print("小程序:\(appid) 资源释放失败:\(err)")
      }
    }
  }

  /// 打开uni小程序
  @IBAction func openUniMP(appid: String) {
    let configuration = DCUniMPConfiguration.init()
    configuration.enableBackground = true

    DCUniMPSDKEngine.openUniMP(appid, configuration: configuration) { instance, error in
        if instance != nil {
            print("小程序打开成功")
        } else {
            print(error as Any)
        }
    }
  }

  func uniMP(onClose appid: String) {
    print("小程序:\(appid) closed")
  }

  func defaultMenuItemClicked(_ appid: String, identifier: String) {
    print("defaultMenuItemClicked:\(appid) \(identifier)")
  }

  func splashView(forApp appid: String) -> UIView {
      let splashView:UIView = Bundle.main.loadNibNamed("SplashView", owner: self, options: nil)?.last as! UIView
      return splashView
  }
} 

Flutter 调用打开小程序

原生部分编写完成后,在 Flutter 代码中创建一个相同的通道 UniMP_mini_apps,通过 MethodChannel 的 invokeMethod 调用通道的 open 方法,打开并监听指定的小程序。

/// 创建通道与原生沟通
const channel = MethodChannel("UniMP_mini_apps");

Future callNativeMethod(String appID) async {
  try {
    // 通过通道,调用原生代码代码的方法
    final future = await channel.invokeMethod("open", {"AppID": appID});
    // 打印执行的结果
    print(future.toString());
  } on PlatformException catch (e) {
    print(e.toString());
  }
}

/// 传入 uni-app 应用标识(AppID)打开指定的小程序  
callNativeMethod("__UNI__xxxxxxx"); 

扩展能力

  • 非内置 uni小程序集成方式:应用资源包(.wgt)可以选择从云端获取或共享文件等方式,宿主通过 uni小程序 SDK 的 API 调用 releaseWgtToRunPath 实现释放资源包集成 uni小程序。
  • 扩展原生能力
  • 更多依赖包内置功能模块集成
  • 等等…

都可在此处查阅:uni 小程序 SDK

相关资料

标签:UniMPSDK,程序,appid,uni,Android,Flutter
From: https://www.cnblogs.com/cps666/p/17326263.html

相关文章

  • unigui中TuniComboBox限制只能选择,不能手工输入的方法
    问题:TuniComboBox限制只能选择,不能手工输入确认清楚了,对于UniComboBo没有任何问题,对于UniDBComboBox,该属性就存在一定的问题,初始前,不能设置为csDropDownList,必须为默认的csDropDown,不然初始显示数据信息时,该DB对应的原始数据项目信息不出来,需要在窗口的UniFormAfterShow中再将它......
  • uni-app实现类似confirm提示框的效果
    uni.showModal({title:'提示',content:'这是一个模态弹窗',success:function(res){if(res.confirm){console.log('用户点击确定');}elseif(res.cancel){console.log('用户点击取消'......
  • Dbunit
    Dbunit【概念】dbunit是一个基于junit扩展的数据库测试框架。它提供了大量的类对与数据库相关的操作进行了抽象和封装,虽然在80%的情况,你只需使用它极少的api。它通过使用用户自定义的数据集以及相关操作使数据库处于一种可知的状态,从而使得测试自动化、可重复和相......
  • [apue] 一图读懂 Unix 时间日期例程相互关系
    概览 开门见山先上图界定一些术语,方便后面说明:GMT:格林威治平均时,太阳每天经过位于英国伦敦郊区的皇家格林威治天文台的时间为中午12点,1972年之前使用的国际标准时间,因地球在它的椭圆轨道里的运动速度不均匀,这个时刻可能和实际的太阳时相差16分钟。UTC:国际标准时间,相当......
  • uni-app接口请求封装
    首先根目录下新建文件夹取名随意,这里我取名common(意为:常见的、共有的)然后新建request.js文件,贴入以下代码letserver_url='';//请求根路径(服务器地址)lettoken='';//token令牌//process.env.NODE_ENV==='development'?'http://192.168**:6002':'http://***/api&......
  • uni-app 介绍及使用
    一、什么是uni-app    uni-app由dcloud公司开发的多端融合框架,是一个使用 Vue.js 开发所有前端应用的框架,开发者编写一套代码,可发布到iOS、Android、Web(响应式)、以及各种小程序(微信/支付宝/百度/头条/飞书/QQ/快手/钉钉/淘宝)、快应用等多个平台。最大的优势一次开发,多......
  • CesiumJS 源码杂谈 - 从光到 Uniform
    目录1.有什么光2.光如何转换成Uniform以及何时被调用2.1.统一值状态对象(UniformState)2.2.上下文(Context)执行DrawCommand2.3.对WebGLUniform值的封装2.4.自动统一值(AutomaticUniforms)3.在着色器中如何使用3.1.点云3.2.冯氏着色法3.3.地球3.4.模型架构中的光着色阶......
  • Unique Snowflakes uva11572
    找最长的,没有相同元素的区间 双指针#include<iostream>#include<set>usingnamespacestd;constintN=1e6+2;intn,a[N];voidsolve(){ intx=1,y=1,ans=0; set<int>st; while(y<=n){ while(y<=n&&!st.count(a[y]))s......
  • Hugging News #0414: Attention 在多模态情景中的应用、Unity API 以及 Gradio 主题构
    每一周,我们的同事都会向社区的成员们发布一些关于HuggingFace相关的更新,包括我们的产品和平台更新、社区活动、学习资源和内容更新、开源库和模型更新等,我们将其称之为「HuggingNews」,本期HuggingNews有哪些有趣的消息,快来看看吧!社区动向Attention在视觉领域的应用注......
  • uniapp面试题
    1.uniapp进行条件编译的两种方法?小程序端和H5的代表值是什么?通过#ifdef、#ifndef的方式H5:H5MP-WEIXIN:微信小程序2.uniapp的配置文件、入口文件、主组件、页面管理部分pages.json配置文件main.js入口文件App.vue主组件pages页面管理部分3.uniapp上传文件时用到a......