Flutter学习笔记&学习资料推荐,GitHub标星50k的Android全栈技术知识

其中关于布局约束一节官方总结的很精辟:

在这里插入图片描述

可能英文的这张图片更好理解,说的更加简洁:

在这里插入图片描述

理解Flutter的布局约束原理对于流畅的书写布局十分重要,布局时你可能不会感到像android中那样舒服,因为经常会遇到一些约束边界报错的问题或者溢出问题,官网教程给出了大概的原理可以帮助你理解并解决这些问题。

关于最新版的插件开发方式官网介绍的还不是很详细,补充两篇:Flutter Plugin插件开发填坑指南Flutter 插件的创建及使用

其实最新的主要是通过实现一些接口来做的:

public class MyFlutterPlugin implements FlutterPlugin, ActivityAware, MethodCallHandler {

}

Flutter还支持将原生应用的部分页面替换成flutter, Android中集成教程可以看这里 在 Android 中集成 Flutter

Flutter pub 社区


Flutter 提供了一个发布网站,可以搜索别人开发好的插件:https://pub.dev

一些官方推荐的插件还有一些优秀个人开发的插件都会在这上面能搜索到,如果你想使用某个功能,如获取照片?拍照?像gilde一样的具有缓存功能的网络图片加载库?首先到pub上搜索,将大大减少你的开发时间,当然如果你有好的插件也可以开源发布到pub上给别人使用。

注意:pub上只支持英文package插件名的搜索,搜中文是搜不到的

下图是Flutter提供的官方维护的插件列表:

需要的可以直接按名字到pub上去搜索,一般搜索结果的第一个就是。只看名字你就能猜出来,很多插件基本上是完全模仿的android开发当中的库。其中有一些开发中会高频使用到的,如device_info获取设备信息、camera拍照、image_picker图片选择、shared_preferences类似android的sp存取数据、path_provider文件操作、url_launcher打开外部链接、video_player视频播放、webview_flutter浏览器等。当然官方维护的插件在Github上也提供了源码,需要的话可以直接下载下来修改代码然后本地使用或者作为新版本发布到pub, 官方的插件源码也提供了插件开发方式最好的学习例子。

另外补充一些比较好用的插件:

| 插件名字 | 功能 |

| — | — |

| dio | 超级好用的网络请求库 |

| sqflite | 类似android中的sqflite数据库 |

| fluttertoast | 类似android中的toast功能 |

| wechat_assets_picker | 类似原生的微信图片选择器 |

| flutter_staggered_grid_view | 支持瀑布流的gridview, 比flutter自带的强大太多 |

| provider | 跨组件状态共享的官方推荐库 |

| event_bus | 类似android中的EventBus消息通信开源库 |

| web_socket_channel | websocket库在官方自带的基础上封装的 |

| flutter_webview_plugin | 比官方的功能要好一点的,官方的插件目前还是开发者预览版 |

| json_annotation | json序列化注解,详细使用可以参考《Flutter实战》中的介绍 |

| json_serializable | json序列化工具dev_dependencies库,详细使用可以参考《Flutter实战》中的介绍 |

| | |

| json_to_model | Json文件转Dart类工具dev_dependencies库,《Flutter实战》中json_model 优化版本 |

| intl | 国际化翻译,详细使用可以参考《Flutter实战》中的介绍 |

| intl_translation | 国际化翻译工具dev_dependencies库,详细使用可以参考《Flutter实战》中的介绍 |

| cached_network_image | 一个支持占位图和图片缓存的网络图片加载库 还可以支持图片下载进度 |

| progress_dialog | 轻量级的进度等待弹窗控件 类似android系统默认进度弹窗 |

| camera_camera | 相机拍照录视频插件,比官方的好用一些 |

Flutter 学习实例


以下列举一些都是我在G站上发现的,虽然有些功能不是很完善,但是当做入门实例来学习的都是相当不错的:

(PS:这里吐槽一下 感觉鸿洋的玩android网站要被玩坏的节奏啊,各种版本的玩android: java版的、 kotlin版的、小程序版的。。现在又出了flutter版的,光flutter版的github上就一搜一大堆。。不过他的网站上有开放的API请求接口可以使用,拿来练手的确是最好不过的了,也难怪这么多人拿它来开刀。。。)

更多实例参考:flutter-doawesome-flutter(这个是国外大神维护的)

