应用程序恢复后单例对象变为空

Posted

技术标签:

【中文标题】应用程序恢复后单例对象变为空【英文标题】:Singleton object becomes null after app is resumed 【发布时间】:2018-08-09 08:22:30 【问题描述】:

对于我的 android 项目,我需要全局单例缓存对象来通过应用程序访问有关用户的数据。

当应用程序进入后台并且在使用其他应用程序一段时间后我尝试打开缓存对象中的应用程序变量为空时,会出现问题。当我杀死应用程序并再次打开它时,一切都很好。 我正在使用依赖注入来访问 Cache 对象。 如果发生这种情况,为什么应用程序不会重新启动? 即使在内存不足的情况下,是否有一些注释可以保持缓存变量?

这是我的缓存类

class Cache 
    var categories : Array<BaseResponse.Category>? = null
    var user : BaseResponse.User? = null
    var options : BaseResponse.OptionsMap? = null
    var order: MenuOrderDataModel? = null

这是 DI 的存储模块

@Module class StorageModule 

    @Singleton @Provides fun getSharedPrefs(context: Context): SharedPreferences 
        return PreferenceManager.getDefaultSharedPreferences(context)
    


    @Singleton @Provides fun getCache() : Cache = Cache()

我注入对象@Inject lateinit var cache: Cache,然后在初始屏幕中填充用户数据。

编辑 - 从应用程序和启动活动添加代码 sn-ps

class MyApp : Application() 
    val component: ApplicationComponent by lazy 
        DaggerApplicationComponent
                .builder()
                .appModule(AppModule(this))
                .build()
    

    companion object 
        @JvmStatic lateinit var myapp: MyApp 
    

    override fun onCreate() 
        super.onCreate()
        myapp= this
        Fabric.with(this, Crashlytics())
    

飞溅活动:

class SplashActivity : AppCompatActivity(), View.OnClickListener 

    @Inject lateinit var viewModel : ISplashViewModel
    private lateinit var disposable : Disposable

    override fun onCreate(savedInstanceState: Bundle?) 
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_splash)

        MyApp.myapp.component.inject(this)

【问题讨论】:

可能你从Application.onCreate而不是MainActivity.onCreate运行依赖注入 是的。你能在 MainActivity 中解释一下为什么吗?因为在 SplashActivity 我需要注入 viewmodel。我已经编辑了答案,因此您可以查看我的 Application 类以及我如何使用 DI 您可能只在一个活动中设置Cache 中的变量,并且您的应用在进程死亡后从其他活动重新启动。 是的,你是对的,但我应该在每个活动中初始化用户数据以保持应用程序一致吗?有没有办法在匕首中注释缓存,以便缓存变量在清理内存后保持它们的状态?如果 dagger 可以自动序列化然后反序列化,或者类似的东西,那就太好了。 现在如果只有在Cache 中初始化某些变量的代码是问题的一部分... :) 【参考方案1】:

您正在崩溃,因为您在一个 Activity 中初始化了这些变量,并希望它始终在另一个 Activity 中设置。

但是 Android 不是这样工作的,你很容易崩溃,因为在内存不足的情况发生后,只有 当前 Activity 会被重新创建,之前的 Activity 会在返回导航时被重新创建,并且所有静态变量都被清空(因为该过程在技术上已重新启动)。

试试看:

使用 HOME 按钮将您的应用程序置于后台

单击 Logcat 选项卡上的终止按钮

然后从启动器重新启动应用程序。

你会体验到这种现象。

在 Android Studio 4.0 中,在您使用 Android Studio 中的 Run 而不是从启动器启动应用程序后,终止按钮有时会有所不同,并且可能会强制停止您的应用程序,使其忘记您的您尝试的任务状态。在这种情况下,只需再试一次,从启动器启动应用程序。它会在您第二次尝试时起作用。

您也可以从终端触发“终止应用程序”行为,按照https://codelabs.developers.google.com/codelabs/android-lifecycles/#6,它看起来像这样:

$ adb shell am kill your.app.package.name

解决方案:检查空值并在基本活动(或 LiveData.onActive)中重新初始化事物,或使用onSaveInstanceState

【讨论】:

如果我们可以在内存不足的情况下保留这些变量,那就太好了。因此,如果这是不可能的,那么这个解决方案是正确的。谢谢 无法保留它们,这就是它们被无效化的原因。 根据需要使用带有 SQL 或 Room 或 Shared Preferences 的持久存储,或者将其保存在经过某种加密的文件中。 @EpicPandaForce 我们能否通过使用开发者选项中的“不保留活动”设置重新创建相同的行为? 不,这是完全不同的行为,因为 static 变量不会被 null 淘汰。 Parcelables 也被缓存,因此它可以掩盖某些状态恢复类加载器错误,例如当您在 extends RecyclerView 的类中使用 BaseSavedState 时得到的错误。

以上是关于应用程序恢复后单例对象变为空的主要内容,如果未能解决你的问题,请参考以下文章

单例实现备忘

传递给组件的对象变为空

合并到父/主上下文后,子上下文对象变为空

chrome.storage.sync.set 后对象属性变为空

如何使 Android 的 Singleton 对象持久化

无法从单例对象打开SQLite数据库