【Android进阶】Android热门依赖库知识点总结

【Android进阶】Android热门依赖库知识点总结

本文介绍了在Android应用层开发过程中,日常使用的重要的依赖库知识点总结梳理

官方组件

ViewModel & Lifecycle

onSaveInstanceState 存储数据原理

onSaveInstanceState 是 Android 中用于临时保存 Activity 或 Fragment 状态的重要机制,主要用于应对系统配置变更(如屏幕旋转)或系统资源回收等情况。

  • 当 Activity 可能被销毁时(如配置变更或后台回收),系统会调用 onSaveInstanceState(Bundle outState)
  • 开发者可以重写此方法,将需要保存的状态数据存入提供的 Bundle 对象
  • 当 Activity 重新创建时,系统会将保存的 Bundle 传递给 onCreate(Bundle savedInstanceState) 或 onRestoreInstanceState(Bundle savedInstanceState)
  • 内部使用 ArrayMap 实现键值存储,支持基本数据类型、String、Parcelable 和 Serializable 对象,数据会被序列化为字节流
  • 系统使用 Binder 事务缓冲区传输这些数据(大小限制约1MB)
  • 数据临时保存在系统进程中,不是持久化存储,进程终止后数据会丢失
  • onSaveInstanceState 被调用的频率可能增加

ViewModel 存数据的原理

ViewModel 是 Android Jetpack 架构组件的一部分,它能在配置变更(如屏幕旋转)时保留数据,但在应用进程被完全终止时数据会丢失。

  • 每个 Activity/Fragment 拥有一个 ViewModelStore 实例
  • ViewModelStore 内部使用 HashMap 存储 ViewModel 实例
  • 配置变更时,ViewModelStore 被保留在内存中
  • ViewModel 的生命周期比创建它的 Activity/Fragment 更长
  • 当 Activity 因配置变更销毁重建时,ViewModel 不会被清除,当 Activity 真正完成(finish())时,ViewModel 会被清除

底层实现原理 ViewModelProvider 工作机制:

// 获取 ViewModel 的简化流程
ViewModelProvider(owner).get(MyViewModel::class.java)

// 内部实现关键步骤:
1. 检查 ownerActivity/Fragment ViewModelStore
2. 如果不存在对应 ViewModel 实例则通过 Factory 创建新实例
3. 将新实例存入 ViewModelStore  HashMap

配置变更可以保存数据原理

配置变更场景:

  • 当 Activity 因配置变更被销毁时,系统保留了一个特殊的”非配置实例”
  • 这个非配置实例持有 ViewModelStore
  • 新创建的 Activity 实例会获取前一个实例的 ViewModelStore

ViewModeProvider 的作用

  • 负责实例化 ViewModel 对象
  • 确保不会重复创建相同的 ViewModel 实例
  • 将 ViewModel 与特定的 Activity/Fragment 生命周期关联
  • 确保 ViewModel 在配置变更时不会被销毁
  • 与 ViewModelStore 协作管理 ViewModel 实例的存储

ViewModel 的工厂模式(ViewModelProvider.Factory)有什么作用

ViewModelProvider.Factory 是 ViewModelProvider 的工厂接口,用于创建 ViewModel 实例。

  • 当 ViewModelProvider 找不到已存在的 ViewModel 实例时,会调用 Factory 的 create 方法创建新实例
  • 可以通过实现 Factory 接口,根据不同的构造参数创建不同的 ViewModel 实例
  • 可以通过依赖注入框架(如 Dagger、Hilt)来提供 Factory 的实现,实现 ViewModel 的依赖注入
  • 可以通过自定义的 ViewModelFactory 来实现不同的 ViewModel 实例创建逻辑,如根据不同的参数创建不同的 ViewModel 实例

Lifecycle 工作机制

Lifecycle 是 Android Jetpack 架构组件的一部分,用于管理 Activity 和 Fragment 的生命周期。

  • 每个 Activity/Fragment 都有一个 LifecycleRegistry 实例
  • LifecycleRegistry 内部维护了一个 LifecycleOwner 和 Lifecycle 的对应关系
  • 当 Activity/Fragment 生命周期状态改变时,LifecycleRegistry 会通知所有的 LifecycleObserver
  • LifecycleObserver 可以通过注解或手动注册来监听特定的生命周期事件

底层实现原理 LifecycleRegistry 工作机制:

// 生命周期状态变化时的简化流程
1. 调用 LifecycleRegistry.markState(Lifecycle.State.CREATED)
2. 遍历所有 LifecycleObserver调用其对应的生命周期方法 onStart()

