微店的Flutter混合开发组件化与工程化架构

  1. 最后把命令行传来的参数链接到Flutter SDK中的flutter进行执行

之后构建Flutter工程则用flutterw命令:

./flutterw build bundle

而不用本地全局配置的flutter命令,避免每个开发同学版本不一致问题,且这种方式对于新加入Flutter开发的同学来说,完全不需要自己手动下载Flutter SDK,只需执行一下flutterw任何命令,如./flutterw --version,即可自动触发对应Flutter SDK的下载与安装,实现优雅的自动化管理,这种方式对打包平台来说也为支持Flutter工程的打包提供基础

七、Flutter混合开发组件化架构

上述说的如果我们要利用Flutter来开发我们现有Native工程中的一个模块或功能,肯定得不能改变Native的工程结构以及不影响现有的开发流程,那么,以何种方式进行混合开发呢? 前面说到Flutter的四种工程模型,Flutter App我们可以直接忽略,因为这是一个开发全新的Flutter App工程,对于Flutter Module,官方提供的本地依赖便是使用Flutter Module依赖到Native App的,而对于Flutter工程来说,构建Flutter工程必须得有个main.dart主入口,恰好Flutter Module中也有主入口

于是,我们进行组件划分,通过Flutter Module作为所有通过Flutter实现的模块或功能的聚合入口,通过它进行Flutter层到Native层的双向关联。而Flutter开发代码写在哪里呢?当然可以直接写在Flutter Module中,这没问题,而如果后续开发了多个模块、组件,我们的Dart代码总不可能全部写在Flutter Module中lib/吧,如果在lib/目录下再建立子目录进行模块区分,这不失为一种最简单的方式,不过这会带来一些问题,所有模块共用一个远程Git地址,首先在组件开发隔离上完全耦合了,其次各个模块组件没有单独的版本号或Tag,且后续模块组件的增多,带来更多的测试回归成本

正确的组件化方式为一个组件有一个独立的远程Git地址管理,这样各个组件在发正式版时都有一个版本号和Tag,且在各个组件开发上完全隔离,后续组件的增多不影响其它组件,某个组件新增需求而不需回归其它组件,带来更低的测试成本

前面提到Flutter Plugin可以有对应Dart层代码与平台层的实现,所以可以这样设计,一个组件对应一个Flutter Plugin,一个Flutter Plugin为一个完整的Flutter工程,有独立的Git地址,而这些组件之间不能互相依赖,保持零耦合,所以这些组件都在业务层,可以叫做业务组件,这些业务组件之间的通信和公共服务可以再划分一层基础层,可以叫做基础组件,所有业务组件依赖基础层,而Flutter Module作为聚合层依赖于所有Flutter组件,这些Flutter工程之间的依赖正是通过Pub依赖进行管理的

所以,综合上述,整体的组件化架构可以设计为:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

业务组件与基础组件的定位

对于上面的基础组件比如还可以进行更细粒度的划分,不过不建议划分太多,对于与Native平台层的通信,每个业务组件对应一个Channel,当然内部还可以进行更细粒度的Channel进行划分,这个Channel主要是负责Native层服务的提供,让Flutter层消费。而对于Native层调用Flutter层的Api,应该尽可能少,需要调也只有出现一些值回调时

因为Flutter的出现最本质的就是一次开发两端运行,而如果有太多这种依赖于平台层的实现,反而出现违背了,最后只是UI写了一份而已。对于平台层的实现也要尽量保持一个原则,即:

尽量让Native平台层成为服务层,让Flutter层成为消费层调用Native层的服务,即Dart调用Native的Api,这样当两端开发人员编写好一致基础的服务接口后,Flutter的开发人员即可平滑使用和开发

而对于基础组件中的公共服务组件Dart Api层的设计,因为公共服务主要调用Native层的服务,在Flutter中提供公共的Dart Api,作为Native到Flutter的一个桥梁,对于Native的服务,会有很有多种,而对应Api的设计为一个dart文件对应一个种类的服务,整个公共服务组件提供一个统一个对外暴露的Dart,内部的细粒度的Dart实现通过export导入,这种设计思想正是Flutter官方Api的设计,即统一对外暴露的Dart为common_service.dart

library common_service;

export ‘network_plugin.dart’;
export ‘messager_plugin.dart’;

而上层业务组件调用Api只需要import一个dart即可,这样对上层业务组件开发人员是透明的,上层不需要了解有哪些Api可用:

import ‘package:common_service/common_service.dart’;

八、Flutter混合开发工程化架构

基本组件化的架构我们搭建好了,接下来是如何让Flutter混合开发进行完整的工程化管理,我们都知道,对于官方的本地依赖这种方式,我们不能直接用,因为这会直接影响Native工程、开发流程与打包流程,所以我们得基于官方这种依赖方式进行优化改造,于是我们衍生出两种Flutter链接到Native工程的方式:

  1. 本地依赖(源码依赖)
  2. 远程依赖(产物依赖)

为什么要有这两种方式,首先本地依赖对于打包平台不支持,现有打包平台的环境,只能支持标准的Gradle工程结构进行打包,且本地依赖对于无需开发Flutter相关业务的同学来说是灾难性的,所以便有了远程依赖,远程依赖直接依赖于打包好的Flutter产物,Android通过Gradle依赖,IOS通过Pod远程依赖,这样对其它业务开发同学来说是透明的,他们无需关心Flutter也不需要知道Flutter是否存在

对于这两种依赖模式的使用环境也各不一样

1. 本地依赖 本地依赖主要用于需要进行Flutter开发的同学,通过在对应Native工程中配置文件配置是否打开本地Flutter Module依赖,以及配置链接的本地Flutter Module地址,这样Native工程即可自动依赖到本地的Flutter工程,整个过程是无缝的,同时本地依赖是通过源码进行依赖的,也可以很方便的进行Debug调试 对于Android中配置文件为本地的local.properties,IOS中为本地新建的local.xcconfig,两个平台的配置属性保持一致:

FLUTTER_MODULE_LINK_ENABLE=true
FLUTTER_MODULE_LOCAL_LINK=/Users/Sunzxyong/FlutterProject/flutter_module

2. 远程依赖 远程依赖是把Flutter Module的构成产物发布到远程,然后在Native工程中远程依赖,这种依赖方式是默认的依赖方式,这样对其它开发同学来说是透明的,不影响开发流程和打包平台

上述说到的两种依赖方式,接下来主要说怎么进行这两种依赖方式的工程化管理和定制化

1. 无侵入Flutter SDK源码进行BugFix和定制化

Flutter SDK在使用时,不免会遇到一些Flutter SDK的问题或Bug,但这些问题通常是在各平台层的链接脚本中出现坑,而如果我们要兼容现有工程和扩展定制化功能,往往会直接修改Flutter SDK源码,这种侵入性的方式极不推荐,这对后续SDK的平滑升级会带来更多的成本

通常出现Bug或需要定制化的脚本往往是和平台链接时相关的,当然排除需要修改dart层Api代码的情况下,这种只能更改源码了,不过这种出bug的几率还是比较小的,比较涉及到SDK的Api层面了。而大概率出现问题需要兼容或进行定制化的几个地方通常为下面几处:

  1. $FLUTTER_SDK/packages/flutter_tools/gradle/flutter.gradle
  2. $FLUTTER_SDK/bin/cache/artifacts/engine/android-arch/flutter.jar
  3. $FLUTTER_MODULE/.android/build.gradle、.android/settings.gradle
  4. $FLUTTER_MODULE/.android/Flutter/build.gradle
  5. $FLUTTER_MODULE/.ios/Flutter/Generated.xcconfig
  6. $FLUTTER_MODULE/.ios/Flutter/podhelper.rb
  7. $FLUTTER_MODULE/.ios/Podfile
  8. $FLUTTER_SDK/packages/flutter_tools/bin/xcode_backend.sh

而我们需要兼容的Flutter SDK的问题和定制化的点有下面几项:

  1. Android:Flutter SDK中的Flutter引擎不支持armeabi架构
  2. Android:Flutter SDK中的flutter.gradle链接脚本不支持非app名称的Application工程
  3. Android:Flutter SDK中的flutter.gradle链接脚本本地依赖存在flutter_shared资源文件不拷贝Bug
  4. Android:解决上述几项需要代理build.gradle构建脚本,以及在build.gradle构建脚本中定制化我们的构建产物收集Task
  5. IOS:Flutter Module中自动生成的.ios中的podhelper.rbruby脚本使用了Pod中的post_install方法,导致Native工程不能使用或使用了的发生冲突,间接侵入了Native工程与耦合,限制性太强
  6. IOS:Flutter Module中自动生成的Podfile文件,需要添加我们自己私有的Specs仓库进行定制化
  7. IOS:解决post_install问题后,Flutter SDK中的xcode_backend.sh链接脚本环境变量的读取问题

为了实现无侵入Flutter SDK,对于上述的这些问题的解决,我们使用代理方式进行Bug的修改和定制化,下面是针对两个平台分别的实现策略

1. Android

在Android平台上述问题和定制化的解决策略,对于armeabi架构的支持,我们可以通过脚本进行自动化,上面讲到flutterw的版本自动化管理,同样,我们在里面加段armeabi架构的支持脚本,这样做得好处是后续不需要支持了可以直接移除,通过调用./flutterw armeabi即可自动添加armeabi架构的引擎

对于Flutter SDK中的flutter.gradle链接脚本的问题兼容,不会直接在源码中进行更改,而是把它拷贝出来,命名为flutter_proxy.gradle,然后在代理脚本中进行问题的修复,主要修复点为flutter_shared的支持与app硬编码名称的兼容,如下:

Task copySharedFlutterAssetsTask = project.tasks.create(name: “copySharedFlutterAssetsKaTeX parse error: Expected '}', got 'EOF' at end of input: …d/*' into "src/{variant.name}”
}

再让copyFlutterAssetsTask任务依赖于它,而app硬编码名称的兼容,则更简单了,通过在Native工程中local.properties配置Module名,再在flutter_proxy.gradle脚本中加入读取该属性代码:

String appName = loadRootProjectProperty(project, “FLUTTER_APP_NAME”, “app”)
Task mergeAssets = project.tasks.findByPath(“: a p p N a m e : m e r g e {appName}:merge appName:merge{variant.name.capitalize()}Assets”)

而对于build.gradle构建脚本的代理,我们可以通过在执行Gradle构建时,通过-c命令进行settings.gradle的代理,进而代理掉build.gradle和指定Module中的build.gradle脚本,如下:

cd .android
./gradlew assembleDebug -c …/script/proxy/settings.gradle

而通过代理的settings.gradle文件再进行build.gradle的代理:

getRootProject().buildFileName = ‘build_proxy.gradle’
project(“:flutter”).buildFileName = “build_proxy.gradle”

其中代理的Flutter/build.gradle中的脚本apply会改为修复的Flutter SDK中的脚本代理:

apply from: “${project.projectDir.parentFile.parentFile.absolutePath}/script/proxy/flutter_proxy.gradle”

这样.android工程在构建时期可以完全由我们自主控制,包括加入一些产物收集插件、产物发布到远程插件等定制功能

不过这种方式需要执行构建命令时手动指定代理脚本,对于本地依赖时Native自动构建来说,是不会指定的,所有基于这种方式,我们再优化一下,因为Flutter Module.android.ios工程是通过Flutter SDK内部模版自动生成的,只要执行build|packages get等命令都会自动生成,首先想到是更改Flutter SDK内部工程模版,在Flutter SDK的packages/flutter_tools/templates目录下,不过这与我们无侵入Flutter SDK违背了,所以不能选取这种方式

回想我们的Flutter SDK版本一致性管理是通过flutterw脚本进行自动化的,而最终会链接调用到原生Flutter SDK中的命令,所以,我们可以在flutterw中加入脚本,用于在.android.ios工程生成后,进行内部脚本文件的替换,把build.gradlesettings.gradle脚本内容直接替换为我们的代理脚本的内容,这样既不侵入Flutter SDK,在后续维护起来也方便,后续不需要这个功能了,只需要把这段脚本代码注释就好了,随即又恢复原生的构建脚本了,flutterw脚本执行过程如下:

function main() {

link_flutter “$@”
inject_proxy_build_script

}

inject_proxy_build_script这个Shell函数会把对应脚本进行我们的脚本替换掉,当前函数内部也有对应判断,因为flutterw主要用于Flutter SDK版本一致性管理,这里仅对Flutter Module工程生效。所以这种方式不管是在本地依赖构建下还是通过命令行构建都可以完美支持

2. IOS

在IOS平台上述问题和定制化的解决策略,对于IOS主要是对Podfilepodhelper.rb脚本进行支持,而对Podfile的支持,这个比较简单,在Podfile头部通过脚本注入我们自己私有的Specs仓库即可:

source ‘https://***/XXSpecs.git’
source ‘https://github.com/CocoaPods/Specs.git’
platform :ios, ‘8.0’

这个工作同样在flutterw执行后进行兼容,后续不需要了可以直接注释,这个自动注入脚本也仅对Flutter Module工程生效

podhelper.rb脚本的兼容,主要是在进行本地依赖时,内部已经用了post_install函数,该函是在pod install后执行,这会与Native已经使用了该函数的发生冲突并报错,所以我们通过flutterw脚本的执行后默认注释掉该脚本中的post_install使用处,但是肯定不能平白无故注释掉,我们要了解这段的作用,其实就是设置环境变量,为后续xcode_backend.sh脚本的构建执行做准备,而注释掉怎么用另外一种方式恢复环境变量的设置这个后面再讲,注释后podhelper.rb脚本代码片段为:

post_install do |installer|

installer.pods_project.targets.each do |target|

target.build_configurations.each do |config|

config.build_settings[‘ENABLE_BITCODE’] = ‘NO’

xcconfig_path = config.base_configuration_reference.real_path

File.open(xcconfig_path, ‘a+’) do |file|

file.puts “#include “#{File.realpath(File.join(framework_dir, ‘Generated.xcconfig’))}””

end

end

end

end

最终在flutterw自动支持上述处理脚本执行流程为:

function main() {

link_flutter “$@”

podfile_support
podhelper_support
collect_ios_product “$@”
}

函数内部判断仅针对Flutter Module工程生效,毕竟其它Flutter Plugin工程不需要这种处理

2. 本地依赖无侵入流程

我们要做到只通过一个属性配置文件,在配置文件中通过配置开发来打开或关闭本地的Flutter Module链接依赖,只按官方的依赖方式肯定是不行的,不管是Android还是IOS,都会直接侵入Native工程,影响其它无Flutter环境同学的开发且影响打包平台上的打包。所以,肯定得做优化,我们在官方这种依赖方式中加一层,作为代理层,而代理层主要做的工作是判断本地是否有对应的属性配置文件且属性值是否符合本地依赖Flutter Module的条件,如果是则进行本地Flutter Module的依赖,如果不是则Return掉,默认不做任何处理

所以通过这种代理方式即不影响Native工程原先的开发流程,对其它业务开发同学和打包平台也是透明的

对于代理层的实现,Android与IOS平台各不一样

1. Android

Android是通过一个Gradle脚本进行自动管理的,这个Gradle脚本主要在settings.gradlebuild.gradle中做local.properties配置文件的属性值校验,决定是否开启本地Flutter Module链接的

2. IOS

IOS则较为复杂一些,因为涉及到Podfile中的ruby执行脚本代理与Build Phases时期的Shell脚本代理,所以得写两种类型的代理脚本:Ruby和Shell,代理脚本的最终执行还是会调用被代理的脚本,只是在调用前做一层包装逻辑判断。而IOS中本身没有本地配置文件,所以我们新建一个IOS的本地配置文件为local.xcconfig,这个配置文件不随版本进行管理,会gitignore掉,于是,在IOS中Podfile最终调用的脚本是:

eval(File.read(File.join(‘./’, ‘FlutterSupport’, ‘podhelper_proxy.rb’)), binding)

而在Build Phases调用的是:

chmod +x “ S R C R O O T / F l u t t e r S u p p o r t / x c o d e b a c k e n d p r o x y . s h " " {SRCROOT}/FlutterSupport/xcode_backend_proxy.sh" " SRCROOT/FlutterSupport/xcodebackendproxy.sh""{SRCROOT}/FlutterSupport/xcode_backend_proxy.sh” flutterBuild

而刚刚上面说到的podhelper.rb脚本中post_install函数被注释掉后怎么用另一种方式进行替换,我们知道这段函数主要就是提供在IOS构建阶段时执行xcode_backend.sh的环境变量的,比如会获取FLUTTER_ROOT等属性值,这些环境变量由Flutter Module中Generated.xcconfig来提供,而如果我们把这个文件的内容通过脚本拷贝到IOS工程下对应构建配置的xcconfig中,如debug.xcconfigrelease.xcconfig,这种方式可行,不过会侵入Native工程,导致Native工程中多了这些变量,而且不优雅,我们要做到的是保证无侵入性

既然我们已经通过代理脚本进行代理,那么这些环境变量我们完全可以获取出来,通过Shell脚本的特性,子Shell会继承于父Shell中export的环境变量值,所以,在代理Shell脚本中再加段下面代码:

function export_xcconfig() {
export ENABLE_BITCODE=NO
if [[ $# != 0 ]]; then
local g_xcconfig= 1 / . i o s / F l u t t e r / G e n e r a t e d . x c c o n f i g i f [ [ − f " 1/.ios/Flutter/Generated.xcconfig if [[ -f " 1/.ios/Flutter/Generated.xcconfigif[[f"g_xcconfig" ]]; then

no piping.

while read -r line
do
if [[ ! “ l i n e " =   / / ] ] ; t h e n e x p o r t " line" =~ ^// ]]; then export " line"= //]];thenexport"line”
fi
done < $g_xcconfig
fi
fi
}

其中注意不能使用管道,管道会在另外一个Shell进程

3. 远程依赖产物打包流程

Flutter的远程产物依赖,Android是通过Aar依赖,IOS是通过.a.framework静态库进行依赖,要进行这些远程依赖很简单,关键是如何打包获取这些依赖的产物以及上传到远程,因为按照现有组件化的打包,除了聚合层Flutter Module中有对应的flutter-debug.aarApp.frameworkflutter_assets等产物的生成,其中业务组件和基础组件中,也有对应的打包产物,这些打包产物会对应各自平台打包不同类型产物,Android还是aar,而IOS则是.a静态库了,下面就分别讲下Android与IOS的打包流程

1. Android

Android的打包比较简单,通过在Flutter Module中的.android子工程下执行./gradlew assembleRelease,则会在对应Flutter中Android子工程的build目录下输出对应aar产物,而重点是怎么获取依赖的各组件(Flutter Plugin)中的产物,则是通过.flutter-plugins文件,该文件是在packages get时自动生成的,里面包含了该Flutter工程通过Pub所依赖的库,我们可以解析这个文件,来获取对应依赖库的产物

2. IOS

IOS上的打包相比Android来说更复杂一些,我们借助.ios/Runner来打包出静态库等产物,所以还需要设置签名,通过在Flutter Module中直接执行./flutterw build ios --release,该命令会自动执行pod install,所以我们不必再单独执行它,IOS中构建出的产物获取也相对繁琐些,除了获取Flutter的相关产物,还需要获取所依赖的各组件的静态库以及头文件,需要获取的产物如下:

Flutter.framework App.framework FlutterPluginRegistrant flutter_assets 所有依赖的Plugin的.a静态库以及头文件

其中Flutter.framework为Flutter引擎,类似Android中的flutter.so,而App.framework则是Flutter中Dart编译后的产物(Debug模式下它仅为一个空壳,具体Dart代码在flutter_assets中,Release模式下为编译后的机器指令),FlutterPluginRegistrant是所有插件Channel的注册表,也是自动生成的,flutter_assets含字体等资源,剩下一些.a静态库则是各组件在IOS平台层的实现了

而收集IOS产物除了在.ios/Flutter目录下收集*.framework静态库和flutter_assets外,剩下的就是收集.a静态库以及对应的头文件了,而这些产物则是在构建Runner工程后,在Flutter Module下的

build/ios/$variant-iphoneos

目录下,variant对应所构建变体名,我们还是通过解析.flutter-plugins文件,来获取对应所依赖Flutter插件的名称,进而在上述的输出目录下找到对应的.a静态库,但是对应的头文件而不在对应.a静态库目录下,所以对于头文件单独获取,因为解析了.flutter-plugins获取到了KV键值对,对应的V则是该Flutter插件工程地址,所以头文件我们从里面获取

最后还需要获取FlutterPluginRegistrant注册表的静态库以及头文件

3. 产物收集与传递依赖

对于通过Flutter Module聚合层构建出来的产物,我们进行收集后再聚合到单独的产物输出目录下,当然这一切都是通过脚本自动做掉的

在Android上,通过Gradle插件Hook assembleTask

collectAarTask.dependsOn assembleTask
assembleTask.finalizedBy collectAarTask

这样当执行完./gradlew assemble${variant}命令后则会自动进行产物收集

在IOS上,通过flutterw脚本,在构建完后判断构建命令是否是IOS构建命令,进而自动收集构建后的产物:

function collect_ios_product() {
if [[ $# != 0 && $# > 2 ]]; then
if [[ “$1” = “build” && “$2” = “ios” ]]; then

do collect…

fi
fi
}

对应.a静态库和头文件的收集关键脚本代码如下:

while read -r line
do
if [[ ! “KaTeX parse error: Expected 'EOF', got '&' at position 14: line" =~ ^// &̲& ! "line” =~ ^# ]]; then
array=( l i n e / / = / ) l o c a l l i b r a r y = {line//=/ }) local library= line//=/)locallibrary=product_dir/ a r r a y [ 0 ] / l i b {array[0]}/lib array[0]/lib{array[0]}.a
if [[ -f " l i b r a r y " ] ] ; t h e n l o c a l p l u g i n = library" ]]; then local plugin= library"]];thenlocalplugin=dest_dir/plugins/${array[0]}
rm -rf $plugin
mkdir -p $plugin
cp -f $library p l u g i n l o c a l c l a s s e s = plugin local classes= pluginlocalclasses={array[1]}ios/Classes
for header in find "$classes" -name *.h; do
cp -f $header $plugin
done
else
echo “The static library $library do not exist!”
fi
fi
done < $flutter_plugins

如下是Android与IOS的打包后产物收集后的目录结构如下: 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

对于传递依赖的支持,我们知道单独的aar文件以及通过podspec声明这些静态库产物,是会丢失传递依赖的,丢失传递依赖可能导致我们Native工程中没有使用到的一些三方库,而Flutter工程中引用了,然后App运行Crash,而保证传递依赖的方式,则是Android发布到远程Maven,最后通过远程依赖,上述产物只是本地依赖,IOS则是解析所有Flutter插件中的podspec文件,把它还原为JSON格式,通过解析dependencies对象,获取对应的依赖库命名以及版本号,最后在IOS远程产物的podspec配置文件中添加这些依赖

对于IOS的远程依赖,我们知道单独建一个独立的Git仓库就可以解决,通过配置好podspec,即可在IOS Native端进行远程依赖,但是像Flutter.frameworkApp.framework这种大文件,如果直接上传到Git仓库中有些不太友好,比如可以上传到CDN中,然后通过podspecspec.prepare_command特性,在pod库安装时候预先执行一段脚本把这两个产物拉下来,对于目前来说,可以先传到Git中,这样比较直观与可控,便于版本的管理

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

深知大多数Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

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

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

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

如果你觉得这些内容对你有帮助,可以添加V获取:vip204888 (备注Android)
img

学习宝典

对我们开发者来说,一定要打好基础,随时准备战斗。不论寒冬是否到来,都要把自己的技术做精做深。虽然目前移动端的招聘量确实变少了,但中高端的职位还是很多的,这说明行业只是变得成熟规范起来了。竞争越激烈,产品质量与留存就变得更加重要,我们进入了技术赋能业务的时代。

不论遇到什么困难,都不应该成为我们放弃的理由!

很多人在刚接触这个行业的时候或者是在遇到瓶颈期的时候,总会遇到一些问题,比如学了一段时间感觉没有方向感,不知道该从那里入手去学习,对此我针对Android程序员,我这边给大家整理了一套学习宝典!包括不限于高级UI、性能优化、移动架构师、NDK、混合式开发(ReactNative+Weex)微信小程序、Flutter等全方面的Android进阶实践技术;希望能帮助到大家,也节省大家在网上搜索资料的时间来学习,也可以分享动态给身边好友一起学习!

【Android核心高级技术PDF文档,BAT大厂面试真题解析】

【算法合集】

【延伸Android必备知识点】

【Android部分高级架构视频学习资源】

了一套学习宝典!包括不限于高级UI、性能优化、移动架构师、NDK、混合式开发(ReactNative+Weex)微信小程序、Flutter等全方面的Android进阶实践技术;希望能帮助到大家,也节省大家在网上搜索资料的时间来学习,也可以分享动态给身边好友一起学习!

【Android核心高级技术PDF文档,BAT大厂面试真题解析】

[外链图片转存中…(img-5eQtl3o9-1712002258397)]

【算法合集】

[外链图片转存中…(img-LzkKwkiQ-1712002258398)]

【延伸Android必备知识点】

[外链图片转存中…(img-jxGAKLau-1712002258398)]

【Android部分高级架构视频学习资源】

本文已被CODING开源项目:《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》收录

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值