另外特别推荐两个可以用来当做API控件功能查询使用的:

  • Flutter UI集录指南: FlutterUnit(特点是可以一边看效果一边展开当前效果对应的代码,demo支持手机/桌面/web端,支持搜索,UI也比较漂亮)

  • Flutter 控件大全_老孟(这个就更厉害了,330多个控件每个控件的使用例子他都整理了,支持搜索,感觉有了这个都不需要去官网查API了。。老孟是个狠人。。。)

Flutter 的一些实际开发问题


下面几篇是超级干干干货的Flutter文章,特别实用,介绍了很多实际开发场景中遇到的问题及解决方法:

下面是我在学习过程中遇到的一些问题:

国际化过程中执行命令arb文件生成dart时报错

在Flutter实战教程中,最后一步根据翻译好的arb文件生成dart文件,执行如下命令报错Cannot open file

flutter pub pub run intl_translation:generate_from_arb --output-dir=lib/l10n --no-use-deferred-loading lib/l10n/localization_intl.dart l10n-arb/intl_*.arb

解决方法:文件管理器中进入项目根目录下,在git bash终端执行命令即可,原因是windows下执行不识别*号通配符会报错。

这句命令在首次运行时会在"lib/l10n"目录下生成多个文件,对应多种Locale,这些代码便是最终要使用的dart代码。

Android Studio运行Flutter时无法连接设备

如下图,有时会发现这里找不到设备无法运行:

在这里插入图片描述

解决方法:先关闭AS,然后到flutter sdk的安装目录下, 将\flutter\bin\cache下的lock文件删除,再重启AS。如果还是不行,那么多试几次就可以了。或者有其他的IDE工具连接了你的设备,关闭其他的IDE就可以了。

在这里插入图片描述

Fliutter运行时一直卡在Running Gradle task 'assembleDebug’

这个有时会卡很久,有时卡一会儿就好了,原因是AS正在后台执行下载,此时可以打开任务管理器,查看AS的磁盘和网络使用状况:

在这里插入图片描述

如果你发现这里的速度很高,说明它正在疯狂的下载,一般多等待一会儿就可以了。可是如果你发现这里也没有什么流量,而它一直卡在那里,说明是依赖库无法下载,如果是android端,可以打开项目的android文件夹,然后修改build.gradle文件如下:

在这里插入图片描述

使用阿里云的maven地址就可以,速度会很快,然后在AS的Tools工具菜单中执行Flutter>Flutter Clean,再重新运行即可。如果还是不行,首先到前面的Flutter 环境配置参考url的配置。如果还不行的话,那可能需要在Terminal终端中cd到android目录下,执行下面命令:

gradlew clean

gradlew build

或者可以选中android文件夹右键Flutter->Open Android module in Android Studio可以把android目录当成一个标准的android项目打开重新build即可。最终再重新回到Flutter项目运行。

showSnackBar报错

Unhandled Exception: Scaffold.of() called with a context that does not contain a Scaffold.

解决方法:使用GlobalKey

var _scaffoldkey = GlobalKey();

return Scaffold(

key: _scaffoldkey,

),

onPressed: () {

_scaffoldkey.currentState.showSnackBar(SnackBar(content: Text(‘显示snackBar’)));

}

使用Utf8Decoder时提示参数错误

在Flutter实战教程中遇到代码片段socket.transform(utf8.decoder).join()报参数不匹配的错误,于是找到了stack overflow的这个utf8.decoder not working after latest Flutter Upgrade,原来这只是一个api的兼容性问题,解决方法:

使用StreamTransformer.bind(Stream) 代替 Stream.transform(StreamTransformer).

例:

  • Before: foo.transform(utf8.decoder)...

  • After: utf8.decoder.bind(foo)...

viewport was given unbounded height.width.

一般情况下是需要给一个固定的高度或宽度,可以参考这个, 但是有时可能都不起作用,还是嵌套的布局使用有问题,去参考官网的布局约束原理 | 处理边界问题

从其他应用切回Flutter应用页面无响应

这是我偶尔发现的一个神奇的问题:Flutter应用在切出应用以后再切回到应用,页面没有响应(不响应任何点击事件),测试手机版本:Android 7.1.1 魅族Pro6

本来我愉快的写着demo, 但是突然有一天,当我按下Home返回桌面再返回来的时候,发现什么都不能点击了。。whf。。?

后来我想了下Flutter这么牛逼的团队,应该不会把有如此明显的严重问题的版本发出来使用吧。。但是它确实是发生了,就发生在我的眼前,而且是百分百重现。

这个问题困扰了好久,因为几乎所有的我创建的应用都是这样的,从github上下载的其他人的demo也有类似情况,但是大多数都是正常的。

后来调查发现是跟flutter sdk版本有关系:

| 版本 | 测试结果 |

| — | — |

| flutter sdk 1.17.4 | 切回应用正常 |

| flutter sdk 1.20.1 | 切回应用无响应 |

| flutter sdk 1.20.2 | 切回应用无响应 |

| flutter sdk 1.20.3 | 切回应用正常 |

现在至少可以确定在 >1.17.4 <1.20.3之间的版本是可能会有兼容性问题的。也可能是Flutter团队在最新版本修复了这个不为人知的bug。

获取屏幕的宽高尺寸

double width = MediaQuery.of(context).size.width;

double height = MediaQuery.of(context).size.height;

有时遇到一些边界问题,不得不指定一个固定的宽度高度,这时可以参考屏幕的宽高,尽量按屏幕尺寸的百分比去设置,而不是写死魔法值。

判断当前是哪个平台

import ‘package:flutter/foundation.dart’;

//TargetPlatform目前支持6种平台

if (defaultTargetPlatform == TargetPlatform.android) {

// 是安卓系统,do something

}

// 我们可以通过显式指定debugDefaultTargetPlatformOverride全局变量的值来指定应用平台。比如:

debugDefaultTargetPlatformOverride = TargetPlatform.iOS;

print(defaultTargetPlatform); // 会输出TargetPlatform.iOS

//上面代码即在Android中运行后,Flutter APP就会认为是当前系统是iOS,Material组件库中所有组件交互方式都会和iOS平台对齐,defaultTargetPlatform的值也会变为TargetPlatform.iOS。

RefreshIndicator有时无法下拉刷新

RefreshIndicator组件的子组件listview或gridview的item数量较少时,无法触发下拉刷新操作,解决方法:滚动组件的physice属性设置为AlwaysScrollableScrollPhysics(),总是保持可滚动状态。

release模式下运行app无法连接网络

首先release模式运行的命令:

flutter run --release

运行之后发现请求接口的列表都出不来了,原来是AndroidManifest文件中默认没有添加INTERNET权限,手动添加上即可。奇怪的是,在debug模式下运行,就算不添加默认也是能请求网络的,害我找了好久原因。。

AS中运行除了main.dart以外的dart文件到手机上

Android Studio中运行除了main.dart以外的dart文件(必须包含main函数),Terminal执行命令:

flutter run lib/animated_list.dart

热加载直接在Terminal键入:r 即可

pubspec.yaml中批量添加图片资源

一开始我都是按照规矩这样添加的:

flutter:

uses-material-design: true

To add assets to your application, add an assets section, like this:

assets:

  • images/ic_test.png

  • images/ic_timg.jpg

  • images/timg4.jpg

  • images/avatar.png

后来看了官网的demo,原来这样也可以:

flutter:

uses-material-design: true

assets: [images/]

这样如果我有很多图片就不用一个一个的去添加依赖了,省了很多力气,哈哈

Flutter 代码/性能优化


目前主要学到的几点:

  • 尽量使用无状态的类组件来代替函数式的组件

  • 尽量保持build方法的纯净,减少跟UI无关的逻辑处理

  • 尽量使用const final等修饰符来避免重复创建新的组件

主要理解两个问题

第一个问题:为啥要使用类组件来代替函数组件?

Class Widget VS Function Widget 的口水之战:

  • [Why should I ever use stateless widgets instead of functional widgets?

]( )(为什么我应该使用无状态组件而不是函数组件?) ——来自Flutter的Github源码的一个issue,国外各路神仙们就此展开了激烈辩论。。

两个链接都是出自同一大神Rousselet的回答,他首先指出的是这两者之间的一个显著的不同点,就是:Flutter的framework对函数是无感知的,但是对类是有感知的

也就是说通过类创建的组件最终会作为一个独立的element被挂载在element树上面去,而函数构建方式仅仅是纯粹的插入到了调用的地方,函数内的组件只是作为调用者的组件的一部分而已。使用函数构建组件会比较容易产生bug,这并不是说你使用函数构建组件就一定会有bug, 只不过如果你使用类构建组件的话,则一定能够保证不会面临用函数构建组件时可能会产生的bug。

既然使用函数实际也是可以的,也不一定就会产生bug, 而且使用函数会比使用类少写很多代码,那么为啥还一定要推荐类组件呢,有啥更特别的地方吗?

