https://avatars.githubusercontent.com/u/22124534?v=4

Watson Wang

OnClickListener2048

HTTP vs. HTTPS:深入理解差异与 HTTPS 全流程

前言

当我们在浏览器地址栏输入网址时,通常会看到 http://https:// 开头。虽然只差一个 “S”,但它们代表着截然不同的网络通信方式,尤其在安全性方面。理解 HTTP 与 HTTPS 的区别以及 HTTPS 的工作原理,对于 Web 开发者和关心网络安全的用户都至关重要。

什么是 HTTP?

HTTP (Hypertext Transfer Protocol),即超文本传输协议,是用于从万维网(WWW)服务器传输超文本到本地浏览器的传送协议。它是互联网上应用最为广泛的一种网络协议,所有的 WWW 文件都必须遵守这个标准。

HTTP 的主要特点:

  1. 无连接: 限制每次连接只处理一个请求。服务器处理完客户的请求,并收到客户的应答后,即断开连接(现代 HTTP 版本如 HTTP/1.1 支持持久连接 Keep-Alive)。
  2. 无状态: 协议对于事务处理没有记忆能力。每个请求都是独立的,服务器不知道客户端之前的请求历史。这通常需要 Cookie 或 Session 等机制来维持状态。
  3. 明文传输: 这是 HTTP 最致命的缺点。所有传输的数据(包括用户名、密码、银行卡信息等)都是未加密的,在传输过程中容易被窃听、截取和篡改。

什么是 HTTPS?

HTTPS (Hypertext Transfer Protocol Secure),即安全超文本传输协议。简单来说,它就是 HTTP 的安全版本。HTTPS 在 HTTP 的基础上加入了 SSL/TLS 协议,依靠 SSL/TLS 来加密数据包、验证服务器身份和保证数据完整性。

HTTPS 的核心优势:

  1. 数据加密: 通信内容通过对称加密和非对称加密技术进行加密,即使被截获,攻击者也无法轻易解密获取真实内容。
  2. 身份认证: 通过数字证书验证服务器的真实身份,防止用户访问到仿冒的钓鱼网站。
  3. 数据完整性: 通过消息认证码(MAC)校验数据在传输过程中是否被篡改。

HTTP 与 HTTPS 的主要区别

特性HTTPHTTPS
安全性明文传输,不安全使用 SSL/TLS 加密,安全
协议层应用层协议HTTP + SSL/TLS 协议(在传输层和应用层之间)
URL 前缀http://https://
默认端口80443
证书不需要需要 CA 颁发的 SSL/TLS 证书
连接过程简单,TCP 三次握手后直接传输 HTTP 报文TCP 三次握手后,还需要进行 SSL/TLS 握手
性能性能开销小建立连接(握手)有一定性能开销,但传输过程影响不大
SEO不利于 SEO对 SEO 友好,被搜索引擎(如 Google)推荐

HTTPS 的网络全流程详解

HTTPS 的通信过程比 HTTP 复杂,主要增加了 SSL/TLS 握手 阶段。以下是简化但关键的步骤:

Android冷启动优化

[toc]

1、冷启动时间检测

通过adb命令来检测应用冷启动时间

adb shell am start -W package/Activity路径

运行结果如下所示

  • TotalTime:应用的启动时间,包括创建进程+Application初始化+Activity初始化到界面显示。
  • WaitTime:一般比TotalTime大点,是AMS启动Activity的总耗时。
  • Android 5.0以下没有WaitTime,所以我们只需要关注TotalTime即可。
Starting: Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] cmp=***/***.SplashActivity }
Warning: Activity not started, intent has been delivered to currently running top-most instance.
Status: ok
LaunchState: UNKNOWN (0)
Activity: ***/***.MainActivity
TotalTime: 785
WaitTime: 787
Complete

2、冷启动时间消耗在哪

2.1、MultiDex耗时

apk的编译过程

  • 1、打包资源文件,生成R.java文件(使用工具AAPT)
  • 2、处理AIDL文件,生成java代码(没有AIDL则忽略)
  • 3、编译 java 文件,生成对应.class文件(java compiler)
  • 4、.class 文件转换成dex文件(dex)
  • 5、打包成没有签名的apk(使用工具apkbuilder)
  • 6、使用签名工具给apk签名(使用工具Jarsigner)
  • 7、对签名后的.apk文件进行对齐处理,不进行对齐处理不能发布到Google Market(使用工具zipalign)

为什么需要使用MultiDex

在apk编译流程的第4步,将class文件转换成dex文件,默认只会生成一个dex文件,单个dex文件中的方法数不能超过65536,不然编译会报错:

理解 Flutter 的三棵树

Flutter 的核心架构依赖于三棵紧密协作的树:Widget 树 (Widget Tree)Element 树 (Element Tree)RenderObject 树 (RenderObject Tree)。它们共同构成了 Flutter UI 的声明、管理和渲染机制,是 Flutter 高性能渲染的关键。

下面我分别解释一下它们的作用:

1. Widget 树 (Widget Tree) - UI 的蓝图 📜

  • 作用: 这是我们开发者 最常接触 的树。它由我们编写的 Widget 对象构成,完全是 UI 界面在特定状态下的 配置信息描述蓝图
  • 特点:
    • 不可变性 (Immutable): Widget 本身是不可变的。每次 UI 需要更新时(例如调用 setState),Flutter 会重新构建 Widget 树(或其一部分)。
    • 轻量级: Widget 只是配置数据,创建和销毁它们的成本相对较低。
  • 类比: 就像建筑的 设计图纸,它描述了建筑应该是什么样子,用了什么材料(配置),但它不是建筑本身。

2. Element 树 (Element Tree) - 连接者与状态管理者 🔗🧠

  • 作用: 这是连接 Widget 树和 RenderObject 树的 关键桥梁。它持有 UI 的 实际结构可变状态
  • 特点:
    • 可变性 (Mutable): 与 Widget 不同,Element 是可变的,它在 Widget 树多次重建之间 保持稳定
    • 生命周期管理: 每个 Widget 在树中的特定位置都有一个对应的 ElementElement 负责管理 WidgetRenderObject 的生命周期(挂载、更新、卸载)。
    • 更新决策: 当 Widget 树重建时,Element 会比较新旧 Widget (canUpdate 方法)。如果可以更新(例如类型和 Key 相同),它会更新自己的配置并指示 RenderObject 更新;否则,会创建新的 ElementRenderObject。这种机制是 Flutter 高效更新 的核心,避免了不必要的重建。
    • 状态持有: 对于 StatefulWidget,对应的 StatefulElement 会持有 State 对象。
  • 类比: 像是建筑工地的 项目经理施工队长。他拿着设计图纸 (Widget),知道当前的施工状态 (State),并指导工人 (RenderObject) 如何建造或修改建筑。他会尽量复用现有的结构 (Element/RenderObject),而不是每次都推倒重来。

3. RenderObject 树 (RenderObject Tree) - 渲染的执行者 🖌️📐

  • 作用: 这是 真正负责 UI 布局和绘制 的树。它包含了 UI 元素的几何信息(尺寸、位置)和绘制逻辑。
  • 特点:
    • 重量级: RenderObject 包含了复杂的布局和绘制计算逻辑,创建和操作它们的成本较高。
    • 核心渲染: RenderObject 负责执行 layout(计算大小和位置)、paint(在画布上绘制)和 hit testing(处理用户交互事件的命中判断)等底层操作。
  • 类比: 就像是建筑的 实际物理结构 以及负责 粉刷、装修的工人。他们负责具体的测量、定位、砌墙、刷漆等实际工作,最终呈现出可见的建筑。

三者如何协作?

  1. 构建: 开发者编写 Widget 构成 Widget 树 (蓝图)。
  2. 实例化与关联: Flutter 框架根据 Widget 树创建对应的 Element 树 (项目经理团队)。每个 Element 会创建并持有一个 RenderObject (或复用),形成 RenderObject 树 (实际施工)。
  3. 更新: 当 setState 被调用时:
    • 新的 Widget 树 (新蓝图) 被创建。
    • Element 树被遍历,每个 Element 对比新旧 Widget
    • 如果 Widget 可以更新 (类型和 Key 相同),Element 会更新其持有的 RenderObject 的属性 (项目经理指导工人微调)。
    • 如果 Widget 不能更新,旧 Element 会被卸载,新的 ElementRenderObject 会被创建 (项目经理决定拆除部分旧结构,按新图纸建新的)。
    • Element 树通过这种方式 最大限度地复用 RenderObject,减少了昂贵的 RenderObject 创建、布局和绘制操作,从而实现了高性能。