// 内部实现关键步骤:
1. 维护一个 LifecycleOwner  Lifecycle 的对应关系
2.  LifecycleOwner 生命周期状态改变时遍历所有 LifecycleObserver调用其对应的生命周期方法

ViewModel如何感知生命周期

ViewModel 的生命周期感知主要依赖于以下组件协作:

  • ViewModelStoreOwner:Activity/Fragment 实现的接口,提供 ViewModelStore
  • ViewModelStore:实际存储 ViewModel 实例的容器
  • LifecycleOwner:Activity/Fragment 提供的生命周期来源

Fragment共享viewmodel

Fragment 间共享数据, 实际使用中,可以通过以下方式来实现:

// 多个 Fragment 通过 activity 获取同一个 ViewModel
val sharedViewModel = ViewModelProvider(requireActivity()).get(SharedViewModel::class.java)

LiveData & Flow

什么是 LiveData?它的主要特点是什么?

  • LiveData 是一种可观察的数据持有者类,具有生命周期感知能力
  • 特点:生命周期感知、自动更新UI、避免内存泄漏、数据始终保持最新状态

LiveData 与 RxJava/Observable 有什么区别?

  • LiveData 是生命周期感知的,专为 Android 设计
  • RxJava 更强大但更复杂,需要手动管理订阅生命周期
  • LiveData 自动处理订阅和取消订阅

LiveData 的生命周期感知是如何实现的?

  • 通过 LifecycleOwner 关联组件生命周期
  • 在 STARTED 或 RESUMED 状态时激活观察者
  • 在 DESTROYED 状态时自动移除观察者

LiveData 的观察者模式是如何工作的?

  • 使用 Observer 接口注册观察者
  • 数据变化时通知处于活跃状态的观察者
  • 新观察者注册时会立即收到当前值

LiveData 的 setValue() 和 postValue() 有什么区别?

  • setValue() 必须在主线程调用
  • postValue() 可以在后台线程调用,内部会切换到主线程

LiveData 如何保证数据不丢失?

  • 新观察者注册时会立即收到最后一次的数据
  • 配置更改时不会丢失数据(与 ViewModel 配合)

如何合并多个 LiveData 源?

  • 使用 MediatorLiveData 可以观察多个 LiveData 源
  • 将多个源添加为 MediatorLiveData 的源

在 Repository 层应该返回 LiveData 吗?

  • 不建议,Repository 应保持框架无关性
  • 建议返回 suspend 函数或 Flow,由 ViewModel 转换为 LiveData

LiveData 在 View 和 ViewModel 之间如何分工?

  • ViewModel 暴露 LiveData
  • View(Activity/Fragment) 观察 LiveData 并更新UI
  • 业务逻辑应放在 ViewModel 中

LiveData 与数据绑定(Data Binding)如何配合?

  • 直接在布局文件中绑定 LiveData
  • 需要设置生命周期所有者 binding.lifecycleOwner = this

LiveData 与 StateFlow/SharedFlow 如何选择?

  • LiveData 适合 Android UI 层,简单场景
  • StateFlow/SharedFlow 适合复杂数据流,跨平台逻辑层
  • LiveData 自动生命周期感知,Flow 需要手动收集

Room

DataStore & SharedPreferences & MMKV

SP

通过xml文件存储。 优点:

  • Android 原生支持,无需额外依赖
  • 简单易用,API 直观
  • 适合存储少量简单数据(键值对)

缺点:

  • 同步API,可能导致主线程阻塞
  • 不支持跨进程
  • 没有类型安全
  • 性能较差,特别是大数据量时
  • 不支持异常处理,可能丢失数据
  • 全量写入,即使只修改一个值也要重写整个文件

DataStore

Google推荐的替代SharedPreferences的方案。

  • 异步API(基于Kotlin协程/Flow)
  • 类型安全(通过Protobuf)
  • 更好的错误处理机制
  • 不会阻塞UI线程
  • 支持数据一致性保证

MMKV

腾讯开源的高性能KV存储框架。

  • 基于内存映射,读写速度快
  • 支持多种加密方式
  • 支持多进程
  • 自动增量更新,效率高
  • 支持加密
  • 支持多种数据类型
  • 微信团队开发,经过大规模验证
内存映射技术

