一、前言

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

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

二、打包apk并分析

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

现有Android项目集成Flutter

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

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

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

现有Android项目集成Flutter

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

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

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

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

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为特定平台的数据和指令

2、lib文件夹

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

三、打包aar

上面通过编译命令得到了apk,如果要想打包aar,理论上只需要把app/build.gradle中的apply plugin: 'com.android.application改为apply plugin: 'com.android.library,同时注释掉applicationId "com.flutterappfirst",并且将清单文件修改为:

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

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

#!/bin/bash

flutter clean
cd android
./gradlew assembleRelease

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

现有Android项目集成Flutter

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

现有Android项目集成Flutter

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

现有Android项目集成Flutter

可以看到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之类的远程仓库中,这个流程就跟之前发布aar流程一样了,然后在build.gradle中进行远程导入就OK

3、拷贝Flutter提供的集成类到项目中

因为集成aar后需要将Flutter写的界面在Android项目中进行展示,那么就需要有个桥去调用,那么具体的桥Flutter sdk已经提供了代码,具体目录为${flutter_sdk}/packages/flutter_tools/templates/module/android/library/Flutter.tmpl/src/main/java/io/flutter/facade,将facade包直接拷到你现在的Android项目中,或者打包成jar再拷贝集成到现有项目中。

4、展示Flutter界面

在某个新建的activity中调用如下代码:

/**
 * @author xueshanshan
 * @date 2018/12/28
 */
public class FlutterMainActivity extends BaseActivity {
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        FlutterView flutterView = Flutter.createView(this, getLifecycle(), "");
        setContentView(flutterView);
    }
}

然后就能展示Flutter void main() => runApp(new MyApp());这句话对应的界面了。
但是,一般情况下我们是根据原生工程调用让Flutter生成不同的组件作为View来供原生工程调用,那么我们就可以从这个main函数入手

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组件了。

相关文章: