原生工程接入Flutter实现混编(1)

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,有两种解决办法:

  1. 重命名module名字,命名为app

  2. 修改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页面


上一个主题我们解决掉一些坑之后终于把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-v7alibflutter.solibapp.so复制到armeabi下。

然后再run一次,这个时候就真正把我们的混合工程跑起来了。

工程最佳实践

最后

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助

因此我收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点!不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

因此我收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

[外链图片转存中…(img-Lz6McWg9-1715862076375)]

[外链图片转存中…(img-bH3DSB1K-1715862076376)]

[外链图片转存中…(img-hAGQA16y-1715862076377)]

[外链图片转存中…(img-BK9bL1kh-1715862076378)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点!不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

  • 16
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值