内存映射技术是一种将文件内容映射到内存中的技术。

  • 内存映射允许应用程序直接读写文件内容,而无需通过传统的文件读写操作
  • 内存映射减少了文件读写的系统调用次数,提高了读写效率
  • 内存映射将文件内容映射到内存中,减少了数据的拷贝操作,提高了数据访问速度

Lottie

  • Lottie 通过 Android 的 Canvas API 逐帧绘制矢量图形
  • 利用 View 的硬件加速层(通过 setLayerType(LAYER_TYPE_HARDWARE, null))提升性能
  • ValueAnimator:核心动画引擎,根据时间插值计算动画进度
  • JSONObject/JSONArray:解析 After Effects 导出的动画 JSON 文件
  • 对包含位图资源的动画,使用 BitmapFactory 解码
  • 在后台线程(通过 HandlerThread)解析动画 JSON 避免主线程阻塞,通过 Handler 将渲染结果同步到 UI 线程
  • 使用 LruCache 缓存常用动画,避免重复解析
  • 对非活跃动画采用弱引用策略减少内存占用

ViewBinding & DataBinding

ViewBinding 是 Android 官方提供的一种类型安全的视图绑定机制,它通过在编译时生成绑定类来替代传统的 findViewById() 方法。

Android Gradle 插件会:

  • 解析布局XML文件:处理所有包含 merge 或根标签的布局文件
  • 生成绑定类:为每个布局文件生成对应的绑定类(如 ActivityMainBinding)
  • 优化代码:移除未使用的绑定引用

运行时工作流程:

  • 调用生成的 inflate() 方法
  • 内部使用 LayoutInflater 加载布局
  • 调用 bind() 方法进行视图绑定
  • 对布局中的每个带有ID的视图执行一次 findViewById()
  • 将找到的视图引用保存在绑定类的字段中
  • 返回绑定类实例

优点:

  • 类型安全:绑定类中包含所有视图的强类型引用
  • 内部有缓存机制,避免重复查找
  • 避免了 findViewById() 的调用,提高了性能
  • 简化了代码编写,减少了错误

缺点:

  • 编译时生成绑定类,可能会增加 APK 大小
  • 对于复杂布局,可能会导致生成的绑定类过于庞大

DataBinding 是 Android 官方提供的数据绑定框架,它允许开发者以声明式方式将布局与数据源绑定,自动同步 UI 和数据变化。

编译过程会执行以下操作:

  • 布局文件预处理,解析所有包含 layout 标签的 XML 文件.
  • 将普通布局转换为 DataBinding 专用格式(添加绑定桥梁)
  • 生成 BR 类(类似 R 类,用于绑定资源)
  • 为每个绑定布局生成对应的绑定类(如 ActivityMainBindingImpl)
  • 生成 ViewDataBinding 的子类
  • 实现观察者模式和绑定逻辑

双向绑定实现:

<EditText android:text="@={user.name}"/>
  • 设置文本变化监听器
  • 数据变化时更新UI
  • UI变化时更新数据源

ViewPager2

Gson & Moshi & KotlinX Serialization

Proguard

Jetpack Compose

三方组件

RecyclerView

用途

  • 大数据集合展示: RecyclerView适用于展示大量数据,通过ViewHolder的复用机制减少内存消耗。
  • 复杂布局: 支持不同的LayoutManager,可以实现线性、网格、瀑布流等多种复杂布局。
  • 滑动性能优化: 通过异步加载和局部刷新等手段,提升滑动的流畅度。

核心组件

  • Recycler:是构建自定义的布局管理器的核心帮助类,几乎干了所有的获取视图、缓存视图等和回收相关的活动。能让RecyclerView能快速的获取一个新视图来填充数据或者快速丢弃不再需要的视图。
  • Adapter:是所有数据的来源,负责提供数据并创建ViewHolders以及将数据绑定到ViewHolders上的重要组件,可以视为是recycler对外的工作的对接者
  • ViewHolder:存取状态信息,在recycler内部也对viewHolder进行了状态信息的存取(是否正在被改变,是否被删除或添加)
  • LayoutManager:决定RecyclerView中Items的排列方式,包含了Item View的获取与回收;当数据集发生变化时,LayoutManager会自动调整子view的位置和大小,以保证RecyclerView的正常显示和性能。

