Skip to content

Commit

Permalink
Merge pull request #18 from egorikftp/patch-1
Browse files Browse the repository at this point in the history
Исправление ошибок и конвертация в книгу
  • Loading branch information
xanderblinov authored Jan 22, 2018
2 parents 32fd048 + 9ecfe8d commit b34ace8
Show file tree
Hide file tree
Showing 6 changed files with 36 additions and 41 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@
* Каждая глава представлена отдельной директорией, в которой лежит текстовый файл и необходимые вложения
* Формат текстовых файлов **M**ark**d**own (.md)

## Правила для контрибьютеров
## Правила для контрибьюторов

* Написание каждой из глав происходит в отдельной feature ветке (см. [шпаргалку по Gitflow](https://danielkummer.github.io/git-flow-cheatsheet/index.ru_RU.html))
* Изменения сливаются в develop посредством pull request
* commit message осуществляются **сторого на русском языке**
* commit message осуществляются **строго на русском языке**


6 changes: 3 additions & 3 deletions cases/auth/Auth_article.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
## Аутентификация <br>
Думаю, что во многих приложениях, особенно в банковских, вы встречались со следующим поведением приложения: работаете, работаете, а потом вдруг появляется экран ввода пин-кода. Такое поведение обосновано мерами безопасности. Опишем данный процесс чуть более подробно с некоторыми техническими подробностями. <br>
При старте приложения вам показывается экран с вводом пинкода. Вы посылаете запрос на сервер, и в случае успеха (верного пинкода) вам возвращается токен, и стартует сессия. Далее часть запросов вы шлете с использованием данного токена. Токен прикрепляется либо к заголовку запроса, либо к телу. Подобные запросы еще также называются запросами авторизованной зоны. То есть сессия - это время, в течении которого данный токен действителен на стороне сервера.<br>
При старте приложения вам показывается экран с вводом пин-кода. Вы посылаете запрос на сервер, и в случае успеха (верного пин-кода) вам возвращается токен, и стартует сессия. Далее часть запросов вы шлете с использованием данного токена. Токен прикрепляется либо к заголовку запроса, либо к телу. Подобные запросы еще также называются запросами авторизованной зоны. То есть сессия - это время, в течении которого данный токен действителен на стороне сервера.<br>
Через какое-то время сессия завершается, обычно по истечению какого-то конкретного промежутка времени, и тогда токен "протухает". Запросы с протухшим токеном возвращают ошибку. Тогда либо нужно просто обновить токен специальным запросом, либо нужно пользователя снова перебросить на экран ввода пин-кода, обновить токен, и продолжить слать запросы авторизованной зоны с обновившимся токеном. <br>
А теперь представим, что подобное поведение вам необходимо реализовать в своем приложении. И архитектура вашего приложения - Чистая архитектура. <br>
Поэтому возникает ряд интересных вопросов: <br>
Expand Down Expand Up @@ -85,7 +85,7 @@ public class MainAuthenticator implements Authenticator {
}
```
Благодаря добавлению ```synchronized``` к методу ```authenticate``` и блокирующему методу ```authHolder.refresh()``` мы добиваемся того, что при возникновении 401 ошибки мы первым делом идем обновлять токен. Все остальные запросы, которые возвращают 401 ошибку, тоже ждут обновления токена, и только после этого идут повторные запросы с обновленным токеном. Очень удобно!<br>
Логичный вопрос. А как должен обновляться **AuthHolder**, через что он организизует свой запрос обновления токена. Так как опять-таки это внутренняя кухня организации работы с сервером, то Repositories и другие слои про это не знают. Это внутреннее дело **Data**. <br>
Логичный вопрос. А как должен обновляться **AuthHolder**, через что он организует свой запрос обновления токена. Так как опять-таки это внутренняя кухня организации работы с сервером, то Repositories и другие слои про это не знают. Это внутреннее дело **Data**. <br>
Хорошо, а через что тогда лучше осуществлять запрос? Через **CommonNetwork**, который будет включать классы для осуществления запросов неавторизованной зоны (запросов, не требующих токена). В [примере](https://github.com/AndroidArchitecture/AuthCase/tree/sample_1) эти классы объединены в **CommonNetworkModule**.<br>
Обновленная схема приобретет вид:<br>
![enter image description here](https://i.imgur.com/xvFKOZK.png)
Expand Down Expand Up @@ -280,7 +280,7 @@ public class AuthHolder {
}
```
Обратим внимание на метод ```public void refresh()```. В нем мы обнуляем пинкод, сообщаем подписчику об истечении сессии, в данном случае сообщаем **AuthRepository**. Затем через обычный ```CountDownLatch``` мы блокируем поток до тех пор пока не будет вызван метод ```public void updatePinCode(@NonNull String pinCode)```. В данном методе приходит введенный пользователем пин-код (**синие стрелочки** на рисунке выше). <br>
На всякий случай отмечу следующий момент. В данном случае обновление пин-кода - синхронная операция и выполняется она в главном потоке, поэтому с главного потока мы без проблем освобождаем поток, задержанный ```CountDownLatch```. Но если обновление пин-кода была бы асинхронной операцией, и выполнялась бы в другом потоке, то поток этот должен был браться не из ```Executors``` в **AuthNetwork**, иначе теоретически могло получиться, что все потоки были бы в режиме ожидания, и обновить пин-код было бы некому.<br>
На всякий случай отмечу следующий момент. В данном случае обновление пин-кода - синхронная операция и выполняется она в главном потоке, поэтому с главного потока мы без проблем освобождаем поток, задержанный ```CountDownLatch```. Но если обновление пин-кода была бы асинхронной операцией, и выполнялась бы в другом потоке, то поток этот должен был браться не из ```Executors``` в **AuthNetwork**, иначе теоретически могло получиться, что все потоки были бы в режиме ожидания, и обновить пин-код было бы некому.<br>
После того как пришел пин-код, поток, вызвавший ```refresh```, разблокируется и запускает запрос по обновлению токена (метод ```private Single<String> updateToken()```). В случае успешного обновления, остальные запросы с 401 ошибкой также перестартуются.<br>
Конечно же, механизм по обновлению токена с ожиданиями через ```CountDownLatch``` вы можете переделать по своему усмотрению. В этом решении также могут быть не учтены некоторые крайние случаи. Тут важна опять-таки - концепция.<br>
Вот собственно и все манипуляции =)
8 changes: 2 additions & 6 deletions cases/wizards/Wizards_article.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

- максимальная переиспользуемость экранов без внесения изменений в соответствующие классы (Презентеры, Интеракторы и т.д.),<br>
- независимость экранов, то есть чтобы экраны не знали друг о друге,<br>
- сосредоточнение логики переходов в одном месте,<br>
- сосредоточение логики переходов в одном месте,<br>
- тестируемость данной логики.<br>

Что же, давайте начнем с [простого примера, ветка sample_1](https://github.com/AndroidArchitecture/WizardCase/tree/sample_1). Нам необходимо написать Визард регистрации пользователя. Состоять он будет из трех экранов:<br>
Expand Down Expand Up @@ -153,7 +153,6 @@ public class MainWizardSmartRouter {
currentWizardStep = WizardStep.LICENSE;
router.backTo(LICENSE_SCREEN);
}

};

public WizardSmartRouter(Router router) {
Expand Down Expand Up @@ -322,7 +321,7 @@ public class InfoFinishFragment extends InfoFragment {
Разве никак нельзя как-то избежать этого безжалостного дублирования кода? На самом деле можно. Давайте еще раз взглянем на схему:<br>
![image](https://habrastorage.org/web/10e/68d/808/10e68d808c994bcdb3f8000724f36ee9.png)

На самом деле ее можно трансформитровать до такой схемы:<br>
На самом деле ее можно трансформировать до такой схемы:<br>
![image](https://habrastorage.org/web/b72/6ee/ab7/b726eeab7080457bb6a0258cfc2dd8db.png)

То есть последовательность экранов InformationScreen, LoginScreen и RegistrationScreen и логику их взаимодействия мы выделяем в новый **AccountWizard**. Этот **AccountWizard** может сообщить внешнему Визарду, допустим, только две вещи:<br>
Expand Down Expand Up @@ -356,7 +355,6 @@ public class AccountWizardSmartRouter {
public void infoWizardBack() {
router.finishChain();
}

};

private final LoginWizardPart loginWizardPart = new LoginWizardPart() {
Expand All @@ -378,7 +376,6 @@ public class AccountWizardSmartRouter {
accountWizardStep = REGISTRATION;
router.navigateTo(REGISTRATION_SCREEN);
}

};

private final RegistrationWizardPart registrationWizardPart = new RegistrationWizardPart() {
Expand All @@ -394,7 +391,6 @@ public class AccountWizardSmartRouter {
accountWizardStep = LOGIN;
router.backTo(LOGIN_SCREEN);
}

};

public AccountWizardSmartRouter(Router router,
Expand Down
Binary file added ebook/Android Architecture Book.epub
Binary file not shown.
Loading

0 comments on commit b34ace8

Please sign in to comment.