总结来说:

面试官:请描述一下Android的打包流程

面试官您好,Android 的打包流程是将我们的项目代码、资源文件、依赖库等最终转换成用户可以在设备上安装和运行的 APK (Android Package) 文件或 AAB (Android App Bundle) 文件的过程。现在这个过程高度依赖于构建工具 GradleAndroid Gradle 插件 (AGP) 来自动化执行。

其核心步骤大致可以分为以下几个阶段:

1. 资源编译 (Compiling Resources)

  • 工具: AAPT2 (Android Asset Packaging Tool 2)
  • 输入: 项目中的资源文件(res 目录下的 layouts, drawables, strings, styles, AndroidManifest.xml 等)以及库依赖中的资源。
  • 过程:
    • 编译 XML 文件为二进制格式。
    • 处理资源依赖,进行资源合并(例如,处理不同 build typeflavor 的资源覆盖)。
    • 生成 R.java (或 R.kt) 文件,这个文件包含了所有资源的 ID 引用,供 Java/Kotlin 代码使用。
    • 生成一个已编译的、扁平化的资源文件包(通常是一个 .ap_ 文件)。
    • 处理 AndroidManifest.xml 文件,进行占位符替换、权限合并等。
  • 输出: 编译后的资源文件(二进制格式)、R.java/R.kt 文件、处理后的 AndroidManifest.xml

2. 源代码编译 (Compiling Source Code)

  • 工具: javac (Java Compiler) 和 kotlinc (Kotlin Compiler)
  • 输入: 项目的 java/kotlin 源代码(包括开发者编写的代码和上一步生成的 R 文件)、库依赖的源代码或 .jar / .aar 文件中的类。
  • 过程: 将所有的 Java 和 Kotlin 源代码编译成 Java 字节码 (.class 文件)。
  • 输出: .class 文件。

3. Dexing (转换为 Dalvik/ART 字节码)

  • 工具: D8 (现代的 Dex 编译器,取代了旧的 DX)
  • 输入: 上一步生成的 .class 文件(包括项目代码和所有库依赖的 .class 文件)。
  • 过程:
    • 将 Java 字节码 (.class) 转换成 Android Runtime (ART) 或旧版 Dalvik 虚拟机可以理解的 .dex (Dalvik Executable) 文件。
    • 这个过程可能包含 Desugaring(语法糖脱糖),即将 Java 8+ 或 Kotlin 的一些新语言特性转换为能在旧版 Android 系统上运行的等效字节码。
    • 可能会进行代码优化。
  • 输出: 一个或多个 .dex 文件。对于较大的应用,可能会启用 MultiDex。

4. 代码和资源合并打包 (Merging)

  • 工具: Gradle (通过 AGP 驱动)
  • 输入:
    • 编译后的资源文件 (.ap_ 文件或类似产物)。
    • .dex 文件。
    • assets 目录下的原始文件。
    • jniLibs 目录下的原生库 (.so 文件)。
    • Java 资源文件 (如果存在)。
    • 处理后的 AndroidManifest.xml
    • 依赖库中的资源、代码和原生库。
  • 过程: 将上述所有内容按照 Android 包的标准结构打包到一个未签名APK 文件或 AAB 文件的基础模块中。
  • 输出: 未签名的 APK 文件或未签名的基础 AAB 模块。

5. 签名 (Signing)

  • 工具: apksigner
  • 输入: 未签名的 APK 文件或 AAB 文件,以及一个签名密钥库 (Keystore)。
  • 过程: 使用私钥对包文件进行数字签名。
    • Debug 构建: 通常使用 SDK 自动生成的 debug keystore。
    • Release 构建: 必须使用开发者自己管理的 release keystore。
  • 目的:
    • 验证应用来源: 确保应用是由密钥持有者发布的。
    • 保证应用完整性: 确保应用在签名后没有被篡改。
    • 实现安全更新: 只有使用相同密钥签名的更新才能覆盖安装。
  • 输出: 已签名的 APKAAB 文件。