RecyclerView的View缓存机制

  • ViewHolder模式: RecyclerView使用ViewHolder模式来缓存视图。当ItemView滑出屏幕时,对应的ViewHolder会被缓存,而不是立即销毁。当需要新的ItemView时,可以从缓存中获取ViewHolder,避免频繁的View创建和销毁。
  • Recycler池: RecyclerView通过Recycler池来管理缓存的ViewHolder。Recycler池中维护了一个可回收的ViewHolder队列,通过这个池来快速获取可重用的ViewHolder。
  • 复用机制: 当新的数据需要显示时,RecyclerView会调用Adapter的onBindViewHolder方法,将新的数据绑定到已存在的ViewHolder上,而不是创建新的View。
  • Scrap缓存: 在RecyclerView内部还有一个Scrap缓存,用于存储一些没有被完全废弃的ItemViews。这个缓存用于快速重用视图,减少了ViewHolder的创建和初始化时间。

Retrofit + Okhttp

Retrofit的设计模式

动态代理机制

核心流程:

public <T> T create(final Class<T> service) {
    return (T) Proxy.newProxyInstance(
        service.getClassLoader(),
        new Class<?>[] { service },
        new InvocationHandler() {
            @Override public Object invoke(Object proxy, Method method, Object[] args) {
                // 将接口方法转换为HTTP请求
                return loadServiceMethod(method).invoke(args);
            }
        });
}
  • 通过Java动态代理技术生成API接口的实现
  • 方法调用时,将注解配置转换为HTTP请求参数
  • 最终通过OkHttp执行实际网络请求

Java的动态代理是一种在运行时动态创建代理类和代理对象的能力。它不需要在编译时就知道具体的代理类,而是通过反射机制在程序运行过程中生成代理类和它的实例。

动态代理的优点

(1)解耦: 将横切关注点(如日志、事务、权限等)从业务逻辑中分离出来,提高了代码的内聚性和可维护性。(2)灵活性: 可以在运行时动态地为对象添加功能,无需修改原有代码。(3)复用性: 相同的代理逻辑可以应用于不同的接口和类。(4)AOP(面向切面编程)的基础: 许多AOP框架(如Spring AOP)底层都使用了动态代理来实现切面功能。

建造者模式 (Builder Pattern)

应用场景:Retrofit 实例的配置和创建

典型实现:

Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://api.example.com/")
    .addConverterFactory(GsonConverterFactory.create())
    .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
    .client(okHttpClient)
    .build();

优势:

  • 支持链式调用,配置灵活
  • 隔离复杂对象的创建过程
  • 保证构建过程的一致性

适配器模式 (Adapter Pattern)

应用场景:Call 到其他类型的转换

核心组件:

CallAdapter.Factory:适配器工厂基类

RxJavaCallAdapterFactory:将 Call 适配为 RxJava 的 Observable

CoroutineCallAdapterFactory:将 Call 适配为 Kotlin 协程的 suspend 函数

工作流程:

OkHttpCall --> CallAdapter.adapt() --> Observable/SuspendFunction/其他类型

工厂方法模式 (Factory Method Pattern)

应用场景:Converter 和 CallAdapter 的创建

实现示例:

public interface Converter.Factory {
    // 根据类型创建转换器
    Converter<ResponseBody, ?> responseBodyConverter(
        Type type, Annotation[] annotations, Retrofit retrofit);
    
    // 根据类型创建请求体转换器
    Converter<?, RequestBody> requestBodyConverter(
        Type type, Annotation[] annotations, Retrofit retrofit);
}

派生工厂:

GsonConverterFactory
MoshiConverterFactory
ScalarsConverterFactory

装饰者模式 (Decorator Pattern)

应用场景:OkHttpCall 的封装

实现方式:

final class OkHttpCall<T> implements Call<T> {
    private final ServiceMethod<T, ?> serviceMethod;
    private final @Nullable Object[] args;
    
    // 装饰原始的OkHttp Call
    private @Nullable okhttp3.Call rawCall;
}

作用:

  • 在不改变原有 Call 接口的情况下增强功能
  • 添加了缓存、线程切换等附加功能

策略模式 (Strategy Pattern)

应用场景:HTTP 请求方法的处理

实现体现:

  • 每个 HTTP 注解(@GET/@POST 等)对应不同的请求策略
  • RequestBuilder 根据注解选择不同的参数处理方式

示例:

void parseMethodAnnotation(Annotation annotation) {
    if (annotation instanceof GET) {
        parseHttpMethodAndPath("GET", ((GET) annotation).value());
    } else if (annotation instanceof POST) {
        parseHttpMethodAndPath("POST", ((POST) annotation).value());
    }
    // 其他HTTP方法...
}

观察者模式 (Observer Pattern)

