Flutter 打包aar并集成到Android项目_flutter aar-程序员宅基地

技术标签: flutter  flutter aar  libflutter.so  Flutter  flutter集成Android  

前言

flutter项目作为组件集成到原Android项目中,官方提供的集成方式只是一个wiki,网上有其他的集成方式,其中

第一种是以.groovy方式在settings.gradle中添加依赖,如图:

这种依赖方式需要团队每个人都要安装flutter 环境,下载sdk等,否则无法编译项目,所以可以依赖jar/aar的方式来集成,也就是下面第二种方式集成。

第二种是把flutter项目打包成aar,然后拷贝到原Android项目中集成

由于本人采用第一种方式集成后,直接运行到手机正常,打包release失败,原因是Flutter官方只提供了四种CPU架构的SO库:armeabi-v7a、arm64-v8a、x86和x86-64,其中x86系列只支持Debug模式,但是公司原项目中大多SDK都只提供了armeabi架构的库(比如地图sdk和语音sdk等),这就导致了打包时缺少armeabi的libflutter.so库而无法运行flutrer模块,按照美团技术方案的so库兼容方案复制armeabi-v7a中的so到armeabi中,结果release包flutter模块报Check failed: vm. Must be able to initialize the VM.有时候还会其他莫名的错误。

今天尝试了第二种集成方式即打包aar并添加到Android项目中打包,最后成功运行flutter模块

一、手动添加armeabi的so文件(libflutter.so)

看下我的flutter版本v1.7.8+hotfix.4

这是公司原项目的build.gradle,原项目中ndk只使用了armeabi下的ndk支持,没有采用armeabi-v7a的支持,而flutter的SDK中有偏偏没有armeabi的libflutter.so,所以需要自己复制armeabi-v7a中的libflutter.so如果项目采用了armeabi-v7a支持,那就不用复制libflutter.so这一步了,直接打aar集成就好。

 开始复制so

1).打开flutter项目

在命令行输入打包命令 flutter build apk

会编译生成apk文件 位于 build/app/outputs/apk/release/app-release.apk

打开apk可以看到,里面lib目录中的armeabi-v7a中有libflutter.so和libapp.so,这里有armeabi文件是因为我操作了第2)步才会生成。

2) 解压apk包,复制lib目录下armeabi-v7a中的libflutter.so和libapp.so,在android/app/目录下创建libs/armeabi,然后将libflutter.so和libapp.so拷贝到armeabi的目录下,我这里还拷贝了libapp.so,刚开始没有拷贝这个libapp.so,导致我修改flutter代码后重新打aar,新修改的flutter代码没有生效,比较恶心的就是这一点,没次需要重新打aar都要拷贝最新的libapp.so

3)然后在gradle中配置

android{
	sourceSets {
        main {
            jniLibs.srcDirs = ['libs']
        }
    }
}

4)在按第1)步重新打包apk就会看到第一步中的armeabi文件夹,里面有libflutter.so和libapp.so

5)也可以不按 第(2~4)部的从apk中复制这两个so,看着比较麻烦,还有一种是运行命令:

cd android
./gradlew assembleRelease

会在build/intermediates/library_and_local_jars_jni/release/armeabi-v7a/中生成libflutter.so和libapp.so,然后复制libflutter.so和libapp.so到build/intermediates/library_and_local_jars_jni/release/armeabi中

但是由于每次更新flutter代码重新编译后libapp.so都会不一样,此外flutter sdk版本升级也比较快,每个版本打出的so可能稍有不同,所以只要更新flutter代码或者升级sdk可能就需要拷贝so,比较麻烦,那么我通过定义一个Task监听打包aar的任务来进行自动拷贝,稍后我会贴出自定义拷贝so库的gradle代码。

二、打包aar

上面通过编译命令得到了apk,如果要想打包aar,理论上只需要把
app/build.gradle中的apply plugin: 'com.android.application
改为apply plugin: 'com.android.library,


同时注释掉applicationId "com.ffl.my_flutter",
并且将清单文件修改为:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.ffl.my_flutter"/>

然后执行以下命令,就能得到app-release.aar文件

flutter clean
cd android
./gradlew assembleRelease

