Процесс входа в систему с помощью Jetpack Navigation и RxJava + Dagger2

TL; DR

Учитывая новые рекомендации относительно экрана запуска и начала навигации по пункту назначения, как нам элегантно интегрировать логику входа в приложение для Android, используя Jetpack Navigation и RxJava?


В соответствии с Принципами навигации в моем приложении должен быть фиксированный начальный / выходной экран. Назовем это HomeScreen.

Учитывая, что доступ к этому экрану предоставляется только пользователям, выполнившим вход (как и все другие экраны, кроме LoginFragment), где и как мне разместить логику, чтобы проверить, «выполнил ли пользователь вход в систему? Нет, перейти к LoginFragment»?

Мое приложение разделено на слои (луковая архитектура). У меня есть SessionManager POJO, введенный в мой Remote layer, содержащий токен и другую информацию, связанную с сеансом, используемую в Remote layer. У меня также есть UserRepository, используемый в моем случае использования LoginUser, который будет заполнять / обновлять SessionManager.

Я не хочу, чтобы каждый вариант использования или каждый докладчик проверяли, вошел ли пользователь в систему, поскольку это не их забота (верно?). Когда и как я должен проверить, что мой пользователь вошел в систему? В MainActivity держится навигационная составляющая? Как я могу правильно изменить навигацию, когда я использую RxJava (поэтому ответ на проверку асинхронный)?

Вопросы

Должен ли я сделать это в SplashScreen / Launcher? Должен ли этот Launcher быть отдельным действием (конфликт с новым Рекомендация по отдельным действиям), или это должно быть частью моей навигации? Должен ли я выполнять проверки в моем приложении OnCreate и отложить запуск своей MainActivity? Должен ли каждый докладчик составлять сценарии использования, сначала задавая вопрос «Я вошел в систему?» тогда "делать что-нибудь"?

Спасибо !


person w00ly    schedule 18.09.2018    source источник


Ответы (1)


Я знаю, что для этого есть множество решений. Вот мои два цента.

Внутри вашего класса Application используйте подкомпонент, чтобы представить состояние входа в приложение. Это, скажем, userComponent добавляет привязки на уровне пользователя для всего, что требует учетной записи пользователя, например имени пользователя, учетных данных, аутентифицированного клиента API. Затем убедитесь, что все фрагменты (кроме LoginFragment) внедряются с использованием этого компонента. Это может быть ваш класс приложения:

class MyApplication : Application() {
    private lateinit var appComponent: AppComponent
    private var userComponent: UserComponent? = null
    ...
}

При первом запуске ваша MainActivity проверит состояние входа в систему (userComponent == null) и при необходимости перейдет к LoginFragment. Успешная аутентификация должна затем создать userComponent и заполнить его данными пользователя. Затем при переходе к любому другому фрагменту он получает доступ к этому субкомпоненту и всем его привязкам. Чтобы позже выйти, установите userComponent = null и уйдите от пользовательских фрагментов.

class MyApplication : Application() {
    ...
    fun signIn(account: Account) {
        userComponent = appComponent.userComponent().account(account).build()
    }

    fun signOut() {
        userComponent = null
    }
}

Чтобы пропустить вход при втором запуске приложения, попробуйте войти в систему пользователя (создайте userComponent) в onCreate приложения. Затем ваша единственная MainActivity сразу перейдет, например, к вашему HomeFragment.

Для пользователей dagger.android: получите свой fragmentInjector со своего userComponent, чтобы AndroidSupportInjection.inject(this) давал вам доступ к привязкам на уровне пользователя.

class MyApplication : Application(), HasSupportFragmentInjector {
    private var fragmentInjector: DispatchingAndroidInjector<Fragment>? = null
    ...
    fun signIn(account: Account) {
        userComponent = appComponent.userComponent().account(account).build()
        fragmentInjector = userComponent.fragmentInjector()
    }

    override fun supportFragmentInjector() = fragmentInjector!!
    ...
}

Надеюсь, этот ответ будет вам полезен!

person laenger    schedule 19.09.2018
comment
Спасибо за ответ. Что касается сборки компонента для 2-го запуска, что, если процесс сборки асинхронный? MainActivity будет загружен до завершения процесса загрузки, верно? - person w00ly; 20.09.2018
comment
Этот случай действительно предполагает синхронный вызов. Однако все, что нужно пользовательскому компоненту, - это своего рода идентификатор ранее выбранного пользователя. Это может быть всего лишь строка или здесь Account из Android AccountManager. Любой вид получения новых токенов аутентификации или проверка существующих происходит в пределах области действия пользователя. Когда это приводит к ситуации, когда пользователю необходимо повторно ввести свои учетные данные или иначе он не может восстановить, приложение может автоматически выйти из системы вскоре после запуска. - person laenger; 20.09.2018
comment
В моем случае такой идентификатор извлекается из репозитория с помощью RxJava, поэтому он определенно асинхронный. Что было бы более элегантным способом справиться с потоком, избегая мигания экранов? Используете заставку и состояние загрузки в UserComponent? - person w00ly; 20.09.2018
comment
Синхронная загрузка одного идентификатора из общих предпочтений мало влияет на производительность по сравнению с другими вещами, которые многие приложения обычно делают при запуске. Таким образом, ваш вариант здесь - не использовать базу данных для этого простого шага. - person laenger; 20.09.2018
comment
Это больше вопрос дизайна и разделения задач, чем проблема производительности: весь доступ к репозиториям асинхронный :) - person w00ly; 20.09.2018