应用场景:通过 Callback 处理异步响应

典型实现:

call.enqueue(new Callback<User>() {
    @Override public void onResponse(Call<User> call, Response<User> response) {
        // 成功回调
    }
    
    @Override public void onFailure(Call<User> call, Throwable t) {
        // 失败回调
    }
});

责任链模式 (Chain of Responsibility)

应用场景:OkHttp 的拦截器体系

Retrofit 中的集成:

OkHttpClient client = new OkHttpClient.Builder()
    .addInterceptor(new LoggingInterceptor())
    .addNetworkInterceptor(new StethoInterceptor())
    .build();

处理流程:

请求 → 拦截器1 → 拦截器2 → ... → 拦截器N → 服务器
响应 ← 拦截器N ← ... ← 拦截器2 ← 拦截器1 ← 

设计模式综合应用示例

// 建造者模式创建Retrofit实例
Retrofit retrofit = new Retrofit.Builder()
    .baseUrl(BASE_URL)
    .client(new OkHttpClient.Builder() // 装饰者模式增强OkHttpClient
        .addInterceptor(new HttpLoggingInterceptor()) // 责任链模式
        .build())
    .addConverterFactory(new GsonConverterFactory()) // 工厂方法模式
    .build();

// 动态代理模式创建API实例
GitHubService service = retrofit.create(GitHubService.class);

// 适配器模式将Call转换为RxJava Observable
Observable<List<Repo>> observable = service.listRepos("user");

// 观察者模式处理响应
observable.subscribe(new Observer<List<Repo>>() {
    @Override public void onSubscribe(Disposable d) {}
    
    @Override public void onNext(List<Repo> repos) {
        // 处理数据
    }
    
    @Override public void onError(Throwable e) {
        // 错误处理
    }
});

Retrofit 通过精心组合这些经典设计模式,实现了简单易用与强大功能之间的完美平衡,成为 Android 网络请求的事实标准。理解这些设计模式有助于开发者更好地使用和扩展 Retrofit。

OKhttp的优势

  • 拦截器机制: OkHttp通过拦截器链来处理请求和响应。每个拦截器可以在请求或响应被处理前或后进行自定义操作。
  • 连接池: OkHttp使用连接池来管理HTTP连接,减少了创建和销毁连接的开销。
  • 缓存机制: OkHttp支持缓存机制,通过缓存策略来控制是否使用缓存。
  • 异步请求: OkHttp支持异步请求,通过回调或协程来处理请求结果。
  • 支持HTTPS: OkHttp支持HTTPS协议,通过SSL/TLS加密来保护数据安全。
  • 支持GZIP压缩: OkHttp支持GZIP压缩,通过压缩请求和响应数据来减少网络传输的数据量。

HTTP 和 HTTPS 的区别?HTTPS 的加密流程?

Ktor

Ktor 是 JetBrains 推出的异步网络框架,支持全栈开发(客户端+服务端),基于 Kotlin 协程设计,100% Kotlin 原生支持。而且随着KMP技术发展,Ktor也已经支持跨平台网络请求。

核心特性:

  • 协程优先:所有API设计为挂起函数
  • 轻量级:模块化设计,按需引入组件
  • 多平台支持:Android、iOS、JVM、JavaScript、Native
  • 插件化架构:通过安装(install)功能扩展能力

和Retrofit对比

  • 协程支持:Ktor 支持 Kotlin 协程,简化异步编程
  • 功能丰富:Ktor 提供了更多的功能,如文件上传、WebSocket、服务器端渲染等
  • 跨平台支持:Ktor 支持跨平台开发,如 Android、iOS、JVM、JavaScript、Native
  • 社区活跃:Ktor 社区活跃,有丰富的文档和示例

CIO引擎

对比OkHttp:

特性CIO 引擎OkHttp 引擎
实现语言100% KotlinJava + Kotlin
线程模型协程事件循环线程池 + NIO
内存占用更低 (~1/3 of OkHttp)较高
HTTP/2支持需要手动启用默认支持
WebSocket基础支持更成熟实现
平台支持全平台 (包括Native)仅JVM/Android
DNS解析纯Kotlin实现依赖系统实现

Hilt & Dagger & Koin 依赖导入

依赖注入

依赖注入是一种设计模式,用于将对象的依赖从内部移到外部进行管理。它的主要好处包括:

解耦代码:通过接口或抽象类注入依赖,降低模块之间的耦合度。 提升测试性:可以使用 Mock 对象注入,方便单元测试。 易于维护:通过集中管理依赖关系,减少手动创建对象的复杂性。