解压app-release.aar文件就能看到jin/armeabi目录下有了libflutter.so和libapp.so

第一步最后说到,每次更新flutter代码或者升级flutterSDK都要拷贝libflutter.so和libapp.so到libs/armeabi中或者build/intermediates/library_and_local_jars_jni/release/armeabi中,作为一个追求完美的程序猿怎么能忍?所以解决方法是在build.gradle中写个Task,在打aar的时候让它自动拷贝so文件,废话不说,直接上代码:

在android/app下的build.gradle文件中配置以下代码

//以下任务为了拷贝so  因为Flutter默认只生成v7的so
Task copyFlutterSo = project.task('copyFlutterSo') {
    doLast {
        println("armeabi-v7a已生成,准备拷贝libflutter.so和libapp.so")
        copy{
            def buildDir =  '/Users/用户名/Documents/wbg_flutter/my_flutter/build/app'
            def dir = "${buildDir}/intermediates/library_and_local_jars_jni/release"
            from "${dir}/armeabi-v7a/libflutter.so",
                    "${dir}/armeabi-v7a/libapp.so"
            into "${dir}/armeabi/"
            println("libflutter.so和libapp.so已复制到armeabi文件夹")
        }
    }
}

project.tasks.whenTaskAdded { Task task ->
    if (task.name == 'transformNativeLibsWithSyncJniLibsForRelease') {
        task.dependsOn(copyFlutterSo)            // 生成JniLibs之后执行自定义task
    }
}

运行命令:

cd android
./gradlew assembleRelease

不过遗憾的是,由于鄙人水平有限,写的task可能有缺陷,导致每次更新flutter代码或者执行clean操作后,要运行2次命令:./gradlew assembleRelease才会把so复制成功,生成的aar中才会有armeabi类型的so。

我猜原因可能是更新代码重新编译时,发现有改动的代码,导致拷贝好的armeabi目录被删除,重新生成library_and_local_jars_jni/release目录,因为拷贝操作是在transformNativeLibsWithSyncJniLibsForRelease任务后,assembleRelease的任务前,我想就算没有被删除,拷贝的libapp.so也不是最新的,因为编译完成重新生成了新的libapp.so.

build.gradle中加入测试代码:

gradle.taskGraph.afterTask { Task task, TaskState state ->
    if (state.failure) {
        println "FAILED"
    }
    else {
        println "done"
    }
}

执行生成aar命令:./gradlew assembleRelease,查看task执行顺序情况

可以看到transformNativeLibsWithSyncJniLibsForRelease执行完后后面还有其他命令,assembleRelease最后执行,我想就是在assembleRelease执行后又生成了新的library_and_local_jars_jni/release目录,这种只会在代码有变动或者clean后才会生成新的release目录。而第二次运行./gradlew assembleRelease检测到没有变动的代码,就不重新生成release目录,这时执行完拷贝任务,最后打出的aar中就包含了armeabi,这时我的理解,有更好办法的小伙伴麻烦告知一下,共同学习。

三、集成到现有Android项目

上述打包成功的aar就可以作为普通的aar集成到Android项目中了

1、拷贝aar到现有android项目中,拷贝到libs目录下

2、配置build.gradle

repositories {
    flatDir { dirs 'libs' }
}

dependencies {
	compile(name: 'app-release', ext: 'aar')
}

3、展示Flutter界面

其实就是模仿新建的flutter项目,在application中初始化,或者在FlutterMainActivityonCreate中初始化

FlutterMain.startInitialization(this);

然后新建一个Activity继承FlutterActivity

public class FlutterMainActivity extends FlutterActivity {

//跳转该页面的时候可以传要跳转的页面,参数名固定为route
    private static final String ROUTE_PAGE = "route";

    public static Intent makeIntent(Context context, String routePage) {
        if (routePage == null || routePage.equals("")) {
            routePage = "/";
        }
        Intent intent = new Intent(context, FlutterMainActivity.class);
        intent.setAction(Intent.ACTION_RUN);
        intent.putExtra(ROUTE_PAGE, routePage);
        return intent;
    }
 