——因为使用类组件你会发现有如下显著优势:

  • 允许性能优化(使用const构造函数,做到更精细的重建)

  • 确保在两种不同布局之间的切换时能正确地释放资源(函数可能会重用一些先前的状态)

  • 能确保热重载正常工作(函数则可能会中断热重载)

  • 类组件会被显示到devTool的widget inspector检查器当中,便于调试

  • 类组件可以拥有key, 而函数则不行

  • 类组件可以使用context API, 而函数则不行

  • 类组件可以通过重写运算符==, 减少重建次数, 而函数则不行

Github上的那个issue评论了两年多终于被关闭了。。不得不佩服外国人的钻研精神。。但是直到这个issue被关闭的时候,仍然有很多人还是不是很理解,为啥要优先用类,其实官方的说法也只是强烈推荐,但并不是必须的强制规则。也有很多人提出了如下代码:

class Toggler extends StatelessWidget {

final VoidCallback onToggle;

const Toggler({Key key, this.onToggle}) : super(key: key);

@override

Widget build(BuildContext context) {

return Row(

children: [

FlatButton(onPressed: onToggle, child: Text(‘On’)),

FlatButton(onPressed: onToggle, child: Text(‘Off’)),

],

);

}

}

上面方法中有两个FlatButton是一样的使用方式,那么你可能会考虑这样去重构:

//…

Widget _toggleButton(String text) =>

FlatButton(onPressed: onToggle, child: Text(text));

@override

Widget build(BuildContext context) {

return Row(

children: [

_toggleButton(‘On’),

_toggleButton(‘Off’),

],

);

}

按照我的理解,其实这样做是完全可以的,因为这两种方式最终是等价的,包括我们经常会写的构建列表组件会使用map方法:

final List fruits;

//…

@override

Widget build(BuildContext context) {

return Column(

children: _buildChildren(),

);

}

List _buildChildren() {

return fruits.map((f) => _FruitInfo(fruit: f)).toList();

}

这样应该也是完全OK的,也就是说使用箭头函数和inline组件的方式完全没有任何区别。更多的可能还是要考虑实际业务场景,如下面的代码,flutter就很难理解,某些情况下可能会造成不可预期的结果,这时应该去函数化来得到更加安全的保障。

bool condition;

Widget _foo();

Widget _bar();

Widget build(BuildContext context) {

return condition

? _foo()
_bar();

}

为了更好理解再看两组代码

以下两组代码等价:

Widget functionA() => Container()

@override

Widget build() {

return functionA()

}

@override

Widget build() {

return Container();

}

以下两组代码等价:

class ClassA extends StatelessWidget {

@override

Widget build(BuildContext context) {

return Container();

}

}

@override

Widget build() {

return ClassA()

}

@override

Widget build() {

return KeyedSubtree(

key: ObjectKey(ClassA),

child: Container(),

);

}

可以看出两种方式本质上的不同,类的优势明显优于函数。当然如果你还是比较习惯写函数式的组件,Rousselet大神写了一个库functional_widget可以让你以写函数组件的方式来写类组件:

@swidget

Widget foo(BuildContext context, int value) {

return Text(‘$value’);

}

比如,当你写上面的代码时,使用他这个库会生成下面的代码:

class Foo extends StatelessWidget {

const Foo(this.value, {Key key}) : super(key: key);

final int value;

@override

Widget build(BuildContext context) {

return foo(context, value);

}

@override

void debugFillProperties(DiagnosticPropertiesBuilder properties) {

super.debugFillProperties(properties);

properties.add(IntProperty(‘value’, value));

}

}

第二个问题:如何避免没必要的重复构建?

How to deal with unwanted widget build? (如何处理多余的组件构建?)

这个链接同样也是Rousselet的回答,他的意思,简而言之就是build方法是Flutter中刷新界面时会高频调用的方法,所以尽量保持它的纯净。这对提高刷新性能尤为重要,我们需要把跟UI构建不直接相关的处理逻辑全部移出build方法。例如他提到的这部分优化代码就是将Future对象的构造从build中移了出来:

class Example extends StatefulWidget {

@override

_ExampleState createState() => _ExampleState();

}

class _ExampleState extends State {

Future future;

@override

void initState() {

future = Future.value(42);

super.initState();

}

@override

Widget build(BuildContext context) {

return FutureBuilder(

future: future,

builder: (context, snapshot) {

// create some layout here

},

);

}

}

Flutter中实例相同的组件构建时不会被重新构建,利用这一点我们可以想办法缓存部分组件,如:

@override