三者简介

  • Dagger是一个用于管理依赖注入的框架,它通过编译时生成代码来实现依赖注入。
  • Hilt是一个用于简化Android依赖注入的框架,它基于Dagger,并提供了一些额外的功能,如简化依赖注入的配置。
  • Koin是一个轻量级的依赖注入框架,它支持Kotlin语言,并提供了一些额外的功能,如简化依赖注入的配置。

写法举例

Dagger

@Module
class NetworkModule {
    @Provides
    fun provideOkHttp(): OkHttpClient = OkHttpClient.Builder().build()
}

@Component(modules = [NetworkModule::class])
interface AppComponent {
    fun inject(activity: MainActivity)
}

// 在Application中初始化
class MyApp : Application() {
    val appComponent = DaggerAppComponent.create()
}

Hilt

@InstallIn(SingletonComponent::class)
object NetworkModule {
    @Provides
    @Singleton
    fun provideOkHttp(): OkHttpClient = OkHttpClient.Builder().build()
}

@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
    @Inject lateinit var okHttpClient: OkHttpClient
}

// Application类需添加注解
@HiltAndroidApp
class MyApp : Application()

Koin

val appModule = module {
    single { OkHttpClient.Builder().build() }
}

class MyApp : Application() {
    override fun onCreate() {
        super.onCreate()
        startKoin {
            androidContext(this@MyApp)
            modules(appModule)
        }
    }
}

class MainActivity : AppCompatActivity() {
    private val okHttpClient: OkHttpClient by inject()
}

对比表格

概念Dagger 2HiltKoin
工作原理编译时代码生成基于Dagger的预配置Android方案运行时依赖解析
配置方式手动创建Component/Module注解驱动+预定义Android组件DSL声明式配置
编译时间影响显著(需kapt处理)显著(继承自Dagger)
运行时性能最佳(编译期解决依赖)同Dagger轻微开销(运行时解析)

Glide & Coil

RxJava

EventBus

LeakCanary

LeakCanary 是 Square 公司开发的一款 Android 内存泄漏检测工具,它能够自动检测应用中的内存泄漏并生成详细的泄漏轨迹报告。

  • LeakCanary 基于 Java 的 WeakReference(弱引用) 和 ReferenceQueue(引用队列) 机制

工作原理流程图:

graph TD
    A[监控对象] --> B[创建KeyedWeakReference]
    B --> C[添加到引用队列]
    D[触发GC] --> E[检查引用队列]
    E -->|对象仍在队列外| F[判定为泄漏]
    E -->|对象进入队列| G[判定为已回收]

LeakCanary 默认监控以下Android组件:

  • 销毁的Activity
  • 销毁的Fragment
  • 销毁的View
  • 销毁的ViewModel

泄漏检测触发时机

  • Activity/Fragment销毁时:通过注册 Application.ActivityLifecycleCallbacks
  • View/ViewModel销毁时:通过各自的销毁回调
  • 手动监控:AppWatcher.objectWatcher.watch(myObject)

泄漏分析过程

  • 当检测到可能的内存泄漏时,调用 Debug.dumpHprofData() 生成堆转储文件
  • 使用 Shark 解析器分析堆转储(替代旧版HAHA)
  • 查找泄漏对象的引用链
  • 识别最短强引用路径(从GC Roots到泄漏对象)
  • 排除系统类引用(过滤噪声)

核心优化技术

  1. 性能优化措施
    • 延迟检测:默认5秒后执行检测(等待主线程空闲)
    • 采样分析:在Debug版本中全量分析,Release版本抽样
    • 后台线程:所有分析操作在独立线程执行
  2. 准确性保障
    • 多次GC验证:确保对象真的无法被回收
    • 引用链验证:确认泄漏路径的有效性
    • 排除软/弱引用:只关注强引用泄漏

常见泄漏场景处理:

  • 单例持有Context:改用Application Context
  • Handler内存泄漏:使用静态Handler+WeakReference
  • 匿名内部类持有:改为静态内部类

LeakCanary 通过巧妙的弱引用监控和堆转储分析技术,为Android开发者提供了简单高效的内存泄漏检测方案,极大提升了内存问题排查效率。

Kotlin

lateinit 和 by lazy 的区别

lateinit(延迟初始化)

  • 用于可变的 var 属性
  • 必须显式初始化后才能访问
  • 不能用于基本数据类型(Int, Boolean等)
  • 编译时不检查初始化状态,运行时检查