    public void onCreate(@Nullable Bundle savedInstanceState) {
      	//初始化Flutter
      	FlutterMain.startInitialization(this);
    	super.onCreate(savedInstanceState);
        GeneratedPluginRegistrant.registerWith(this);
    }
}

打开这个activity就能运行到Flutter的main.dart中的main()方法了,可以看到我们还有传参,那么这个参数就可以在flutter里面接收到,我们可以根据这个参数的值来跳转对应的界面。

四、与原生交互

与原生交互方式跟第一种集成方式有一点点区别,就是FlutterView,由于FlutterMainActivity继承了FlutterActivity,所以可以直接通过getFlutterView()来获取FlutterView

根据定义的method名称调用native相应的api并回调

五、第三方依赖问题

假如你的flutter工程依赖了三方的flutter plugin,那么打包aar没法把plugin内容也打进去。
这个时候就可以使用fat-aar-android来实现将第三方库的android代码打进aar

一、 flutter module 中
 
1 使用插件
 apply plugin: 'com.kezong.fat-aar'
 
2 在dependencies 中添加
 
 def flutterProjectRoot = rootProject.projectDir.parentFile.toPath()
    def plugins = new Properties()
    def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins')
    if (pluginsFile.exists()) {
        pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) }
    }
    plugins.each { name, _ ->
        println name
        embed project(path: ":$name", configuration: 'default')
    }
 
 
二、 project 中的 build.gradle 里添加
 
   classpath 'com.kezong:fat-aar:1.0.3'
 
 
三、setting.gradle 中 添加
 
    def flutterProjectRoot = rootProject.projectDir.parentFile.toPath()
 
    def plugins = new Properties()
    def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins')
    if (pluginsFile.exists()) {
        pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) }
    }
 
    plugins.each { name, path ->
        def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile()
        include ":$name"
        project(":$name").projectDir = pluginDirectory
    }
 
复制代码

 

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/l707941510/article/details/99757995

智能推荐

874计算机科学基础综合,2018年四川大学874计算机科学专业基础综合之计算机操作系统考研仿真模拟五套题...-程序员宅基地

文章浏览阅读1.1k次。一、选择题1. 串行接口是指( )。A. 接口与系统总线之间串行传送,接口与I/0设备之间串行传送B. 接口与系统总线之间串行传送,接口与1/0设备之间并行传送C. 接口与系统总线之间并行传送,接口与I/0设备之间串行传送D. 接口与系统总线之间并行传送,接口与I/0设备之间并行传送【答案】C2. 最容易造成很多小碎片的可变分区分配算法是( )。A. 首次适应算法B. 最佳适应算法..._874 计算机科学专业基础综合题型

XShell连接失败:Could not connect to '192.168.191.128' (port 22): Connection failed._could not connect to '192.168.17.128' (port 22): c-程序员宅基地

文章浏览阅读9.7k次,点赞5次,收藏15次。连接xshell失败,报错如下图,怎么解决呢。1、通过ps -e|grep ssh命令判断是否安装ssh服务2、如果只有客户端安装了,服务器没有安装,则需要安装ssh服务器,命令:apt-get install openssh-server3、安装成功之后,启动ssh服务,命令:/etc/init.d/ssh start4、通过ps -e|grep ssh命令再次判断是否正确启动..._could not connect to '192.168.17.128' (port 22): connection failed.

杰理之KeyPage【篇】_杰理 空白芯片 烧入key文件-程序员宅基地

文章浏览阅读209次。00000000_杰理 空白芯片 烧入key文件

一文读懂ChatGPT,满足你对chatGPT的好奇心_引发对chatgpt兴趣的表述-程序员宅基地

文章浏览阅读475次。2023年初,“ChatGPT”一词在社交媒体上引起了热议,人们纷纷探讨它的本质和对社会的影响。就连央视新闻也对此进行了报道。作为新传专业的前沿人士,我们当然不能忽视这一热点。本文将全面解析ChatGPT,打开“技术黑箱”,探讨它对新闻与传播领域的影响。_引发对chatgpt兴趣的表述

中文字符频率统计python_用Python数据分析方法进行汉字声调频率统计分析-程序员宅基地

