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

Гладкая навигация с Cicerone

Гладкая навигация с Cicerone

Avatar for Alidibir Akhbulatov

Alidibir Akhbulatov

January 23, 2019

More Decks by Alidibir Akhbulatov

Other Decks in Programming

Transcript

  1. Архитектура в Android-приложениях: • Паттерн MVC (устарел) • Паттерн MVP

    • Паттерн MVVM + Clean Architecture (Чистая архитектура)
  2. Навигация в MVP Навигация - это скорее бизнес-логика Ответственность за

    переходы возлагается на презентер В презентере не должно быть импортов из Android Framework, например, Context
  3. Навигация в MVP Навигация - это скорее бизнес-логика Ответственность за

    переходы возлагается на презентер В презентере не должно быть импортов из Android Framework, например, Context import android.content.Context import android.content.res.Resources class UsersPresenter { ... }
  4. Навигация в MVP Навигация - это скорее бизнес-логика Ответственность за

    переходы возлагается на презентер В презентере не должно быть импортов из Android Framework, например, Context import android.content.Context import android.content.res.Resources class UsersPresenter { ... }
  5. Плюсы Cicerone • Прямой доступ из презентера • Не завязана

    на фрагменты (т.е. есть также поддержка переходов между Activity)
  6. Плюсы Cicerone • Прямой доступ из презентера • Не завязана

    на фрагменты (т.е. есть также поддержка переходов между Activity) • Не фреймворк
  7. Плюсы Cicerone • Прямой доступ из презентера • Не завязана

    на фрагменты (т.е. есть также поддержка переходов между Activity) • Не фреймворк • Короткие вызовы
  8. Плюсы Cicerone • Прямой доступ из презентера • Не завязана

    на фрагменты (т.е. есть также поддержка переходов между Activity) • Не фреймворк • Короткие вызовы • Легка в расширении
  9. Плюсы Cicerone • Прямой доступ из презентера • Не завязана

    на фрагменты (т.е. есть также поддержка переходов между Activity) • Не фреймворк • Короткие вызовы • Легка в расширении • Не зависит от жизненного цикла!
  10. Структура Cicerone На схеме четыре сущности: Command – простейшая команда

    перехода, которую выполняет Navigator Navigator – реализация «переключения экранов» внутри контейнера (для фрагментов) и Activity.
  11. Структура Cicerone На схеме четыре сущности: Command – простейшая команда

    перехода, которую выполняет Navigator Navigator – реализация «переключения экранов» внутри контейнера (для фрагментов) и Activity. Router - класс, которому передаем команды для выполнения перехода
  12. Структура Cicerone На схеме четыре сущности: Command – простейшая команда

    перехода, которую выполняет Navigator Navigator – реализация «переключения экранов» внутри контейнера (для фрагментов) и Activity. Router - класс, которому передаем команды для выполнения перехода CommandBuffer – отвечает за сохранность вызванных команд навигации, если нет возможности сразу выполнить переход
  13. Команды переходов Forward Осуществляет переход на новый экран, добавляя его

    в текущую цепочку экранов R обозначен корневой экран приложения
  14. Команды переходов Forward public Forward(Screen screen) { this.screen = screen;

    } Осуществляет переход на новый экран, добавляя его в текущую цепочку экранов R обозначен корневой экран приложения
  15. Команды переходов Back Удаляет последний активный экран из цепочки, и

    возвращает на предыдущий R обозначен корневой экран приложения
  16. Команды переходов Back Удаляет последний активный экран из цепочки, и

    возвращает на предыдущий R обозначен корневой экран приложения При вызове на корневом экране ожидается выход из приложения
  17. Команды переходов BackTo Возвращает на любой из экранов в цепочке,

    достаточно указать его экран (Screen) R обозначен корневой экран приложения
  18. Команды переходов BackTo Возвращает на любой из экранов в цепочке,

    достаточно указать его экран (Screen) R обозначен корневой экран приложения public BackTo(Screen screen) { this.screen = screen; }
  19. Команды переходов BackTo Возвращает на любой из экранов в цепочке,

    достаточно указать его экран (Screen) R обозначен корневой экран приложения При вызове на корневом экране ожидается выход из приложения public BackTo(Screen screen) { this.screen = screen; }
  20. Команды переходов Replace Заменяет активный экран на новый R обозначен

    корневой экран приложения public Replace(Screen screen) { this.screen = screen; }
  21. Screen public abstract class Screen { protected String screenKey =

    getClass().getCanonicalName(); public String getScreenKey() { return screenKey; } }
  22. Screen public abstract class Screen { protected String screenKey =

    getClass().getCanonicalName(); public String getScreenKey() { return screenKey; } } public abstract class SupportAppScreen extends Screen { public Fragment getFragment() { return null; } public Intent getActivityIntent(Context context) { return null; } }
  23. Screen public abstract class Screen { protected String screenKey =

    getClass().getCanonicalName(); public String getScreenKey() { return screenKey; } } public abstract class SupportAppScreen extends Screen { public Fragment getFragment() { return null; } public Intent getActivityIntent(Context context) { return null; } } Реализация:
  24. Screen public abstract class Screen { protected String screenKey =

    getClass().getCanonicalName(); public String getScreenKey() { return screenKey; } } public abstract class SupportAppScreen extends Screen { public Fragment getFragment() { return null; } public Intent getActivityIntent(Context context) { return null; } } public class UsersScreen extends SupportAppScreen { public Fragment getFragment() { return new UsersFragment(); } } Реализация:
  25. Navigator Команды только описывают переходы Как реализовать переключение экранов? -

    c помощью Navigator public interface Navigator { void applyCommands(Command[] commands); }
  26. Navigator Navigator всегда будет там, где находится контейнер для переключаемых

    экранов • в Activity для переключения Fragment’ов. • во Fragment’е для переключения вложенных (child) Fragment’ов.
  27. Готовая реализация Navigator - SupportAppNavigator public class SupportAppNavigator implements Navigator

    { public SupportAppNavigator(FragmentActivity activity, int containerId) { this(activity, activity.getSupportFragmentManager(), containerId); } }
  28. Готовая реализация Navigator - SupportAppNavigator public class SupportAppNavigator implements Navigator

    { public SupportAppNavigator(FragmentActivity activity, int containerId) { this(activity, activity.getSupportFragmentManager(), containerId); } public SupportAppNavigator(FragmentActivity activity, FragmentManager fragmentManager, int containerId) { this.activity = activity; this.fragmentManager = fragmentManager; this.containerId = containerId; } }
  29. Router public class Router extends BaseRouter { // Переход на

    новый экран public void navigateTo(Screen screen) { executeCommands(new Forward(screen)); } }
  30. Router public class Router extends BaseRouter { // Переход на

    новый экран public void navigateTo(Screen screen) { executeCommands(new Forward(screen)); } // Очищает все экраны и открывает новый корневой public void newRootScreen(Screen screen) { executeCommands( new BackTo(null), new Replace(screen) ); } }
  31. Router public class Router extends BaseRouter { // Переход на

    новый экран public void navigateTo(Screen screen) { executeCommands(new Forward(screen)); } // Очищает все экраны и открывает новый корневой public void newRootScreen(Screen screen) { executeCommands( new BackTo(null), new Replace(screen) ); } // Замена текущего экрана public void replaceScreen(Screen screen) { executeCommands(new Replace(screen)); } }
  32. Router public class Router extends BaseRouter { // Переход на

    новый экран public void navigateTo(Screen screen) { executeCommands(new Forward(screen)); } // Очищает все экраны и открывает новый корневой public void newRootScreen(Screen screen) { executeCommands( new BackTo(null), new Replace(screen) ); } // Замена текущего экрана public void replaceScreen(Screen screen) { executeCommands(new Replace(screen)); } // Возврат на любой экран в цепочке public void backTo(Screen screen) { executeCommands(new BackTo(screen)); } }
  33. Router public class Router extends BaseRouter { ... // Открывает

    несколько экранов внутри одной транзакции public void newChain(Screen... screens) { ... } }
  34. Router public class Router extends BaseRouter { ... // Открывает

    несколько экранов внутри одной транзакции public void newChain(Screen... screens) { ... } // Очищает стек и открывает несколько экранов внутри 1-й транзакции public void newRootChain(Screen... screens) { ... } }
  35. Router public class Router extends BaseRouter { ... // Открывает

    несколько экранов внутри одной транзакции public void newChain(Screen... screens) { ... } // Очищает стек и открывает несколько экранов внутри 1-й транзакции public void newRootChain(Screen... screens) { ... } // Удаляет все экраны из цепочки и выход (из приложения) public void finishChain() { executeCommands( new BackTo(null), new Back() ); } }
  36. Router public class Router extends BaseRouter { ... // Открывает

    несколько экранов внутри одной транзакции public void newChain(Screen... screens) { ... } // Очищает стек и открывает несколько экранов внутри 1-й транзакции public void newRootChain(Screen... screens) { ... } // Удаляет все экраны из цепочки и выход (из приложения) public void finishChain() { executeCommands( new BackTo(null), new Back() ); } // Возвращает на предыдущий экран в цепочке public void exit() { executeCommands(new Back()); } }
  37. CommandBuffer class CommandBuffer implements NavigatorHolder { private Navigator navigator; private

    Queue<Command[]> pendingCommands = new LinkedList<>(); @Override public void setNavigator(Navigator navigator) { this.navigator = navigator; ... } @Override public void removeNavigator() { this.navigator = null; } void executeCommands(Command[] commands) { if (navigator != null) { navigator.applyCommands(commands); } else { pendingCommands.add(commands); } } }
  38. CommandBuffer class CommandBuffer implements NavigatorHolder { private Navigator navigator; private

    Queue<Command[]> pendingCommands = new LinkedList<>(); @Override public void setNavigator(Navigator navigator) { this.navigator = navigator; ... } @Override public void removeNavigator() { this.navigator = null; } void executeCommands(Command[] commands) { if (navigator != null) { navigator.applyCommands(commands); } else { pendingCommands.add(commands); } } }
  39. CommandBuffer class CommandBuffer implements NavigatorHolder { private Navigator navigator; private

    Queue<Command[]> pendingCommands = new LinkedList<>(); @Override public void setNavigator(Navigator navigator) { this.navigator = navigator; ... } @Override public void removeNavigator() { this.navigator = null; } void executeCommands(Command[] commands) { if (navigator != null) { navigator.applyCommands(commands); } else { pendingCommands.add(commands); } } }
  40. CommandBuffer class CommandBuffer implements NavigatorHolder { private Navigator navigator; private

    Queue<Command[]> pendingCommands = new LinkedList<>(); @Override public void setNavigator(Navigator navigator) { this.navigator = navigator; ... } @Override public void removeNavigator() { this.navigator = null; } void executeCommands(Command[] commands) { if (navigator != null) { navigator.applyCommands(commands); } else { pendingCommands.add(commands); } } }
  41. CommandBuffer class CommandBuffer implements NavigatorHolder { private Navigator navigator; private

    Queue<Command[]> pendingCommands = new LinkedList<>(); @Override public void setNavigator(Navigator navigator) { this.navigator = navigator; ... } @Override public void removeNavigator() { this.navigator = null; } void executeCommands(Command[] commands) { if (navigator != null) { navigator.applyCommands(commands); } else { pendingCommands.add(commands); } } }
  42. CommandBuffer class CommandBuffer implements NavigatorHolder { private Navigator navigator; private

    Queue<Command[]> pendingCommands = new LinkedList<>(); @Override public void setNavigator(Navigator navigator) { this.navigator = navigator; ... } @Override public void removeNavigator() { this.navigator = null; } void executeCommands(Command[] commands) { if (navigator != null) { navigator.applyCommands(commands); } else { pendingCommands.add(commands); } } }
  43. NavigatorHolder public interface NavigatorHolder { void setNavigator(Navigator navigator); void removeNavigator();

    } Пример: @Override protected void onResumeFragments() { super.onResume(); App.INSTANCE.getNavigatorHolder().setNavigator(navigator); } @Override protected void onPause() { App.INSTANCE.getNavigatorHolder().removeNavigator(); super.onPause(); }
  44. От теории к практике. Как использовать Cicerone? public class App

    extends Application { public static App INSTANCE; private Cicerone<Router> cicerone; @Override public void onCreate() { super.onCreate(); INSTANCE = this; cicerone = Cicerone.create(); } public NavigationHolder getNavigationHolder() { return cicerone.getNavigationHolder(); } public Router getRouter() { return cicerone.getRouter(); } } dependencies { implementation 'ru.terrakok.cicerone:cicerone:4.0.2' }
  45. От теории к практике. Как использовать Cicerone? public class App

    extends Application { public static App INSTANCE; private Cicerone<Router> cicerone; @Override public void onCreate() { super.onCreate(); INSTANCE = this; cicerone = Cicerone.create(); } public NavigationHolder getNavigationHolder() { return cicerone.getNavigationHolder(); } public Router getRouter() { return cicerone.getRouter(); } } dependencies { implementation 'ru.terrakok.cicerone:cicerone:4.0.2' }
  46. От теории к практике. Как использовать Cicerone? public class App

    extends Application { public static App INSTANCE; private Cicerone<Router> cicerone; @Override public void onCreate() { super.onCreate(); INSTANCE = this; cicerone = Cicerone.create(); } public NavigationHolder getNavigationHolder() { return cicerone.getNavigationHolder(); } public Router getRouter() { return cicerone.getRouter(); } } dependencies { implementation 'ru.terrakok.cicerone:cicerone:4.0.2' }
  47. От теории к практике. Как использовать Cicerone? public class App

    extends Application { public static App INSTANCE; private Cicerone<Router> cicerone; @Override public void onCreate() { super.onCreate(); INSTANCE = this; cicerone = Cicerone.create(); } public NavigationHolder getNavigationHolder() { return cicerone.getNavigationHolder(); } public Router getRouter() { return cicerone.getRouter(); } } dependencies { implementation 'ru.terrakok.cicerone:cicerone:4.0.2' }
  48. От теории к практике. Как использовать Cicerone? public class MainActivity

    extends AppCompatActivity { Navigator navigator = SupportAppNavigator(this, R.id.main_container); @Override protected void onResumeFragments() { super.onResume(); App.INSTANCE.getNavigatorHolder().setNavigator(navigator); } @Override protected void onPause() { App.INSTANCE.getNavigatorHolder().removeNavigator(); super.onPause(); } }
  49. От теории к практике. Как использовать Cicerone? public class MainActivity

    extends AppCompatActivity { Navigator navigator = SupportAppNavigator(this, R.id.main_container); @Override protected void onResumeFragments() { super.onResume(); App.INSTANCE.getNavigatorHolder().setNavigator(navigator); } @Override protected void onPause() { App.INSTANCE.getNavigatorHolder().removeNavigator(); super.onPause(); } }
  50. От теории к практике. Как использовать Cicerone? public class MainActivity

    extends AppCompatActivity { Navigator navigator = SupportAppNavigator(this, R.id.main_container); @Override protected void onResumeFragments() { super.onResume(); App.INSTANCE.getNavigatorHolder().setNavigator(navigator); } @Override protected void onPause() { App.INSTANCE.getNavigatorHolder().removeNavigator(); super.onPause(); } }
  51. От теории к практике. Как использовать Cicerone? public class MainActivity

    extends AppCompatActivity { Navigator navigator = SupportAppNavigator(this, R.id.main_container); @Override protected void onResumeFragments() { super.onResume(); App.INSTANCE.getNavigatorHolder().setNavigator(navigator); } @Override protected void onPause() { App.INSTANCE.getNavigatorHolder().removeNavigator(); super.onPause(); } }
  52. От теории к практике. Как использовать Cicerone? public class MainActivity

    extends AppCompatActivity { Navigator navigator = SupportAppNavigator(this, R.id.main_container); @Override protected void onResumeFragments() { super.onResume(); App.INSTANCE.getNavigatorHolder().setNavigator(navigator); } @Override protected void onPause() { App.INSTANCE.getNavigatorHolder().removeNavigator(); super.onPause(); } }
  53. От теории к практике. Как использовать Cicerone? public class MainActivity

    extends AppCompatActivity { Navigator navigator = SupportAppNavigator(this, R.id.main_container); @Override protected void onResumeFragments() { super.onResume(); App.INSTANCE.getNavigatorHolder().setNavigator(navigator); } @Override protected void onPause() { App.INSTANCE.getNavigatorHolder().removeNavigator(); super.onPause(); } }
  54. От теории к практике. Как использовать Cicerone? public class UsersPresenter

    extends BasePresenter<UsersView> { private Router router; public UsersPresenter() { router = App.INSTANCE.getRouter(); } public void onUserClicked(User user) { router.navigateTo(new UserScreen(user)); } public void onBackClicked() { router.exit(); } }
  55. От теории к практике. Как использовать Cicerone? public class UsersPresenter

    extends BasePresenter<UsersView> { private Router router; public UsersPresenter() { router = App.INSTANCE.getRouter(); } public void onUserClicked(User user) { router.navigateTo(new UserScreen(user)); } public void onBackClicked() { router.exit(); } }
  56. От теории к практике. Как использовать Cicerone? public class UsersPresenter

    extends BasePresenter<UsersView> { private Router router; public UsersPresenter() { router = App.INSTANCE.getRouter(); } public void onUserClicked(User user) { router.navigateTo(new UserScreen(user)); } public void onBackClicked() { router.exit(); } }
  57. От теории к практике. Как использовать Cicerone? public class UsersPresenter

    extends BasePresenter<UsersView> { private Router router; public UsersPresenter() { router = App.INSTANCE.getRouter(); } public void onUserClicked(User user) { router.navigateTo(new UserScreen(user)); } public void onBackClicked() { router.exit(); } }
  58. От теории к практике. Как использовать Cicerone? public class UsersPresenter

    extends BasePresenter<UsersView> { private Router router; public UsersPresenter() { router = App.INSTANCE.getRouter(); } public void onUserClicked(User user) { router.navigateTo(new UserScreen(user)); } public void onBackClicked() { router.exit(); } }