by lazy(延迟加载)

  • 用于不可变的 val 属性
  • 首次访问时自动初始化
  • 可以配置属性,自定义初始化逻辑
  • 可以用于基本数据类型

实现原理

lateinit

  • 编译时生成额外代码
  • 获取时,自动检查初始化状态
  • 生成一个访问器方法,用于获取属性

by lazy

  • 使用委托模式
  • 内部维护初始化状态和值
  • 首次访问时,生成一个访问器方法
  • 该方法内部使用 synchronized 确保线程安全
  • 可以配置为非线程安全模式,提升速度

协程

见更全面的总结文章: Kotlin协程浅谈

apply & with & let & run

Kotlin 提供了几个强大的作用域函数(scope functions),它们都用于在对象的上下文中执行代码块,但在使用方式和场景上有所不同。

函数上下文对象引用返回值是否扩展函数典型使用场景
letitLambda 结果非空对象处理、链式操作
runthisLambda 结果对象配置、计算返回值
withthisLambda 结果对象分组操作
applythis对象本身对象初始化
alsoit对象本身附加效果、日志记录

let

// 安全调用非空对象
val length = nullableString?.let { 
    println("Processing: $it")
    it.length  // 返回值
} ?: 0

// 链式操作
user?.let { it.copy(name = "NewName") }?.let(::print)
  • 通过 it 引用对象
  • 返回 lambda 表达式结果
  • 常用于空安全检查

run

val result = service.run {
    port = 8080  // 直接访问属性
    query()      // 直接调用方法
    // 最后一行作为返回值
}

// 替代构造器模式
val rectangle = Rectangle().run {
    width = 10
    height = 20
    area()  // 返回面积
}
  • 通过 this 引用对象(可省略)
  • 返回 lambda 表达式结果
  • 适合对象转换和计算

with

val output = with(StringBuilder()) {
    append("Hello")
    append(" ")
    append("World")
    toString()  // 返回结果
}
  • 非扩展函数
  • 通过 this 引用对象
  • 返回 lambda 表达式结果
  • 适合对同一对象进行多次操作

apply

val myView = TextView(context).apply {
    text = "Hello"
    textSize = 16f
    setPadding(10, 0, 10, 0)
    // 返回TextView对象本身
}

// 替代Builder模式
val intent = Intent().apply {
    action = "ACTION_VIEW"
    data = Uri.parse("https://kotlinlang.org")
}
  • 通过 this 引用对象
  • 返回对象本身
  • 主要用于对象初始化

also

val numbers = mutableListOf(1, 2, 3).also {
    println("Before adding: $it")
    it.add(4)
}.also {
    println("After adding: $it")
}
  • 通过 it 引用对象
  • 返回对象本身
  • 适合执行附加操作

数据类

Kotlin编译时移动生成了hashcode equals toString copy等方法。

底层实现:编译器生成的数据类大致如下:

public final class User {
    private final String name;
    private final int age;

    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        User user = (User) o;
        return age == user.age && name.equals(user.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }

    @Override
    public String toString() {
        return "User{name='" + name + "', age=" + age + "}";
    }

    public User copy(String name, int age) {
        return new User(name, age);
    }

    public String component1() {
        return name;
    }

    public int component2() {
        return age;
    }
}
  • 附加功能:数据类支持解构声明,可以将对象的属性直接解构到多个变量中。

扩展函数原理

Kotlin 允许你为现有类添加新功能,而不需要继承这个类或使用装饰者模式。通过扩展函数,你可以直接在类外部为它添加新方法。 示例:

fun String.hello() = "Hello, $this"
println("World".hello())  // 输出: Hello, World

解析:

  • 简化的语法:扩展函数可以让你对现有类添加自定义功能,而无需修改类的定义。比如上例中的hello()函数,并不是String类的内置方法,但你可以像调用内置方法一样使用它。
  • 底层实现:扩展函数在编译时被转换为静态方法,类的实例作为第一个参数传递。
public static String hello(String receiver) {
     return "Hello, " + receiver;
}

