先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7
深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年最新Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新
如果你需要这些资料,可以添加V获取:vip204888 (备注Android)
正文
自定义数据存储
Android 8.0 允许您为首选项提供自定义数据存储,如果您的应用将首选项存储在云或本地数据库中,或者如果首选项特定于某个设备,此功能会非常有用。如需了解有关实现数据存储的详细信息,请参阅自定义数据存储。
findViewById() 签名变更
现在,findViewById() 函数的全部实例均返回
- 例如,如果 someMethod(View) 和 someMethod(TextView) 均接受调用 findViewById() 的结果,这可能导致现有代码的返回类型不确定。
- 在使用 Java 8 源语言时,这需要在返回类型不受限制时(例如,assertNotNull(findViewById(…)).someViewMethod()))显式转换为 View。
- 重写非最终的 findViewById() 函数(例如,Activity.findViewById())将需要更新其返回类型。
媒体增强功能
VolumeShaper
有一个新的 VolumeShaper 类。您可以用它来执行简短的自动音量转换,例如淡入、淡出和交叉淡入淡出。
音频焦点增强功能
音频应用通过请求和舍弃音频焦点的方式在设备上共享音频输出。应用通过启动或停止播放或者闪避音量的方式处理处于聚焦状态的变更。有一个新的 AudioFocusRequest 类。对于此类,应用在处理音频焦点变化时会使用新功能:自动闪避和延迟聚焦。
媒体指标
新的 getMetrics() 函数将返回一个包含配置和性能信息的 PersistableBundle 对象,用一个包含属性和值的地图表示。为以下媒体类定义 getMetrics() 函数:
- MediaPlayer.getMetrics()
- MediaRecorder.getMetrics()
- MediaCodec.getMetrics()
- MediaExtractor.getMetrics()
为每个实例单独收集指标,并持续到实例的生命周期结束为止。如果没有可用的指标,则此函数将返回 null。返回的实际指标取决于类。
MediaPlayer
Android 8.0 为 MediaPlayer 类添加了多种新函数。这些函数可以从多个方面增强您的应用处理媒体播放的能力:
- 在搜索帧时进行精细控制。
- 播放受数字版权管理保护的材料的功能。
MediaPlayer 现在支持采样级加密。
音频录制器
- 音频录制器现在支持对流式传输有用的 MPEG2_TS 格式:mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_2_TS);
- MediaMuxer 现在可以处理任意数量的音频和视频流,而不再仅限于一个音频曲目和/或一个视频曲目。使用 addTrack() 可混录所需的任意数量的曲目。
- MediaMuxer 还可以添加一个或多个包含用户定义的每帧信息的元数据曲目。元数据的格式由您的应用定义。仅对 MP4 容器支持元数据曲目。
元数据可以用于离线处理。例如,传感器的陀螺仪信号可以用于执行视频稳定操作。
在添加元数据曲目时,曲目的 MIME 格式必须以前缀“application/”开头。除了数据不是来源于 MediaCodec 以外,写入元数据的操作与写入视频/音频数据相同。相反,应用将包含相关时间戳的 ByteBuffer 传递给 writeSampleData() 函数。时间戳必须和视频及音频曲目处于相同的时基。
生成的 MP4 文件使用 ISOBMFF 的 12.3.3.2 部分定义的 TextMetaDataSampleEntry,指示元数据的 MIME 格式。在使用 MediaExtractor 提取包含元数据曲目的文件时,元数据的 MIME 格式将提取到 MediaFormat 中。
音频播放控制
Android 8.0 允许您查询和请求设备产生声音的方式。对音频播放的以下控制将让您的服务更轻松地仅在有利的设备条件下产生声音。
Google 智能助理的新音频使用类型
AudioAttributes 类包含一种新的声音类型,即 USAGE_ASSISTANT,对应于 Google 智能助理在设备上的回答。
设备音频播放的变更
如果您希望自己的服务仅在特定的设备音频配置处于活动状态时开始产生声音,您可以使用 AudioManager 类注册一个 AudioManager.AudioPlaybackCallback 实例,后者的onPlaybackConfigChanged() 函数可以帮助您确定当前活动的音频属性集。
显式请求音频焦点
您的服务可以使用 requestAudioFocus() 函数提交一个更精细的设备级音频焦点接收请求。传入一个 AudioFocusRequest 对象,您可以使用 AudioFocusRequest.Builder 创建这个对象。在这个构建类中,您可以指定以下选项:
- 您希望获得的焦点类型,例如 AUDIOFOCUS_GAIN_TRANSIENT 或 AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK。
- 当另一个音频服务获得设备焦点时,您的服务应以更安静的方式继续,还是完全暂停。
- 您的服务能否等待获得焦点,直至设备就绪。
注:构建您的 AudioFocusRequest 实例时,如果您通过调用 setAcceptsDelayedFocusGain() 指示您的服务可以等待产生声音,您也必须调用 setOnAudioFocusChangeListener(),以便您的服务了解它何时可以开始产生声音。
增强的媒体文件访问功能
存储访问框架 (SAF) 允许应用显示自定义 DocumentsProvider,后者可以为其他应用提供访问数据源中的文件的权限。事实上,文档提供程序甚至可以提供驻留在网络存储区或使用媒体传输协议 (MTP) 等协议的文件的访问权限。
但是,访问远程数据源中的大媒体文件面临一些挑战:
- 媒体播放器需要以寻址方式访问来自文档提供程序的文件。当大媒体文件驻留在远程数据源上时,文档提供程序必须事先提取所有数据,并创建快照文件描述符。媒体播放器无法播放没有文件描述符的文件,因此在文档提供程序完成文件下载前,无法开始播放。
- 照片应用等媒体集合管理器必须通过作用域文件夹遍历一系列访问 URI 才能访问存储在外部 SD 卡上的媒体。这种访问模式会让媒体上的批量操作(例如移动、复制和删除)变得非常缓慢。
- 媒体集合管理器无法根据文档的 URI 确定其位置。这就让这些类型的应用难以允许用户选择媒体文件的保存位置。
Android 8.0 通过改进存储访问框架解决了各个挑战。
自定义文档提供程序
从 Android 8.0 开始,存储访问框架允许自定义文档提供程序为驻留在远程数据源中的文件创建可寻址的文件描述符。SAF 可打开文件,获取原生可寻址的文件描述符。然后 SAF 向文档提供程序提交离散字节请求。此功能使文档提供程序可以返回媒体播放器应用请求的准确字节范围,而不必事先缓存整个文件。
要使用此功能,您需要调用新的 StorageManager.openProxyFileDescriptor() 函数。openProxyFileDescriptor() 函数可接受 ProxyFileDescriptorCallback 对象作为回调。任何时候,当客户端应用对文档提供程序返回的文件描述符执行文件操作时,SAF 都会调用回调。
直接文档访问
从 Android 8.0 开始,您可以使用 getDocumentUri() 函数获得与给定 mediaUri 引用相同文档的 URI。不过,由于返回的 URI 由 DocumentsProvider 提供支持,媒体集合管理器可以直接访问文档,不用遍历作用域目录树。因此,媒体管理器能够以明显加快的速度对文档执行文件操作。
注意:getDocumentUri() 函数仅可以定位媒体文件;无法授予应用访问这些文件的权限。要详细了解如何获取媒体文件的访问权限,请参阅参考文档。
文档路径
在 Android 8.0 中使用存储访问框架时,您可以根据文档的 ID,使用 findDocumentPath() 函数(存在于 DocumentsContract 和 DocumentsProvider 类中)从文件系统的根目录中确定路径。该函数将在 DocumentsContract.Path 对象中返回此路径。如果文件系统对相同文档有多个定义的路径,该函数将返回访问具有给定 ID 的文档时最常使用的路径。
此功能在下列情况下特别有用:
- 您的应用使用可以显示特定文档位置的“另存为”对话框。
- 您的应用在搜索结果视图中显示文件夹并且如果用户选择某个文件夹,应用必须加载此特定文件夹内的子文档。
注:如果您的应用仅具有路径中某些文档的访问权限,那么 findDocumentPath() 的返回值将仅包含您的应用可以访问的文件夹和文档。
连接
WLAN 感知
Android 8.0 新增了对 WLAN 感知的支持,此技术基于周边感知联网 (NAN) 规范。在具有相应 WLAN 感知硬件的设备上,应用和附近设备可以通过 WLAN 进行搜索和通信,无需依赖互联网接入点。我们正在与硬件合作伙伴合作,以尽快将 WLAN 感知技术应用于设备。要了解有关如何将 WLAN 感知集成到您的应用中的信息,请参阅 WLAN 感知。
蓝牙
Android 8.0 通过增加以下功能,增强了平台对蓝牙的支持:
- 支持 AVRCP 1.4 标准,该标准支持音乐库浏览。
- 支持蓝牙低功耗 (BLE) 5.0 标准。
- 将 Sony LDAC 编解码器集成到蓝牙堆叠中。
配套设备配对
在尝试通过蓝牙、BLE 和 WLAN 与配套设备配对时,Android 8.0 提供的 API 允许您自定义配对请求对话框。如需了解详细信息,请参阅配套设备配对。
如需了解有关在 Android 上使用蓝牙的详细信息,请参阅蓝牙指南。有关对蓝牙所作的特定于 Android 8.0 的变更,请参阅 Android 8.0 行为变更页面的蓝牙部分。
共享
智能共享
Android 8.0 了解用户的个性化分享首选项,在通过哪些应用分享各个类型的内容方面,也有着更好的把握。例如,如果用户为一张收据拍照,Android 8.0 可以建议费用跟踪应用;如果用户自拍,一款社交媒体应用可以更好地处理图像。Android 8.0 可以根据用户的个性化首选项自动学习所有这些模式。
智能分享适用于 image 之外的内容类型,例如 audio、video、text 和 URL 等。
要启用智能分享,请将具有最多三个字符串注释的 ArrayList 添加到分享内容的 intent。这些注释应说明内容中的主要部分或主题。下面的代码示例显示了如何向 intent 添加注释:
`ArrayList annotations = new ArrayList<>();
annotations.add(“topic1”);
annotations.add(“topic2”);
annotations.add(“topic3”);
intent.putStringArrayListExtra(
Intent.EXTRA_CONTENT_ANNOTATIONS,
annotations
);`
智能文本选择
在兼容设备上,Android 8.0 让应用可以帮助用户以更有意义的方式与文本交互。当用户长按某个实体中可识别格式的单词(例如某个地址或餐馆名称)时,系统会选中整个实体。用户会看到一个浮动工具栏,该工具栏包含可以处理所选文本实体的应用。例如,如果系统识别出某个地址,它可以将用户导向地图应用。
系统识别的实体包括地址、网址、电话号码和电子邮件地址。如需了解详细信息,请参阅 TextClassifier。
无障碍功能
ndroid 8.0 支持开发者使用以下无障碍功能创建自己的无障碍服务。如需了解有关如何让您的应用更便于访问的更多信息,请参阅无障碍功能。
无障碍功能按钮
您的无障碍服务现在可以请求在系统的导航区域显示无障碍功能按钮,该按钮让用户可从其设备上的任意位置快速激活您的服务功能。要执行此操作,请在某个 AccessibilityServiceInfo 对象的 android:accessibilityFlags 属性中添加 FLAG_REQUEST_ACCESSIBILITY_BUTTON 标志。稍后,您可以使用 registerAccessibilityButtonCallback() 注册回调。
注:此功能仅适用于提供软件渲染导航区域的设备。请始终使用 isAccessibilityButtonAvailable(),并通过实现 onAvailabilityChanged() 根据无障碍功能按钮的可用性来响应变更。通过该方式,用户可以始终访问您的服务功能,即使该无障碍功能按钮不受支持或变得不可用。
独立的音量调整
Android 8.0 引入了 STREAM_ACCESSIBILITY 音量类别,允许您单独控制无障碍服务音频输出的音量,而不会影响设备上的其他声音。
要使用这个新的流类型来控制无障碍服务音量,请在无障碍服务中设置 FLAG_ENABLE_ACCESSIBILITY_VOLUME 选项。然后,您可以使用 adjustStreamVolume() 更改设备的无障碍服务音频音量。
指纹手势
您的无障碍服务也可以响应替代的输入机制,即沿设备的指纹传感器按特定方向滑动(上、下、左和右)。要接收有关这些交互的回调,请完成以下一系列步骤:
- 声明 USE_FINGERPRINT 权限和 CAPABILITY_CAN_REQUEST_FINGERPRINT_GESTURES 功能。
- 在 android:accessibilityFlags 属性中设置 FLAG_REQUEST_FINGERPRINT_GESTURES 标志。
- 使用 registerFingerprintGestureCallback() 注册回调。
请记住,并非所有设备都包含指纹传感器。您可以使用 isHardwareDetected() 函数识别设备是否支持此传感器。即使对于包含指纹传感器的设备,您的服务也只有在指纹传感器不用于身份验证目的时才可使用它。要识别此传感器何时可用,请调用 isGestureDetectionAvailable() 函数并实现 onGestureDetectionAvailabilityChanged() 回调。
字词级突出显示
要确定 TextView 对象中可见字符的位置,您可以在 EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY 中将其作为第一个参数传递到 refreshWithExtraData() 中。随后会更新您为 refreshWithExtraData() 提供的作为第二个参数的 Bundle 对象,使之包含一个可打包的 Rect 对象数组。每个 Rect 对象代表某个特定字符的边界框。
如果您的服务使用 TextToSpeech 对象朗读屏幕上出现的内容,您可以获取有关文本到语音转换引擎何时开始朗读单个合成字词时的准确时间信息,前提是文本到语音转换引擎提供此信息。当引擎即将开始播放特定范围文本的音频时,Text-to-Speech API 会通知您的服务,将使用 onRangeStart() 函数开始朗读此范围的文本。
如果您创建自己的 TextToSpeechService 实现,您可以使用 rangeStart() 函数支持这一新功能。
标准化单端范围值
AccessibilityNodeInfo 的一些实例使用 AccessibilityNodeInfo.RangeInfo 的某个实例来表明界面元素可接受一定范围的值。使用 RangeInfo.obtain() 创建范围或使用 getMin() 和 getMax() 检索此范围的极值时,请注意,Android 8.0 规定了标准化单端范围:
- 对于没有最小值的范围,Float.NEGATIVE_INFINITY 表示最小值。
- 对于没有最大值的范围,Float.POSITIVE_INFINITY 表示最大值。
提示文本
Android 8.0 包含可用于与文本可编辑对象的提示文本进行交互的多个函数:
- isShowingHintText() 和 setShowingHintText() 函数分别显示和设置节点的当前文本内容是否表示节点的提示文本。如果节点不包含可编辑文本,则它不应包含提示文本。
- 要访问提示文本本身,请使用 getHintText()。即使某个对象当前未显示提示文本,系统也能成功调用 getHintText()。
连续的手势分派
您的服务现在可以使用 GestureDescription.StrokeDescription 构造函数中的最后一个参数 willContinue,指定属于同一设定手势的笔划的顺序。
安全性与隐私
权限
Android 8.0 引入了多个与电话有关的新权限:
- ANSWER_PHONE_CALLS 允许您的应用通过编程方式接听呼入电话。要在您的应用中处理呼入电话,您可以使用 acceptRingingCall() 函数。
- READ_PHONE_NUMBERS 权限允许您的应用读取设备中存储的电话号码。
这些权限均被划分为危险类别,属于 PHONE 权限组。
新的帐号访问和 Discovery API
Android 8.0 对应用访问用户帐号的方式引入多项改进。对于由身份验证器管理的帐号,身份验证器在决定对应用隐藏帐号还是显示帐号时可以使用自己的策略。Android 系统跟踪可以访问特定帐号的应用。
在以前的 Android 版本中,想要跟踪用户帐号列表的应用必须获取有关所有帐号的更新,包括具有不相关类型的帐号。Android 8.0 添加了 addOnAccountsUpdatedListener(android.accounts.OnAccountsUpdateListener, android.os.Handler, boolean, java.lang.String[]) 函数,其允许应用指定应接收帐号变更的帐号类型列表。
API 变更
AccountManager 提供六个新函数以帮助身份验证器管理哪些应用可以查看某个帐号:
- setAccountVisibility(android.accounts.Account, java.lang.String, int):针对特定用户帐号和软件包组合设置可见性级别。
- getAccountVisibility(android.accounts.Account, java.lang.String):获取特定用户帐号和软件包组合的可见性级别。
- getAccountsAndVisibilityForPackage(java.lang.String, java.lang.String):允许身份验证器获取帐号和给定软件包的可见性级别。
- getPackagesAndVisibilityForAccount(android.accounts.Account):允许身份验证器获取存储的给定帐号的可见性值。
- addAccountExplicitly(android.accounts.Account, java.lang.String, android.os.Bundle, java.util.Map
- addOnAccountsUpdatedListener(android.accounts.OnAccountsUpdateListener, android.os.Handler, boolean, java.lang.String[]):将 OnAccountsUpdateListener 侦听器添加到 AccountManager 对象。无论设备上的帐号列表何时发生变化,系统都将调用此侦听器。
Android 8.0 引入两个特殊的软件包名称值,以使用 setAccountVisibility(android.accounts.Account, java.lang.String, int) 函数指定未设置的应用的可见性级别。PACKAGE_NAME_KEY_LEGACY_VISIBLE 可见性值应用于具有 GET_ACCOUNTS 权限的应用,并且其目标 Android 版本低于 Android 8.0,或其签名与针对任意 Android 版本的身份验证器匹配。PACKAGE_NAME_KEY_LEGACY_NOT_VISIBLE 为之前未设置的应用提供默认的可见性值,对于此类应用,PACKAGE_NAME_KEY_LEGACY_VISIBLE 不适用。
Google Safe Browsing API
WebView 类现在添加了一个 Safe Browsing API 来增强网络浏览的安全性。如需了解详细信息,请参阅 Google Safe Browsing API。
测试
仪器测试
Android 8.0 为应用的仪器测试提供以下几项额外支持。
针对非默认应用进程运行
现在,您可以指定针对您的应用的默认进程以外的进程运行特定仪器测试。如果您的应用包含多个在不同进程中运行的操作组件,此配置非常有用。
要定义非默认进程仪器测试,请导航至您的清单文件,然后导航至所需的
- 特定进程的名称。
- 以逗号分隔的进程名称列表。
- 通配符(“*”),允许针对任何执行 android:targetPackage 属性中指定的软件包中的代码的已启动进程运行仪器测试。
在执行仪器测试时,您可以通过调用 getProcessName() 检查正在测试哪个进程。
在测试过程中报告结果
现在,通过调用 addResults(),您可以在执行仪器测试时(而不用等到测试后)报告结果。
用于测试的模拟 Intent
为了更轻松地为您应用的操作组件创建隔离、独立的界面测试,Android 8.0 引入了 onStartActivity() 函数。要处理您的测试类调用的特定 intent,您可以在 Instrumentation.ActivityMonitor 类的自定义子类中替换此函数。
当您的测试类调用 intent 时,该函数将返回一个存根 Instrumentation.ActivityResult 对象,而不是执行 intent 本身。通过在您的测试中使用这种模拟 intent 逻辑,您可以侧重于自己的操作组件如何准备和处理您传递到不同操作组件或完全不同的应用中的 intent。
行时和工具
平台优化
Android 8.0 为平台引入了运行时优化和其他优化,这些优化将带来多项性能改进。这些优化包括并发压缩垃圾回收、更有效的内存利用和代码区域。
它们可以加快启动时间,并为 OS 和应用带来更好的性能。
更新的 Java 支持
Android 8.0 添加了对更多 OpenJDK Java API 的支持:
- OpenJDK 8 中的 java.time。
- OpenJDK 7 中的 java.nio.file 和 java.lang.invoke。
要详细了解这些新添加的软件包中的类和函数,请参阅 API 参考文档。
如果您想要在 Android Studio 中使用 Java 8 语言功能,您应下载最新的预览版本。
更新的 ICU4J Android Framework API
Android 8.0 扩展了 ICU4J Android 框架 API—,它是 ICU4J API 的子集—,供应用开发者在 android.icu 软件包中使用。这些 API 使用设备上具有的本地化数据。因此,您无需在 APK 中编译 ICU4J 库,从而减少 APK 占用空间。
表 1. Android 中使用的 ICU、CLDR 和 Unicode 版本。
Android API 级别 | ICU 版本 | CLDR 版本 | Unicode 版本 |
---|---|---|---|
Android 7.0(API 级别 24) | Android 7.1(API 级别 25) | 56 | 28 |
Android 8.0 | 58.2 | 30.0.3 | 9.0 |
Android 企业版
已为运行 Android 8.0 的设备引入新的企业功能和 API。重要功能包括如下:
- 完全托管的设备中的工作资料使企业可以在管理工作数据与个人数据的同时,将它们分离开来。
- API 委派允许设备所有者和个人资料所有者将应用管理分配给其他应用。
- 配置流程中的用户体验改进措施(包含新的自定义选项)缩短了设置时间。
- 蓝牙、WLAN、备份和安全性方面的新增控制选项使企业可以更精细地管理设备。网络操作组件日志记录可帮助企业追查问题。
如需详细了解上述及其他新增 Android 企业版 API 和功能,请参阅企业中的 Android。
如果你想第一时间看我的后期文章,扫码关注公众号,长期推送Android开发文章、最新动态、开源项目,让你各种涨姿势。
Android开发666 - 安卓开发技术分享 扫描二维码加关注
分类: android
标签: Android8.0功能, Android 8.0API, Android 8.0介绍
好文要顶😉 关注我😉 收藏该文😉 ; “分享至新浪微博”)
; “分享至微信”)
安辉
关注 - 2
粉丝 - 98
+加关注😉
1
0
« 上一篇:Android微信登录、分享、支付
» 下一篇:封装好的图片滑动框架(AndroidImageSlider)
最后
光有这些思路和搞懂单个知识的应用是还远远不够的,在Android开源框架设计思想中的知识点还是比较多的,想要搞懂还得学会整理和规划:我们常见的**Android热修复框架、插件化框架、组件化框架、图片加载框架、网络访问框架、RxJava响应式编程框架、IOC依赖注入框架、最近架构组件Jetpack等等Android第三方开源框架,**这些都是属于Android开源框架设计思想的。如下图所示:
这位阿里P8大佬针对以上知识点,熬夜整理出了一本长达1042页的完整版如何解读开源框架设计思想PDF文档,内容详细,把Android热修复框架、插件化框架、组件化框架、图片加载框架、网络访问框架、RxJava响应式编程框架、IOC依赖注入框架、最近架构组件Jetpack等等Android第三方开源框架这些知识点从源码分析到实战应用都讲的简单明了。
由于文档内容过多,篇幅受限,只能截图展示部分
整理不易,觉得有帮助的朋友可以帮忙点赞分享支持一下小编~
你的支持,我的动力;祝各位前程似锦,offer不断!!!
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
需要这份系统化的资料的朋友,可以添加V获取:vip204888 (备注Android)
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
1713639095744)]
[外链图片转存中…(img-ZZyODeXx-1713639095745)]
整理不易,觉得有帮助的朋友可以帮忙点赞分享支持一下小编~
你的支持,我的动力;祝各位前程似锦,offer不断!!!
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
需要这份系统化的资料的朋友,可以添加V获取:vip204888 (备注Android)
[外链图片转存中…(img-IS6IxG55-1713639095745)]
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!