博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Flutter打包aar并且集成现有Android项目
阅读量:4085 次
发布时间:2019-05-25

本文共 5016 字,大约阅读时间需要 16 分钟。

一、前言

Flutter现在已经很火了,但是如果我们要想使用Flutter开发,在新的Flutter项目上集成以前的所有代码肯定是不现实的,同时又不想将Flutter直接侵入到我们的项目结构中去,于是最优解就是将开发的Flutter项目单独编译成aar,然后以组件的形式被主工程依赖。

这样做的好处是显而易见的:对Flutter进行探索开发的同学可以在自己的Flutter工程内编写dart代码,独立运行调试,完成的时候打包成aar集成到主工程中供写native代码的同学接入,两方协同工作,不会产生冲突。

二、打包apk并分析

首先创建flutter工程,会得到如下目录

在这里插入图片描述

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

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

打开apk可以看到,里面目录为

在这里插入图片描述

可以看到多出来很多东西,这些产物都来自于flutter的构建代码,

android/app/build.gradle中依赖了了flutter.gradle

apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
  •  

通过阅读flutter.gradle的构建源码,可以发现在构建apk的过程中,会将需要的文件构建到apk中。

注意:flutter新版本sdk打出来的apk已经不包括flutter_assets文件了

1、assets文件夹

assets文件夹下面有flutter_assets文件夹、flutter_shared文件夹、isolate_snapshot_data、isolate_snapshot_instr、vm_snapshot_data、vm_snapshot_instr文件

  • flutter_assets里是flutter工程产生的assets文件
  • flutter_shared里是封装在flutter.jar里面的处理字符编码的ICU库 (新版本已经不需要了)
  • isolate_snapshot_data、isolate_snapshot_instr、vm_snapshot_data、vm_snapshot_instr为特定平台的数据和指令

debug模式下编译出来的apk没有vm/isolate_snapshot_instr文件,而是kernel_blob.bin,这是因为编译模式不同造成的。

2、lib文件夹

lib文件夹下是特定平台(arm或者x86)的so文件,flutter在Android平台下会默认生成arm-v7架构的的so库,debug模式下会同时生成x86的so文件。

当然有的项目可能配置了

ndk {      abiFilters 'armeabi' }
  •  

所以为了解决so对齐问题,你需要在你的flutter项目中手动添加armeabi的so文件,这样的话打包出来的aar就包含了armeabi的so文件,这个armeabi的so文件可以拷贝armeabi-v7a下面的,一般情况下他们两个是没什么区别的,这里拷贝的话亲测可用。

在app目录下创建libs/armeabi,然后将libflutter.so拷贝到armeabi的目录下,然后在gradle中配置

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

但是由于flutter sdk版本升级比较快,每个版本打出的so可能稍有不同,所以只要升级sdk可能就需要拷贝so,比较麻烦,那么我通过监听打包aar的任务来进行自动拷贝,在gradle文件中配置以下代码

//以下任务为了拷贝so  因为Flutter默认只生成v7的sotask copyFlutterSo(dependsOn: 'transformNativeLibsWithSyncJniLibsForRelease', type: Copy) {    //${buildDir} =  /Users/xueshanshan/project/flutter/flutter_debug_library/build/app    def dir = "${buildDir}/intermediates/library_and_local_jars_jni/release"    from "${dir}/armeabi-v7a/libflutter.so"    into "${dir}/armeabi/"}
  •  

三、打包aar

上面通过编译命令得到了apk,如果要想打包aar,理论上只需要把

app/build.gradle中的apply plugin: 'com.android.application
改为apply plugin: 'com.android.library,
同时注释掉applicationId "com.flutterappfirst",
并且将清单文件修改为:

  •  

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

#!/bin/bashflutter cleancd android./gradlew assembleRelease
  •  

但是,我们将aar解压然后看其目录结构,如下图:

在这里插入图片描述

注意:以下内容在flutter新版本sdk上不需要拷贝,因为此文件已经不需要了

我们发现在aar文件夹下的assets里面缺少了flutter_shared文件夹,icudtl.dat文件正是在该文件夹里面,也就是说flutter.gradle在编译流程中并没有将icudtl.dat文件打进aar包里面,那么我们的解决办法是将该文件夹手动拷贝到flutter工程中,如下图

在这里插入图片描述

然后再次执行上述打包aar的命令,得到app-release.aar文件,正确的结构如下图:

在这里插入图片描述

可以看到flutter_shared文件夹已经放到assets目录里面了。

四、集成到现有Android项目

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

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

2、配置build.gradle

repositories {    flatDir { dirs 'libs' }}dependencies {	compile(name: 'app-release', ext: 'aar')}
  •  

当然也可以将aar发布到jcenter之类的远程仓库中,然后在build.gradle中进行远程导入就OK

3、展示Flutter界面

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

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);    }}
  •  

然后就能展示Flutter void main() => runApp(new MyApp());这句话对应的界面了。

可以看到我们还有传参,那么这个参数就可以在flutter里面接收到,我们可以根据这个参数的值来跳转对应的界面。

void main() => runApp(_widgetForRoute(window.defaultRouteName));
  •  
Widget _widgetForRoute(String route) {  switch (route) {    case 'route1':      return SomeWidget(...);    case 'route2':      return SomeOtherWidget(...);    default:      return Center(        child: Text('Unknown route: $route', textDirection: TextDirection.ltr),      );  }}
  •  

五、第三方依赖问题

假如你的flutter工程依赖了三方的flutter plugin,那么打包aar没法把plugin内容也打进去。

这个时候就可以使用来实现将第三方库的android代码打进aar

还有一个问题,如果你这个aar要上传远程,那么这个库并不会帮你修改pom文件,删除掉相关依赖,所以你需要自己编写任务去删除依赖

当你依赖了flutter第三方库时,会在根目录的.flutter-plugins里面有配置,比如:

shared_preferences=/Users/xueshanshan/.pub-cache/hosted/pub.flutter-io.cn/shared_preferences-0.5.2/

然后在android目录下的settings.gradle里面有配置,会依赖.flutter-plugins里面配置的库作为module

include ':app'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}

所以打包aar时,我们需要做的遍历所有的依赖,然后remove掉,然后替换成compileOnly和flat-aar库提供的embed

转载地址:http://puhni.baihongyu.com/

你可能感兴趣的文章
PEP8 python规范神器
查看>>
Fiddler的安装与使用(基础篇)
查看>>
CentOS7.5搭建Hadoop分布式集群
查看>>
HDU-4315 Climbing the Hill
查看>>
Xampp + Zend Studio + xDebug 环境搭建 (Mac,Windows都适用)
查看>>
利用栈实现字符串中三种括号的匹配问题c++语言实现
查看>>
dijistra和Folyd模板
查看>>
AtCoder Regular Contest 079 E - Decrease (Judge ver.)
查看>>
Android Mms 源码结构
查看>>
spool的简单使用
查看>>
screen模式下鼠标无法滚动【问题】
查看>>
手机端rem
查看>>
Data组件的JSON数据格式
查看>>
正则验证URL合法性
查看>>
在存储过程中编写正确的事务处理代码(SQL Server 2000 & 2005)
查看>>
梦断代码读书笔记03
查看>>
个人总结14
查看>>
MySQL二进制文件规范安装
查看>>
我的devops实践经验分享一二
查看>>
Linux下tmpfs与ramfs的区别
查看>>