原生工程接入Flutter实现混编(1)
implementation project(“:flutter”)ok,这两步是官方的指引,配置完之后就完事了? 太天真了,还需要有一些额外的调整。构建一下就知道了:异常1:Gradle DSL method not found: ‘google()’项目中用的gradle版本还是比较旧的,需要升级一下:异常2:AAPT error:resource android:attr/fontVaria
implementation project(“:flutter”)
ok,这两步是官方的指引,配置完之后就完事了? 太天真了,还需要有一些额外的调整。构建一下就知道了:
异常1:Gradle DSL method not found: ‘google()’

项目中用的gradle版本还是比较旧的,需要升级一下:

异常2:AAPT error:resource android:attr/fontVariationSettings not found

这个异常需要将compileSdkVersion升级到28,之前是26。
异常3:assert appProject !=null

这个问题巨坑,我们的主工程名是course,但flutter的构建脚本是硬编码为app,有两种解决办法:
-
重命名module名字,命名为app
-
修改flutter脚本(我选的是这种)

这样,flutter脚本就能找到我们的工程,编译也ok了。
但其实还有问题,因为目前我们还未升级support包到AndroidX版本,而创建出来的module工程默认是支持AndroidX的,所以我们需要进行降级,等后续升级工程之后再处理。
修改edu_flutter_module/pubspec.yaml,将androidX改为false:
module:
androidX: false
androidPackage: com.tencent.edu
iosBundleIdentifier: com.tencent.edu
改完这个之后,终于工程编译通过了,但这就结束了吗,还有坑等着你呢。
上一个主题我们解决掉一些坑之后终于把flutter作为一个module集成到我们的工程中,接下来我们尝试写个页面嵌入到我们页面。
目前课堂用的flutter版本是:v1.12.13+hotfix.5,这个版本的使用跟之前的版本会有些差异,可以参考官方的wiki:
https://github.com/flutter/flutter/wiki/Upgrading-pre-1.12-Android-projects
这里我尝试把课堂的首页替换成Flutter页面,做了以下调整:
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
// TODO: 2020-04-01 增加flutter视图
View view = inflater.inflate(R.layout.fragment_index, container, false);
FlutterEngine flutterEngine = new FlutterEngine(getActivity());
flutterEngine.getDartExecutor().executeDartEntrypoint(
DartExecutor.DartEntrypoint.createDefault()
);
flutterEngine.getNavigationChannel().setInitialRoute(“route1”);
FlutterView flutterView = new FlutterView(getActivity());
FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
FrameLayout flContainer = view.findViewById(R.id.fl_content);
// 关键代码,将Flutter页面显示到FlutterView
flutterView.attachToFlutterEngine(flutterEngine);
flContainer.addView(flutterView, lp);
return view;
}
fragment_index.xml
<LinearLayout xmlns:android=“http://schemas.android.com/apk/res/android”
android:orientation=“vertical” android:layout_width=“match_parent”
android:layout_height=“match_parent”>
<FrameLayout
android:id=“@+id/fl_content”
android:layout_width=“match_parent”
android:layout_height=“match_parent”
/>
dart代码实现:
main.dart
import ‘package:edu/home_page.dart’;
import ‘package:flutter/material.dart’;
import ‘dart:ui’;
import ‘dart:convert’;
void main() {
runApp(_widgetForRoute(window.defaultRouteName));
}
// 获取路由名称
String _getRouteName(String s) {
if (s.indexOf(‘?’) == -1) {
return s;
} else {
return s.substring(0, s.indexOf(‘?’));
}
}
// 获取参数
Map<String, dynamic> _getParamsStr(String s) {
if (s.indexOf(‘?’) == -1) {
return Map();
} else {
return json.decode(s.substring(s.indexOf(‘?’) + 1));
}
}
Widget _widgetForRoute(String url) {
String route = _getRouteName(url);
Map<String, dynamic> params = _getParamsStr(url);
switch (route) {
default:
return MaterialApp(
theme: ThemeData(
primaryColor: Color(0xFF008577),
primaryColorDark: Color(0xFF00574B),
),
home: HomePage(route, params),
);
}
}
home_page.dart
import ‘package:flutter/material.dart’;
class HomePage extends StatefulWidget {
String route;
Map<String, dynamic> params;
HomePage(this.route, this.params);
@override
State createState() {
return _HomePageState();
}
}
class _HomePageState extends State {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(‘Flutter页面’),
automaticallyImplyLeading: false,
),
body: Center(
child: Text(‘首页’),
),
);
}
}
ok,Demo代码到这里就写完了,然后信心满满的run起来,发现直接崩了。这就是我要跟你说的其中一个坑,so架构的问题:

大部分老项目工程中用到的是armeabi架构,但flutter最低支持到armeabi-v7a,如果不做特殊处理,就会出现上面的Crash。怎么办?解决办法自然有,就是找到flutter module工程的构建物,把armeabi-v7a下的libFlutter.so拿出来,放到原生工程的armeabi下,我写了个shell脚本,然后通过Hook Gradle Task的方式插入到编译流程中去。
copyFlutterSo.sh
#!/bin/bash
当前目录
CURRENT_DIR=“pwd”
当前build目录,具体以工程为准
BUILD_DIR=“pwd/build”
gradle 5.6.2 armeabi so路径
#ARMEABI_DIR=“$BUILD_DIR/intermediates/merged_native_libs/debug/out/lib/armeabi”
gradle 4.10.1 armeabi so路径
ARMEABI_DIR=“$BUILD_DIR/intermediates/transforms/mergeJniLibs/$1/0/lib/armeabi”
armeabi-v7a so存放路径
ARMEABI_V7A_DIR=“$BUILD_DIR/intermediates/transforms/mergeJniLibs/$1/0/lib/armeabi-v7a”
echo -e “\033[47;30m ========== copy $1 libflutter.so ========== \033[0m”
if [[ “$1” == “debug” ]]; then
将libflutter.so copy到armeabi架构中去
cp -rf ${ARMEABI_V7A_DIR}/libflutter.so ${ARMEABI_DIR}
echo “copy ${ARMEABI_V7A_DIR}/libflutter.so to ${ARMEABI_DIR}”
elif [[ “$1” == “profile” ]]; then
将libflutter.so copy到armeabi架构中去
cp -rf ${ARMEABI_V7A_DIR}/libflutter.so ${ARMEABI_DIR}
将libapp.so也copy到armeabi架构中去
cp -rf ${ARMEABI_V7A_DIR}/libapp.so ${ARMEABI_DIR}
echo “copy ${ARMEABI_V7A_DIR}/libflutter.so to ${ARMEABI_DIR}”
echo “copy ${ARMEABI_V7A_DIR}/libapp.so to ${ARMEABI_DIR}”
elif [[ “$1” == “release” ]]; then
将libflutter.so copy到armeabi架构中去
cp -rf ${ARMEABI_V7A_DIR}/libflutter.so ${ARMEABI_DIR}
将libapp.so也copy到armeabi架构中去
cp -rf ${ARMEABI_V7A_DIR}/libapp.so ${ARMEABI_DIR}
echo “copy ${ARMEABI_V7A_DIR}/libflutter.so to ${ARMEABI_DIR}”
echo “copy ${ARMEABI_V7A_DIR}/libapp.so to ${ARMEABI_DIR}”
fi
Hook Gradle Task
afterEvaluate { project ->
android.applicationVariants.each { variant ->
/**
-
由于flutter不支持armeabi,此处在merge(Debug|Profile|Release)NativeLibs与strip(Debug|Profile|Release)DebugSymbols之间插入一个任务,
-
将libflutter.so和libapp.so拷贝到merged_native_libs/(debug|profile/release)/out/lib/armeabi目录下,使它们能打到最终的apk里。
-
详情见copyFlutterSo.sh
*/
def taskPostfix = variant.name.substring(0, 1).toUpperCase() +
variant.name.substring(1)
project.task(“copyFlutterSo$taskPostfix”) {
doLast {
exec {
// 执行shell脚本
commandLine “sh”, “./copyFlutterSo.sh”, variant.name
}
}
}
// 注意这个是在gradle 5.6.2版本的task
// project.tasks[“copyFlutterSo t a s k P o s t f i x " ] . d e p e n d s O n ( p r o j e c t . t a s k s [ " m e r g e taskPostfix"].dependsOn(project.tasks["merge taskPostfix"].dependsOn(project.tasks["mergetaskPostfix” + “NativeLibs”])
// project.tasks[“strip t a s k P o s t f i x " + " D e b u g S y m b o l s " ] . d e p e n d s O n ( p r o j e c t . t a s k s [ " c o p y F l u t t e r S o taskPostfix" + "DebugSymbols"].dependsOn(project.tasks["copyFlutterSo taskPostfix"+"DebugSymbols"].dependsOn(project.tasks["copyFlutterSotaskPostfix”])
//
// gradle 4.10.1,注意插入task的依赖顺序
project.tasks[“copyFlutterSo t a s k P o s t f i x " ] . d e p e n d s O n ( p r o j e c t . t a s k s [ " t r a n s f o r m N a t i v e L i b s W i t h M e r g e J n i L i b s F o r {taskPostfix}"].dependsOn(project.tasks["transformNativeLibsWithMergeJniLibsFor taskPostfix"].dependsOn(project.tasks["transformNativeLibsWithMergeJniLibsFor{taskPostfix}”])
project.tasks[“process t a s k P o s t f i x J a v a R e s " ] . d e p e n d s O n ( p r o j e c t . t a s k s [ " c o p y F l u t t e r S o {taskPostfix}JavaRes"].dependsOn(project.tasks["copyFlutterSo taskPostfixJavaRes"].dependsOn(project.tasks["copyFlutterSotaskPostfix”])
}
}
}
这样我们每次执行assembleDebug 或者assembleRelease都能自动将对应的armeabi-v7a的libflutter.so和libapp.so复制到armeabi下。
然后再run一次,这个时候就真正把我们的混合工程跑起来了。
最后为了帮助大家深刻理解Android相关知识点的原理以及面试相关知识,这里放上相关的我搜集整理的24套腾讯、字节跳动、阿里、百度2020-2021面试真题解析,我把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包知识脉络 + 诸多细节。
还有 高级架构技术进阶脑图、Android开发面试专题资料 帮助大家学习提升进阶,也节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习。



网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门,即可获取!
了帮助大家深刻理解Android相关知识点的原理以及面试相关知识,这里放上相关的我搜集整理的24套腾讯、字节跳动、阿里、百度2020-2021面试真题解析,我把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包知识脉络 + 诸多细节。
还有 高级架构技术进阶脑图、Android开发面试专题资料 帮助大家学习提升进阶,也节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习。
[外链图片转存中…(img-HC0sHqXt-1715233880004)]
[外链图片转存中…(img-jfmZ2FKv-1715233880005)]
[外链图片转存中…(img-AkOtQ0rd-1715233880005)]
网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门,即可获取!
更多推荐

所有评论(0)