文章浏览阅读259次。用Python数据分析方法进行汉字声调频率统计分析木合塔尔·沙地克;布合力齐姑丽·瓦斯力【期刊名称】《电脑知识与技术》【年(卷),期】2017(013)035【摘要】该文首先用Python程序,自动获取基本汉字字符集中的所有汉字,然后用汉字拼音转换工具pypinyin把所有汉字转换成拼音,最后根据所有汉字的拼音声调,统计并可视化拼音声调的占比.【总页数】2页(13-14)【关键词】数据分析;数据可..._汉字声调频率统计

linux输出信息调试信息重定向-程序员宅基地

文章浏览阅读64次。最近在做一个android系统移植的项目,所使用的开发板com1是调试串口,就是说会有uboot和kernel的调试信息打印在com1上(ttySAC0)。因为后期要使用ttySAC0作为上层应用通信串口,所以要把所有的调试信息都给去掉。参考网上的几篇文章,自己做了如下修改,终于把调试信息重定向到ttySAC1上了,在这做下记录。参考文章有:http://blog.csdn.net/longt..._嵌入式rootfs 输出重定向到/dev/console

随便推点

uniapp 引入iconfont图标库彩色symbol教程_uniapp symbol图标-程序员宅基地

文章浏览阅读1.2k次,点赞4次,收藏12次。1,先去iconfont登录,然后选择图标加入购物车 2,点击又上角车车添加进入项目我的项目中就会出现选择的图标 3,点击下载至本地,然后解压文件夹,然后切换到uniapp打开终端运行注:要保证自己电脑有安装node(没有安装node可以去官网下载Node.js 中文网)npm i -g iconfont-tools(mac用户失败的话在前面加个sudo,password就是自己的开机密码吧)4,终端切换到上面解压的文件夹里面,运行iconfont-tools 这些可以默认也可以自己命名(我是自己命名的_uniapp symbol图标

C、C++ 对于char*和char[]的理解_c++ char*-程序员宅基地

文章浏览阅读1.2w次,点赞25次,收藏192次。char*和char[]都是指针,指向第一个字符所在的地址,但char*是常量的指针,char[]是指针的常量_c++ char*

Sublime Text2 使用教程-程序员宅基地

文章浏览阅读930次。代码编辑器或者文本编辑器,对于程序员来说,就像剑与战士一样,谁都想拥有一把可以随心驾驭且锋利无比的宝剑,而每一位程序员,同样会去追求最适合自己的强大、灵活的编辑器,相信你和我一样,都不会例外。我用过的编辑器不少,真不少~ 但却没有哪款让我特别心仪的,直到我遇到了 Sublime Text 2 !如果说“神器”是我能给予一款软件最高的评价,那么我很乐意为它封上这么一个称号。它小巧绿色且速度非

对10个整数进行按照从小到大的顺序排序用选择法和冒泡排序_对十个数进行大小排序java-程序员宅基地

文章浏览阅读4.1k次。一、选择法这是每一个数出来跟后面所有的进行比较。2.冒泡排序法,是两个相邻的进行对比。_对十个数进行大小排序java

物联网开发笔记——使用网络调试助手连接阿里云物联网平台(基于MQTT协议)_网络调试助手连接阿里云连不上-程序员宅基地

文章浏览阅读2.9k次。物联网开发笔记——使用网络调试助手连接阿里云物联网平台(基于MQTT协议)其实作者本意是使用4G模块来实现与阿里云物联网平台的连接过程,但是由于自己用的4G模块自身的限制,使得阿里云连接总是无法建立,已经联系客服返厂检修了,于是我在此使用网络调试助手来演示如何与阿里云物联网平台建立连接。一.准备工作1.MQTT协议说明文档(3.1.1版本)2.网络调试助手(可使用域名与服务器建立连接)PS:与阿里云建立连解释,最好使用域名来完成连接过程,而不是使用IP号。这里我跟阿里云的售后工程师咨询过,表示对应_网络调试助手连接阿里云连不上

<<<零基础C++速成>>>_无c语言基础c++期末速成-程序员宅基地

文章浏览阅读544次,点赞5次,收藏6次。运算符与表达式任何高级程序设计语言中,表达式都是最基本的组成部分,可以说C++中的大部分语句都是由表达式构成的。_无c语言基础c++期末速成