Lambda

  • 简化的语法:Lambda 表达式是一种简洁的函数表示形式,可以用更少的代码来表达相同的逻辑。它在 Kotlin 中被广泛用于集合操作、异步编程等场景。
  • 底层实现:Lambda 表达式在编译时被转换为匿名函数类的实例。Kotlin 提供了一组函数接口,如 Function1、Function2,分别表示接受 1 个参数、2 个参数的函数。
    Function2<Integer, Integer, Integer> sum = new Function2<Integer, Integer, Integer>() {
      @Override
      public Integer invoke(Integer a, Integer b) {
          return a + b;
      }
    };
    int result = sum.invoke(1, 2);
    
  • 捕获变量:如果 Lambda 表达式在定义时捕获了外部变量,那么编译器会生成一个闭包类来封装这些变量。

When关键字

  • 简化的语法:when 表达式比传统的 switch 语句更强大,它不仅可以匹配值,还可以匹配条件,甚至使用复杂的表达式。此外,它也可以返回一个结果。
  • 底层实现:编译器会将 when 表达式转换为一系列条件检查(if-else 语句),或者在可能的情况下转换为 Java 字节码中的 switch 语句。
val result = when (x) {
    1 -> "One"
    2 -> "Two"
    else -> "Unknown"
}

String result;
if (x == 1) {
    result = "One";
} else if (x == 2) {
    result = "Two";
} else {
    result = "Unknown";
}

?空安全

转换成Java实际就是一个null检查,如何配合?:操作符,就是三目运算符后面赋一个默认值

委托

在 Kotlin 中,委托(Delegation) 是一种强大的设计模式,它允许对象将部分功能委托给另一个辅助对象来实现。Kotlin 原生支持多种委托方式,主要分为以下几种:

  1. 类委托(Class Delegation) 通过 by 关键字,将类的接口实现委托给另一个对象,常用于 “装饰器模式” 或 “代理模式”。

示例:委托接口实现

interface Printer {
    fun print(message: String)
}

class DefaultPrinter : Printer {
    override fun print(message: String) {
        println("Default Printer: $message")
    }
}

// 委托给 printer 对象
class CustomPrinter(private val printer: Printer) : Printer by printer {
    // 可以覆盖部分方法
    override fun print(message: String) {
        println("Before Printing...")
        printer.print(message) // 调用委托对象的方法
        println("After Printing...")
    }
}

fun main() {
    val defaultPrinter = DefaultPrinter()
    val customPrinter = CustomPrinter(defaultPrinter)
    customPrinter.print("Hello, Kotlin!")
}

输出: Before Printing… Default Printer: Hello, Kotlin! After Printing…

适用场景:

  • 增强或修改现有类的行为(如日志、缓存、权限控制)。
  • 避免继承,使用组合代替。
  1. 属性委托(Property Delegation) Kotlin 提供标准库委托(如 lazy、observable),也可以自定义委托。

(1) lazy 延迟初始化

val lazyValue: String by lazy {
    println("Computed only once!")
    "Hello"
}

fun main() {
    println(lazyValue) // 第一次访问时计算
    println(lazyValue) // 直接返回缓存值
}

输出: Computed only once! Hello Hello

(2) observable 监听属性变化

import kotlin.properties.Delegates

var observedValue: Int by Delegates.observable(0) { _, old, new ->
    println("Value changed from $old to $new")
}

fun main() {
    observedValue = 10  // 触发回调
    observedValue = 20  // 再次触发
}

输出: Value changed from 0 to 10 Value changed from 10 to 20

(3) vetoable 可拦截修改

var positiveNumber: Int by Delegates.vetoable(0) { _, old, new ->
    new > 0  // 只有 new > 0 时才允许修改
}

fun main() {
    positiveNumber = 10  // 允许
    println(positiveNumber)  // 10
    positiveNumber = -5     // 拒绝修改
    println(positiveNumber)  // 仍然是 10
}

(4) 自定义属性委托

class StringDelegate(private var initValue: String) {
    operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
        println("Getting value: $initValue")
        return initValue
    }

    operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
        println("Setting value: $value")
        initValue = value
    }
}

fun main() {
    var text by StringDelegate("Default")
    println(text)  // 调用 getValue
    text = "New Value"  // 调用 setValue
}

输出: Getting value: Default Default Setting value: New Value

字符串连接

编译后实际还是java的➕拼接

Object关键字

声明单例类、伴生对象(Companion Object)以及匿名对象。

object Singleton {
    val name = "Kotlin Singleton"
}

class MyClass {
    companion object {
        val instance = MyClass()
    }
}

作用为单例类 生成静态内部实例,私有构造器,确保单例

public final class Singleton {
    public static final Singleton INSTANCE = new Singleton();

    private Singleton() {
        // private constructor
    }

    public String getName() {
        return "Kotlin Singleton";
    }
}