Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Cicerone. How it works and how to use it. Mobiu...

Avatar for terrakok terrakok
April 22, 2017

Cicerone. How it works and how to use it. MobiusConf 2017

Avatar for terrakok

terrakok

April 22, 2017
Tweet

More Decks by terrakok

Other Decks in Technology

Transcript

  1. Ты кто такой? Константин Цховребов Android Team Lead MobileUp Более

    6 лет в Android. Работал в ooApps, Phereo, i-Free. С октября 2014 г. – ведущий Android-разработчик в MobileUp. В 2015 году стал победителем конкурса Павла Дурова для Android- разработчиков «Telegram Challenge». Выступал на конференциях AppsConf 2016, DevFest Siberia 2016.
  2. Навигация в андроид приложении • Переход между экранами • Экраны

    сменяются в некотором контейнере • Для совершения перехода нужен Context
  3. Mosby (http://hannesdorfmann.com/mosby/) public interface LoginView extends MvpView { public void

    showLoginForm(); public void showError(); public void showLoading(); public void loginSuccessful(); } public class LoginFragment … implements LoginView { … // Called when login was successful @Override public void loginSuccessful() { getActivity().finish(); } … }
  4. Требования к идеальной навигации • прямой доступ из презентера •

    не завязана на фрагменты • не фреймворк • короткие вызовы
  5. Требования к идеальной навигации • прямой доступ из презентера •

    не завязана на фрагменты • не фреймворк • короткие вызовы • легка в расширении
  6. Требования к идеальной навигации • прямой доступ из презентера •

    не завязана на фрагменты • не фреймворк • короткие вызовы • легка в расширении • приспособлена для тестов
  7. Требования к идеальной навигации • прямой доступ из презентера •

    не завязана на фрагменты • не фреймворк • короткие вызовы • легка в расширении • приспособлена для тестов • не зависит от жизненного цикла!
  8. //TODO: • прямой доступ из презентера • не завязана на

    фрагменты • не фреймворк • короткие вызовы • легка в расширении • приспособлена для тестов • не зависит от жизненного цикла!
  9. Navigator public interface Navigator { void applyCommand(Command command); } public

    class MainActivity extends Activity { private Navigator navigator = new Navigator() { @Override public void applyCommand(Command command) { ... } } }
  10. //TODO: • прямой доступ из презентера • не завязана на

    фрагменты • не фреймворк • короткие вызовы • легка в расширении • приспособлена для тестов • не зависит от жизненного цикла!
  11. //TODO: • прямой доступ из презентера • не завязана на

    фрагменты • не фреймворк • короткие вызовы • легка в расширении • приспособлена для тестов • не зависит от жизненного цикла! ✓
  12. public class Presenter { @Inject Navigator navigator; private void next()

    { navigator.applyCommand(new Forward(“Some screen”)); } }
  13. //TODO: • прямой доступ из презентера • не завязана на

    фрагменты • не фреймворк • короткие вызовы • легка в расширении • приспособлена для тестов • не зависит от жизненного цикла! ✓
  14. //TODO: • прямой доступ из презентера • не завязана на

    фрагменты • не фреймворк • короткие вызовы • легка в расширении • приспособлена для тестов • не зависит от жизненного цикла! ✓ ✓
  15. public class Presenter { @Inject Navigator navigator; private void authError()

    { navigator.applyCommand(new BackTo(null)); navigator.applyCommand(new Replace("Login screen", null)); navigator.applyCommand(new SystemMessage("Token expired!")); } }
  16. Router public class Router extends BaseRouter { void newRootScreenWithMessage(String screenKey,

    Object data, String message) {...} } public class Presenter { @Inject Router router; private void authError() { router.newRootScreenWithMessage("Login screen", null, "Token expired!"); } }
  17. //TODO: • прямой доступ из презентера • не завязана на

    фрагменты • не фреймворк • короткие вызовы • легка в расширении • приспособлена для тестов • не зависит от жизненного цикла! ✓ ✓
  18. //TODO: • прямой доступ из презентера • не завязана на

    фрагменты • не фреймворк • короткие вызовы • легка в расширении • приспособлена для тестов • не зависит от жизненного цикла! ✓ ✓ ✓
  19. public class Router extends BaseRouter { private Navigator navigator; public

    void setNavigator(Navigator navigator) { this.navigator = navigator; } public void removeNavigator() { this.navigator = null; } public void newRootScreenWithMessage(String screenKey, Object data, String message) { if (navigator != null) { navigator.applyCommand(new BackTo(null)); navigator.applyCommand(new Replace("Login screen", null)); navigator.applyCommand(new SystemMessage("Token expired!")); } } }
  20. public class MainActivity extends Activity { @Override protected void onResume()

    { super.onResume(); router.setNavigator(navigator); } @Override protected void onPause() { super.onPause(); router.removeNavigator(); } }
  21. //TODO: • прямой доступ из презентера • не завязана на

    фрагменты • не фреймворк • короткие вызовы • легка в расширении • приспособлена для тестов • не зависит от жизненного цикла! ✓ ✓ ✓
  22. //TODO: • прямой доступ из презентера • не завязана на

    фрагменты • не фреймворк • короткие вызовы • легка в расширении • приспособлена для тестов • не зависит от жизненного цикла! ✓ ✓ ✓ ✓
  23. Unit тестирование @Test public void someTest() throws Exception { Router

    mockRouter = mock(Router.class); Presenter presenter = new Presenter(); presenter.onNextButtonClicked(); verify(mockRouter, times(1)).navigateTo( eq("Some screen"), argument.capture() ); }
  24. //TODO: • прямой доступ из презентера • не завязана на

    фрагменты • не фреймворк • короткие вызовы • легка в расширении • приспособлена для тестов • не зависит от жизненного цикла! ✓ ✓ ✓ ✓
  25. //TODO: • прямой доступ из презентера • не завязана на

    фрагменты • не фреймворк • короткие вызовы • легка в расширении • приспособлена для тестов • не зависит от жизненного цикла! ✓ ✓ ✓ ✓ ✓
  26. Расширение возможностей • в большинстве случаев достаточно добавить метод в

    Router и реализовать его с помощью Command • реже необходимо создать новую Command
  27. //TODO: • прямой доступ из презентера • не завязана на

    фрагменты • не фреймворк • короткие вызовы • легка в расширении • приспособлена для тестов • не зависит от жизненного цикла! ✓ ✓ ✓ ✓ ✓
  28. //TODO: • прямой доступ из презентера • не завязана на

    фрагменты • не фреймворк • короткие вызовы • легка в расширении • приспособлена для тестов • не зависит от жизненного цикла! ✓ ✓ ✓ ✓ ✓ ✓
  29. //TODO: • прямой доступ из презентера • не завязана на

    фрагменты • не фреймворк • короткие вызовы • легка в расширении • приспособлена для тестов • не зависит от жизненного цикла! ✓ ✓ ✓ ✓ ✓ ✓ ✓
  30. Для кого мы ее создали? • В приложении нелинейная навигация

    Почему она может пригодиться вам? Для себя :)
  31. Для кого мы ее создали? • В приложении нелинейная навигация

    • Отделение логики от отображения Почему она может пригодиться вам? Для себя :)
  32. Для кого мы ее создали? • В приложении нелинейная навигация

    • Отделение логики от отображения • Сохранение вызовов навигации после возвращения к приложению Почему она может пригодиться вам? Для себя :)
  33. Возможности Cicerone public class Router extends BaseRouter { void navigateTo(String

    screenKey, Object data); void newScreenChain(String screenKey, Object data); void newRootScreen(String screenKey, Object data); void replaceScreen(String screenKey, Object data); void backTo(String screenKey); void exit(); void finishChain(); void exitWithMessage(String message); void showSystemMessage(String message); } public abstract class FragmentNavigator implements Navigator {} public abstract class AppNavigator implements Navigator {}
  34. Практика или Чичероне в реальной жизни Я понятия не имею

    кто это, но именно такая у меня ассоциация со словом чичероне :)
  35. public class CiceroneHolder { private HashMap<String, Cicerone<Router>> containers; public CiceroneHolder()

    { containers = new HashMap<>(); } public Cicerone<Router> getCicerone(String containerTag) { if (!containers.containsKey(containerTag)) { containers.put(containerTag, Cicerone.create()); } return containers.get(containerTag); } }
  36. IMHO: Activity & Fragments • Fragment’ы - легкая адаптация под

    планшеты • Activity - долгий старт на слабых устройствах
  37. IMHO: Activity & Fragments • Fragment’ы - легкая адаптация под

    планшеты • Activity - долгий старт на слабых устройствах • Activity - ограничение в возможностях анимации
  38. IMHO: Activity & Fragments • Fragment’ы - легкая адаптация под

    планшеты • Activity - долгий старт на слабых устройствах • Activity - ограничение в возможностях анимации • От Fragment’ов полностью не отказаться (табы)
  39. IMHO: Activity & Fragments • Fragment’ы - легкая адаптация под

    планшеты • Activity - долгий старт на слабых устройствах • Activity - ограничение в возможностях анимации • От Fragment’ов полностью не отказаться (табы) • Мы смогли полюбить Fragment’ы
  40. IMHO: Activity & Fragments • Fragment’ы - легкая адаптация под

    планшеты • Activity - долгий старт на слабых устройствах • Activity - ограничение в возможностях анимации • От Fragment’ов полностью не отказаться (табы) • Мы смогли полюбить Fragment’ы • Activity - “приложение внутри приложения”
  41. Смешанная навигация. Ч. 2 private Navigator navigator = new AppNavigator(this,

    R.id.mainContainer) { @Override protected Intent createActivityIntent(String screenKey, Object data) { switch (screenKey) { case "F_4": return new Intent(getBaseContext(), LoginActivity.class); } return null; } @Override protected Fragment createFragment(String screenKey, Object data) { switch (screenKey) { case "Some screen": return new SomeFragment(); } return null; } };