6. (可选,主要针对 APK) 对齐 (Aligning)

  • 工具: zipalign
  • 输入: 已签名的 APK 文件。
  • 过程: 优化 APK 文件结构,将未压缩的数据(如资源文件)在文件内按照 4 字节边界对齐。
  • 目的: 提高运行时性能。Android 系统可以通过 mmap 直接内存映射访问 APK 中对齐的资源,减少了 RAM 消耗和加载时间。
  • 关键点: zipalign 必须在签名之后 执行。如果在签名之前执行,签名过程会破坏对齐。
  • 对于 AAB: 对齐通常由 Google Play 在根据用户设备生成最终分发 APK 时处理。
  • 输出: 对齐后的、最终可发布的 APK 文件。

总结与补充

  • 自动化: 整个流程由 GradleAndroid Gradle Plugin (AGP) 编排和自动化,开发者通过配置 build.gradle 文件来控制构建过程(如依赖管理、编译选项、签名配置、ProGuard/R8 混淆优化等)。
  • ProGuard/R8: 在 Release 构建中,通常还会在 Dexing 之前或期间加入 ProGuardR8 进行代码混淆、优化和缩减(移除无用代码和资源),以减小包体积和提高安全性。R8 是现在 AGP 默认的工具。
  • Android App Bundle (AAB): 这是目前推荐的发布格式。打包流程类似,但最终产物是 AAB 文件。AAB 将应用代码和资源拆分成模块,Google Play 会根据用户设备的配置(如屏幕密度、CPU 架构、语言)动态生成并分发最优化的 APK,从而减小用户下载体积。签名机制略有不同(涉及 Google Play App Signing)。

总而言之,Android 打包是一个涉及资源处理、代码编译、格式转换、合并、签名和优化的复杂过程,而 Gradle 和 AGP 极大地简化了开发者的操作。

面试官:让你设计一个图片加载框架,你会怎么做?

设计一个图片加载框架是一个复杂但非常有价值的挑战,因为它涉及到性能、内存管理、用户体验等多个方面。如果让我来设计,我会从以下几个核心模块和设计原则出发:

核心目标

首先,明确框架的核心目标:

  1. 高效加载: 快速地从不同来源(网络、本地、资源文件等)加载图片。
  2. 内存优化: 避免 OOM (Out of Memory) 错误,智能地管理 Bitmap 内存。
  3. 流畅体验: 加载过程不阻塞 UI 线程,提供占位符、加载失败图等。
  4. 可扩展性: 易于添加新的图片来源、缓存策略、图片变换等。
  5. 易用性: 提供简洁、链式调用的 API 供开发者使用。

架构设计 - 分层与模块化

我会采用分层和模块化的设计思路,将框架拆分为几个主要部分:

1. 请求层 (Request Layer) / API

  • 职责: 提供给开发者使用的入口。负责接收加载请求(URL/URI/Resource ID)、目标 View (ImageView 等)、以及各种配置选项(占位符、错误图、变换、缓存策略等)。
  • 设计:
    • 采用链式调用 (Fluent Interface),例如 ImageLoader.with(context).load(url).placeholder(R.drawable.placeholder).error(R.drawable.error).into(imageView);
    • 使用 RequestBuilder 模式来构建复杂的加载请求。
    • 持有 Context,但需要注意内存泄漏,通常使用 ApplicationContext 或与组件生命周期绑定。

2. 调度层 (Dispatcher Layer)

  • 职责: 管理和调度图片加载任务。判断图片是否在缓存中,决定是从缓存加载还是启动新的加载任务,管理线程池。
  • 设计:
    • 维护一个任务队列。
    • 线程池 (ExecutorService): 使用固定大小或可缓存的线程池来执行耗时的网络请求和磁盘 I/O 操作。区分网络线程池和磁盘线程池可能更有利于优化。
    • 任务优先级: (可选) 支持设置请求优先级。
    • 请求合并: (可选) 对于同一个资源、相同参数的并发请求,可以合并,避免重复加载。

3. 缓存层 (Cache Layer)

这是图片加载框架的核心优化点,通常包含两级或三级缓存: