• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

Flutter以离线aar嵌入安卓工程的具体做法

武飞扬头像
拳布离手
帮助1

血泪史

项目引入了FlutterBoost以嵌入flutter模块,但是开发的人员分散,组织结构复杂,技术水平参差不齐,导致flutter环境安装推行困难(尽管我提供了环境安装的文档),所以保险起见,用离线的形式将 flutter模块嵌入到安卓原生工程。

研究期间,经历了屎山项目的各种环境问题(主要是Android Gradle Plugin 版本,Jdk版本,flutterSDk版本,以及FlutterBoost版本各种不兼容,各种看不懂的编译器问题),最终以 aar so 离线文件的方式,让原生工程能够以Fragment的形式,或者以activity的方式打开 flutter页面,解除了 Flutter环境与原生工程硬性绑定关系,实现了flutter模块的静态发布,又能够支撑flutter开发者的动态调试,不影响flutter模块的开发工作。

综合经验总结如下,供有类似经历的开发者参考。

最终方案

先声明每个关键部分的版本号:

  • JDK - 11
  • Gradle Wrapper - 6.7
  • FlutterBoost - 3.1.3
  • Flutter sdk - 2.5.3
  • AndroidStudio - Electric Eel | 2022.1.1 Patch 2

Flutter侧

步骤1,创建Flutter Module工程

创建Flutter工程时,必须选择module类型,因为它才是能够嵌入native的工程形式。

学新通

创建完成之后尝试去打它的aar包,

学新通

坑1

第一个坑来了,有可能你们不会出现,这个属于环境问题:

* Where:
Settings file 'D:\testPro\20230801\flutter_pro_002.android\settings.gradle' line: 6

* What went wrong:
A problem occurred evaluating settings 'android_generated'.
> BUG! exception in phase 'semantic analysis' in source unit 'D:\testPro\20230801\flutter_pro_002.android\include_flutter.groovy' Unsupported class file major version 62

提取问题关键字 :Unsupported class file major version 62, 它发生的主要原因是:

你尝试运行一个使用了较高版本的 Java 编译器编译的类文件,而当前运行环境的 Java 版本不支持该版本的类文件, 简单来说就是 JRE和 JDK不匹配,比如jdk用的是 17,但是jre停留在 11。

解决方法,只要让 两者一致即可,具体做法为,找到 flutter项目的 gradle.properties文件,加入此属性:

org.gradle.java.home=D://env//androidStudio//jbr

(注意,找到你当前安卓项目设置的JDK路径,D://env//androidStudio//jbr 是我自己本机的JDK路径,不要照搬)

然后再次尝试 打aar,就会发现:


D:\env\flutter_windows_2.5.3-stable\flutter\bin\flutter.bat --no-color build aar

 Building with sound null safety 

Running Gradle task 'assembleAarDebug'...                          41.0s
√ Built build\host\outputs\repo.
Running Gradle task 'assembleAarProfile'...                        37.7s
√ Built build\host\outputs\repo.
Running Gradle task 'assembleAarRelease'...                        42.0s
√ Built build\host\outputs\repo.

Consuming the Module
  1. Open <host>\app\build.gradle
  2. Ensure you have the repositories configured, otherwise add them:

      String storageUrl = System.env.FLUTTER_STORAGE_BASE_URL ?: "https://storage.谷歌apis.com"
      repositories {
        maven {
            url 'D:\testPro\20230801\flutter_pro_002\build\host\outputs\repo'
        }
        maven {
            url "$storageUrl/download.flutter.io"
        }
      }

  3. Make the host app depend on the Flutter module:

    dependencies {
      debugImplementation 'com.example2.flutter_pro_002:flutter_debug:1.0'
      profileImplementation 'com.example2.flutter_pro_002:flutter_profile:1.0'
      releaseImplementation 'com.example2.flutter_pro_002:flutter_release:1.0'
    }


  4. Add the `profile` build type:

    android {
      buildTypes {
        profile {
          initWith debug
        }
      }
    }

To learn more, visit https://flutter.dev/go/build-aar

这就是已经成功打出aar包的表现,具体的包位置在:build\host\outputs\repo 中:

学新通

坑1解决完毕。

步骤2:集成FlutterBoost

由于我用的是FlutterSDK的2.5.3版本,所以我能匹配的最新flutterBoost版本是 3.1.3.

引入flutterBoost
  flutter_boost:
    git:
      url: 'https://github.com/alibaba/flutter_boost.git'
      ref: '3.1.3'

记得 pub get拉取依赖包

编辑dart代码

为了管理方便,我将flutterBoost相关的代码都集中到 boost_core.dart中,代码如下:

iimport 'package:flutter/material.dart';
import 'package:flutter_boost/flutter_boost.dart';
import 'package:flutterpro/test_route_widget.dart';

class BoostCore {
  static void init() {
    PageVisibilityBinding.instance
        .addGlobalObserver(AppGlobalPageVisibilityObserver());
    CustomFlutterBinding();
  }

  static Map<String, FlutterBoostRouteFactory> routerMap = {
    'TopOrg': (RouteSettings settings, String? uniqueId) {
      return PageRouteBuilder<dynamic>(
          settings: settings,
          pageBuilder: (_, __, ___) {
            var arguments = settings.arguments;
            var name = settings.name;

            debugPrint("flutter收到的参数是: arguments:$arguments");
            debugPrint("flutter收到的参数是: name:$name");

            return const TestRouteWidget();
          });
    },
  };

  static Route<dynamic>? routeFactory(
      RouteSettings settings, String? uniqueId) {
    FlutterBoostRouteFactory? func = routerMap[settings.name!];
    if (func == null) {
      return PageRouteBuilder<dynamic>(
          settings: settings,
          pageBuilder: (_, __, ___) => const TestRouteWidget());
    }
    return func(settings, uniqueId);
  }
}

class CustomFlutterBinding extends WidgetsFlutterBinding
    with BoostFlutterBinding {}

class AppGlobalPageVisibilityObserver extends GlobalPageVisibilityObserver {
  @override
  void onPagePush(Route<dynamic> route) {
    Logger.log(
        'boost_lifecycle: AppGlobalPageVisibilityObserver.onPageCreate route:${route.settings.name}');
  }

  @override
  void onPageShow(Route<dynamic> route) {
    Logger.log(
        'boost_lifecycle: AppGlobalPageVisibilityObserver.onPageShow route:${route.settings.name}');
  }

  @override
  void onPageHide(Route<dynamic> route) {
    Logger.log(
        'boost_lifecycle: AppGlobalPageVisibilityObserver.onPageHide route:${route.settings.name}');
  }

  @override
  void onPagePop(Route<dynamic> route) {
    Logger.log(
        'boost_lifecycle: AppGlobalPageVisibilityObserver.onPageDestroy route:${route.settings.name}');
  }

  @override
  void onForeground(Route route) {
    Logger.log(
        'boost_lifecycle: AppGlobalPageVisibilityObserver.onForeground route:${route.settings.name}');
  }

  @override
  void onBackground(Route<dynamic> route) {
    Logger.log(
        'boost_lifecycle: AppGlobalPageVisibilityObserver.onBackground route:${route.settings.name}');
  }
}

class CustomInterceptor1 extends BoostInterceptor {
  @override
  void onPrePush(
      BoostInterceptorOption option, PushInterceptorHandler handler) {
    Logger.log('CustomInterceptor#onPrePush1~~~, $option');
    // Add extra arguments
    option.arguments!['CustomInterceptor1'] = "1";
    super.onPrePush(option, handler);
  }

  @override
  void onPostPush(
      BoostInterceptorOption option, PushInterceptorHandler handler) {
    Logger.log('CustomInterceptor#onPostPush1~~~, $option');
    handler.next(option);
  }
}

    Logger.log('CustomInterceptor#onPostPush1~~~, $option');
    handler.next(option);
  }
}

然后是main.dart:

import 'package:flutter/material.dart';
import 'package:flutter_boost/flutter_boost.dart';

import 'boost_core.dart';

Future<void> main() async {
  BoostCore.init();

  // 实际上这里就是在启动引擎
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      builder: (context, child) {
        return MediaQuery(
            data: MediaQuery.of(context).copyWith(textScaleFactor: 1.0),
            child: child!);
      },
      home: FlutterBoostApp(BoostCore.routeFactory,
          interceptors: [CustomInterceptor1()]),
    );
  }
}

最后是 一个 测试的 test_route_widget.dart:

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';

class TestRouteWidget extends StatelessWidget {
  const TestRouteWidget({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Container(
      color: Colors.red,
    );
  }
}

然后再次尝试打aar,看是否能成功。

坑2

每一次你重新pub get,flutter项目的android临时文件都会重新刷新,所以坑1中的操作你得再做一次。修改之后,有可能无效,这时候重启工程试试看,不行就重启电脑,这个也属于AndroidStudio的bug。

一般重启工程之后就能顺利打出AAR。

拿到release版本的AAR

学新通

Flutter这端的工作已经完成,这个aar文件先放着。

Android侧

未完待续...

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhfkgbee
系列文章
更多 icon
同类精品
更多 icon
继续加载