Widget build(BuildContext context) {

return const DecoratedBox(

decoration: BoxDecoration(),

child: Text(“Hello World”),

);

}

其中const修饰的组件不会每次都被重新创建,使用final关键字也可以做到:

@override

Widget build(BuildContext context) {

final subtree = MyWidget(

child: Text(“Hello World”)

);

return StreamBuilder(

stream: stream,

initialData: “Foo”,

builder: (context, snapshot) {

return Column(

children: [

Text(snapshot.data),

subtree,

],

);

},

);

}

这里final修饰的组件也不会每次都被重新创建,这种模式在动画中被大量使用如AnimatedBuilder。另外,也提到其他解决方法如尽可能拆分到更小的独立Statefull组件中,或者使用Provider库来解决。

build方法在下面的场景下会被调用:

  • 调用 initState 方法之后

  • 调用 didUpdateWidget 方法之后

  • 调用 setState()方法时

  • 键盘被打开时

  • 屏幕方向发生旋转时

  • 父组件被构建子组件也会重新构建

自定义组件的一个重要点就是在didUpdateWidget中根据新旧组件的状态值比较决定是否要重新构建UI:

@override

void didUpdateWidget(MyRichText oldWidget) {

if (widget.text != oldWidget.text) {

_textSpan = parseText(widget.text);

}

super.didUpdateWidget(oldWidget);

}

又如:

@override

void didUpdateWidget(CounterWidget oldWidget) {

//通过新旧widget的一些属性来判断是否变化

// 检查新旧child是否发生变化(key和类型同时相等则返回true,认为没变化)

if (Widget.canUpdate(widget.child, oldWidget.child)) {

// child没变化,…

} else {

super.didUpdateWidget(oldWidget);

}

}

Flutter 解决嵌套地狱的几种方案


开头介绍过Flutter的不好的地方,第一个就是嵌套层级过多的问题,

第一种方法,将复杂的widget抽取变量,或者抽取类组件

详细的戳这篇文章:Flutter避免代码嵌套,写好build方法

这篇文章的主要思想是不断的复用抽取的控件变量来减少嵌套层级,包括利用控件变量结合if条件渲染, 以及将组件抽取到类当中(Stateful或Stateless)。注意这里提到的是抽取变量,而不是抽取函数,详细前面优化部分已经介绍过了。

其实简单来说,这种方法做的事情就是两个字:重构!不借助任何外力或者依赖多余的库,重新设计和组织你的代码,以得到更加清晰的代码架构和逻辑,并且能够减少bug。我觉得这个已经不算是什么新鲜的姿势了,而是作为一只优秀程序猿的基本素养。

总的来说这种方法是比较原生态的,我个人也比较推荐这种。

第二种方法,通过Dart 2.7的新属性扩展函数来解决

主要看这篇文章:https://blog.csdn.net/c6E5UlI1N/article/details/104057737

虽然这种方式也能解决嵌套问题,但是最终使用起来你会发现它的思想都是反着去添加的,跟日常使用习惯部分,有点反人类。。所以我个人不推荐这种方式。

第三种方法,利用建造者模式来解决

这个可以看这里:flutter解决布局嵌套问题

设计模式是用来优化代码和重构的终极法宝,很多时候你想不到的答案可能先人已经总结了方法。按照build模式我们可以将常用的布局组件都封装一下使用,不限于文中介绍的。只是提供了一种思路。

第四种方法,将组件属性封装为方法来解决

这种其实是由上面第三种建造者模式得到的启发,但是这里我不用建造者模式,因为建造者模式到最后一步build才会生成对象实例,而我想先生成对象实例,然后再调用实例的方法来为其添加属性。最终想要的效果如下:

import ‘package:flutter/material.dart’;

import ‘column_row_wrap.dart’;

class UnNestLayoutTestPage extends StatelessWidget {

@override

Widget build(BuildContext context) {

return Scaffold(

appBar: AppBar(title: Text(“去除嵌套示例”),),

body: ColumnWidget()

.addChild(Text(“呵呵”))

.addChild(Text(“怎样”))

.addChildByPadding(Text(“可以”), EdgeInsets.all(10.0))

.addChild(CenterWidget(Text(“测试”)))

.addChild(RowWidget()

.addChild(Text(“你好”))

.addChildByMargin(Text(“很好”), EdgeInsets.all(20.0))

.addChild(Text(“大家好”)))

.addChild(ContainerWidget(Text(“中国”))

.addPadding(EdgeInsets.all(10.0))

.addDecoration(BoxDecoration(color: Colors.red)))

.addChild(RaisedButtonWrap(“提交”)

.textColor(Colors.blue)

.onClick(() => { print(“点击了提交”)}))

);

}

}

