Android新手关于Application类与Singleton的困惑及技术咨询
Hey there! 作为Android新手,纠结Application和单例的问题太正常了——这俩确实容易踩坑,尤其是内存这块。咱们一步步来拆解你的问题:
一、除持久化外,安全存储全局值的替代方案
首先得明确:OOM的核心原因是长期持有大对象/过多对象的强引用,导致GC无法回收。所以解决方案的关键就是减少不必要的全局强引用,让内存能及时释放。
用ViewModel绑定Application生命周期
如果你需要的全局数据是和App生命周期绑定,但又不想直接存在Application里,可以用ViewModel,并通过ViewModelProvider.AndroidViewModelFactory把Application作为ViewModel的Owner。这样ViewModel会和Application同生命周期,但好处是它的设计更规范,自带生命周期感知,而且比单例更容易测试和替换。注意:别往ViewModel里塞过大的对象(比如几十MB的Bitmap),小的业务数据或配置信息完全没问题。依赖注入(DI)的作用域管理
比如用Hilt这类DI框架,别滥用@Singleton(它本质是和Application绑定的全局实例),而是根据实际需求用自定义作用域。比如某个数据只在用户登录后的流程中用到,就用@UserScope这类自定义作用域,当用户退出时就销毁这个作用域内的对象,释放内存。DI能帮你更清晰地管理对象的生命周期,避免单例的“永久持有”问题。用SoftReference/WeakReference存储非必需全局数据
如果某些全局数据不是必须一直留在内存里(比如缓存的用户头像缩略图),可以用SoftReference包裹它——当系统内存不足时,GC会自动回收这些对象,从而避免OOM。使用的时候记得先判断get()是否为null,比如:private val cachedAvatar = SoftReference<Bitmap>(loadAvatarBitmap()) // 使用时 val avatar = cachedAvatar.get() ?: loadAvatarBitmap()WeakReference则会在对象没有其他强引用时立即被回收,适合更临时的全局数据。按需创建,及时释放
尽量别把所有东西都做成“全局”。比如某组数据只在A、B、C三个页面用到,那就在A页面创建,在C页面销毁时把引用置为null,让GC能回收。这种“局部全局”的方式比真正的全局更灵活,也更省内存。
二、Application类的正确适用场景
Application是App的全局上下文,它的生命周期和App完全一致,但它不是用来存业务数据的容器,这些场景才是它的主场:
全局框架初始化
像OkHttp、Retrofit、Glide、Hilt这些第三方框架,通常需要在Application.onCreate()里初始化一次,因为它们需要全局唯一的实例,而且初始化成本较高,只做一次最划算。全局生命周期监听
注册ActivityLifecycleCallbacks,可以监听所有Activity的创建、销毁等事件,用来统计App的活跃时长、给所有Activity统一设置主题/字体,或者在App进入后台时做一些清理工作。全局异常捕获
实现Thread.UncaughtExceptionHandler,并在Application里设置为默认的异常处理器,这样可以捕获App中未处理的崩溃,收集崩溃日志或者给用户显示友好的提示。存储轻量全局配置
比如App的版本号、当前设备的基本信息、全局的开关配置(比如是否开启推送)这些体积很小的数据,放在Application里完全没问题,不会占用过多内存。作为全局上下文的载体
当某些工具类需要上下文,但又不想依赖Activity/Fragment的上下文时,可以用Application的上下文,避免内存泄漏(因为Application的生命周期和App一致,不会因为页面销毁而导致泄漏)。
内容的提问来源于stack exchange,提问作者Vineet

