Для моего проекта Android мне нужен глобальный объект Singleton Cache для доступа к данным о пользователе через приложение.

Проблема возникает, когда приложение переходит в фоновый режим, и после некоторого времени использования других приложений я пытаюсь открыть переменные приложения в объектах кэша, равные нулю. Все нормально, когда я убиваю приложение и снова открываю его. Я использую внедрение зависимостей для доступа к объекту 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, а затем заполняю пользовательскими данными в заставке.

Изменить - добавлены фрагменты кода из приложения и активности запуска

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)
}
11
Nikola 1 Мар 2018 в 12:43
Вероятно, вы запускаете инъекцию депеднентности из Application.onCreate вместо MainActivity.onCreate
 – 
Marko Topolnik
1 Мар 2018 в 12:50
Да. Не могли бы вы подробнее объяснить, почему именно в MainActivity? Потому что в SplashActivity мне нужно ввести viewmodel. Я отредактировал ответ, чтобы вы могли видеть мой класс приложения и то, как я использую DI
 – 
Nikola
1 Мар 2018 в 14:34
Вероятно, вы установили переменные в Cache только в ОДНОМ действии, и ваше приложение перезапускается из другого Действия после смерти процесса.
 – 
EpicPandaForce
5 Мар 2018 в 00:19
Да, вы правы, но следует ли инициализировать пользовательские данные в каждом действии, чтобы приложение оставалось согласованным? Есть ли способ аннотировать кеш в кинжале, чтобы переменные кеша сохраняли свое состояние после очистки оперативной памяти? Было бы здорово, если бы dagger мог автоматически сериализоваться, а затем десериализоваться или что-то в этом роде.
 – 
Nikola
5 Мар 2018 в 00:32
Вот если бы частью вопроса был только код для инициализации некоторой переменной в Cache ... :)
 – 
EpicPandaForce
5 Мар 2018 в 01:01

1 ответ

Лучший ответ

Вы получаете сбои, потому что вы инициализируете эти переменные в одном действии и ожидаете, что они будут установлены всегда в другом действии.

Но Android так не работает, вы можете легко закончить сбой, потому что после того, как произойдет нехватка памяти, сначала воссоздается только текущее Activity, предыдущее Activity воссоздается при обратной навигации и все статические переменные обнуляются (потому что процесс технически перезапущен).

Попробуйте:

  • переведите приложение в фоновый режим с помощью кнопки HOME

  • нажмите кнопку ПРЕКРАТИТЬ на вкладке Logcat

terminate button

  • затем повторно запустите приложение с панели запуска.

Вы испытаете это явление.

В Android Studio 4.0 после того, как вы используете Run из Android Studio вместо запуска приложения из средства запуска, кнопка "Завершить" ИНОГДА работает немного иначе и МОЖЕТ принудительно остановить приложение, заставляя его забыть о вашей задаче. заявить о вашей попытке. В этом случае просто попробуйте еще раз, запустив приложение из средства запуска. Будет работать со второй попытки.

Вы также можете активировать поведение «Завершить приложение» из терминала в соответствии с https : //codelabs.developers.google.com/codelabs/android-lifecycles/#6, это выглядит так:

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

Решение: проверьте наличие нулей и повторно инициализируйте вещи в базовом действии (или в LiveData.onActive) или используйте onSaveInstanceState.

31
EpicPandaForce 28 Янв 2021 в 05:55
Было бы здорово, если бы мы могли сохранить эти переменные даже в условиях нехватки памяти. Так что, если это невозможно, то это решение правильное. Спасибо
 – 
Nikola
7 Мар 2018 в 12:30
Удержать их невозможно, поэтому они обнуляются.
 – 
EpicPandaForce
7 Мар 2018 в 12:48
Используйте постоянное хранилище либо с SQL, либо с комнатой, либо с общими настройками, если вам нужно, или сохраните его в файле с некоторым шифрованием.
 – 
SowingFiber
16 Дек 2019 в 08:24
Можем ли мы воссоздать то же поведение, используя параметр «Не сохранять действия» в параметрах разработчика?
 – 
11m0
10 Янв 2020 в 00:30
1
Нет, это совершенно другое поведение, поскольку переменные static не удаляются null. Также Parcelables кэшируются, поэтому он может маскировать определенные ошибки загрузчика классов восстановления состояния, подобные той, которую вы получаете, когда используете BaseSavedState в классе, который extends RecyclerView.
 – 
EpicPandaForce
10 Янв 2020 в 00:34