可以看到,在build方法中我用了20行不到的方法添加了很多控件,并且可以轻松的设置颜色、padding、margin、点击事件等,如果要用Flutter原生的写法实现上面的代码远远不止20行,因为每一行要扩展出来好几行,代码量要暴增好几倍。

很显然,这样的代码,肯定是更加清晰且易于维护的。那怎么实现的呢,没有很神秘的东西,直接上代码:

import ‘package:flutter/material.dart’;

class ColumnWidget extends StatelessWidget {

List _children = [];

ColumnWidget addChild(Widget widget) {

_children.add(widget);

return this;

}

ColumnWidget addChildByPadding(Widget widget, EdgeInsetsGeometry padding) {

_children.add(Padding(

child: widget,

padding: padding,

));

return this;

}

@override

Widget build(BuildContext context) {

return Column(

children: _children,

);

}

}

class RowWidget extends StatelessWidget {

List _children = [];

RowWidget addChild(Widget widget) {

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

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

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

img

img

img

img

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

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

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)

尾声

评论里面有些同学有疑问关于如何学习material design控件,我的建议是去GitHub搜,有很多同行给的例子,这些栗子足够入门。

有朋友说要是动真格的话,需要NDK以及JVM等的知识,首现**NDK并不是神秘的东西,**你跟着官方的步骤走一遍就知道什么回事了,无非就是一些代码格式以及原生/JAVA内存交互,进阶一点的有原生/JAVA线程交互,线程交互确实有点蛋疼,但平常避免用就好了,再说对于初学者来说关心NDK干嘛,据鄙人以前的经历,只在音视频通信和一个嵌入式信号处理(离线)的两个项目中用过,嵌入式信号处理是JAVA->NDK->.SO->MATLAB这样调用的我原来MATLAB的代码,其他的大多就用在游戏上了吧,一般的互联网公司会有人给你公司的SO包的。
至于JVM,该掌握的那部分,相信我,你会掌握的,不该你掌握的,有那些专门研究JVM的人来做,不如省省心有空看看计算机系统,编译原理。

一句话,平常多写多练,这是最基本的程序员的素质,尽量挤时间,读理论基础书籍,JVM不是未来30年唯一的虚拟机,JAVA也不一定再风靡未来30年工业界,其他的系统和语言也会雨后春笋冒出来,但你理论扎实会让你很快理解学会一个语言或者框架,你平常写的多会让你很快熟练的将新学的东西应用到实际中。
初学者,一句话,多练。

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!**

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

[外链图片转存中…(img-ZZYBMh0z-1712513076652)]

[外链图片转存中…(img-X6RCG0gw-1712513076653)]

[外链图片转存中…(img-GMxlOUfq-1712513076653)]

[外链图片转存中…(img-sYBXWseL-1712513076653)]

[外链图片转存中…(img-cDacMj8K-1712513076654)]

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

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

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)

尾声

评论里面有些同学有疑问关于如何学习material design控件,我的建议是去GitHub搜,有很多同行给的例子,这些栗子足够入门。

有朋友说要是动真格的话,需要NDK以及JVM等的知识,首现**NDK并不是神秘的东西,**你跟着官方的步骤走一遍就知道什么回事了,无非就是一些代码格式以及原生/JAVA内存交互,进阶一点的有原生/JAVA线程交互,线程交互确实有点蛋疼,但平常避免用就好了,再说对于初学者来说关心NDK干嘛,据鄙人以前的经历,只在音视频通信和一个嵌入式信号处理(离线)的两个项目中用过,嵌入式信号处理是JAVA->NDK->.SO->MATLAB这样调用的我原来MATLAB的代码,其他的大多就用在游戏上了吧,一般的互联网公司会有人给你公司的SO包的。
至于JVM,该掌握的那部分,相信我,你会掌握的,不该你掌握的,有那些专门研究JVM的人来做,不如省省心有空看看计算机系统,编译原理。

一句话,平常多写多练,这是最基本的程序员的素质,尽量挤时间,读理论基础书籍,JVM不是未来30年唯一的虚拟机,JAVA也不一定再风靡未来30年工业界,其他的系统和语言也会雨后春笋冒出来,但你理论扎实会让你很快理解学会一个语言或者框架,你平常写的多会让你很快熟练的将新学的东西应用到实际中。
初学者,一句话,多练。

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值