Представляем курс по архитектуре клиент-серверных андроид-приложений на основе материалов курса Артура Василова, который проходил на Google Developers Group 2016  в Казани.

Введение в архитектуру android приложений

Прежде чем приступить непосредственно к изучению способов построения архитектуры клиент-серверных Android-приложений, было бы хорошо узнать, почему вообще эта тематика важна.

И этот вопрос логичен. Во-первых, пользователю совсем-совсем безразлична архитектура вашего приложения. Серьезно, кто из вас при использовании программ и приложений часто задумывается о том, сделали его по MVP или MVC? Ответ – никто. Во-вторых, работа с архитектурой требует дополнительных усилий: ее нужно создавать, в нее нужно вникать и учить людей работать по ней. Но чтобы создать более четкую картину для ответа на этот вопрос, нужно вернуться в относительно недалекое прошлое, а именно в 2007 и 2008 года, когда были выпущены соответственно первые версии устройств под iOS и Android.

Нужно признать, что Google успел отстать от Apple в плане выхода на мобильный рынок и это привело к некоторым последствиям, а именно к спешке при выходе первой версии Android. Нет ничего удивительного в том, что Google стремился в первую очередь доделать основные пользовательские функции, а забота об удобстве разработчиков была вторым приоритетом. Поэтому вместе с первой версией Android Google не предоставил разработчикам каких-то стандартных рекомендаций ни по разработке, ни по дизайну и UX. Что привело к тому, что каждый разработчик или каждая компания были вынуждены писать как хотели и как умели.

Конечно, нужно признать, что в дальнейшем Google проделал огромную работу по популяризации системы Android среди разработчиков. Но при этом изначальные проблемы так и не были до конца решены.

Какие же это проблемы? В плане дизайна понятно, что абсолютно разные стили приложений смущают пользователя системы Android, и ему бывает тяжело ориентироваться. Но что не так с отсутствием стандартов в самом коде? Ведь как было сказано чуть выше, пользователь никак не может знать, насколько хорош код вашего приложения, и это не влияет на его использование. Проблема заключается в том, что не все разработчики хорошо владеют паттернами проектирования и умеют разрабатывать хорошую архитектуру приложений. Если же не следовать четким принципам в архитектуре вы очень скоро получите код, который:

  • Невозможно поддерживать. В коде будет много сложной логики, она не будет расположена в строго определенных классах, будет непонятно, как работает та или иная часть вашего приложения. Из этого следует, что при добавлении нового функционала вам придется либо долго и усиленно разбираться в написанном коде, рефакторить его и делать все правильно, либо сделать задачу кое-как, то есть, образно говоря, через костыли. В связи с тем, что не все разработчики понимают необходимость рефакторинга и умеют убеждать в этой необходимости руководство, и не каждый руководитель согласится отсрочить выход новой версии и понести дополнительные траты из-за рефакторинга, намного чаще выбирается второй вариант. Это часто приводит к ужасающим последствиям. Лично мне не раз приходилось видеть огромные приложения, состоящие из трех файлов Activity. Разумеется, каждая из этих Activity состояла из тысяч, а то и из десятков тысяч строк, что делало их абсолютно невозможными для чтения. Более того, каждый новый функционал, реализованный через костыли, является причиной дополнительных багов и крашей.
  • Невозможно протестировать. Эта проблема плавно вытекает из первой. Вы не сможете писать модульные тесты, если все приложение – это один большой модуль. Более того, в силу особенностей написания тестов для Android-приложений на JVM, при большом количестве зависимостей от классов Android в тестируемых классах, вы не сможете писать тесты. А отсуствие тестов:
    1. Дает вам гораздо меньше уверенности в том, что ваш код работает правильно.
    2. Вы не сможете быстро проверить, что добавленные изменения не сломают работу остальных частей вашего приложения.

Такая ситуация продолжалась достаточно долго. Приложения под Android продолжались писаться в разных стилях с абсолютно разными подходами в дизайне и в архитектуре. Кто-то брал дизайн из системы iOS, а паттерны проектирования из Web-разработки (в частности, попытки использовать MVC в Android обязаны своему существованию именно Web-разработчикам, перешедшим в Android). И сложно сказать, почему не было никаких попыток исправить эту ситуацию, Android – это очень молодая система, и к моменту ее выхода все паттерны проектирования и архитектурные паттерны уже были широко известны.

В общем, все шло своим чередом до 2014 года, когда случилось сразу два важнейших события. Первое хорошо известно всем – это презентация концепции Material Design на Google I/O. Можно по-разному относиться к этой концепции, кто-то считает ее неудачной, кто-то говорит, что таким образом Google ограничивает свободу разработчиков в выборе дизайна. Но то, что появление этой концепции сильно улучшило ситуацию в среднем, – это бесспорно.

Понятно, что за конференцией Google I/O следят все и что Google приложил немало усилий в популяризации философии Material Design, так что Material Design был обречен на использование всеми. А вот другое знаковое событие произошло куда с меньшей популярностью, так как это была всего лишь статья. Это статья “Architecting Android… The clean way?” от Fernando Cejas. По сути эти всего лишь адаптация принципов Clean Architecture от “дядюшки Боба” (Роберта Мартина) для использования в Android. Эта статья дала огромный толчок (а вполне возможно, что это просто совпадение и статья вышла в тот момент, когда разработчики уже были готовы искать лучшие решения) в развитии архитектуры приложений.

Fernando Cejas

Если говорить кратко (а подробнее мы посмотрим дальше по курсу), то хорошая архитектура должна позволять писать тесты для классов, содержащих бизнес-логику и должна строить модули приложения независимыми от почти всех внешних элементов. А если говорить еще проще, то ваш код должен быть тестируемым и его должно быть легко применять и приятно читать. Качество кода приложения можно даже замерить стандартной единицей измерения – количество WTF в минуту (из книги Роберта Мартина “Clean Code”).

Теперь мы можем примерно представить, что от нас требуется при построении архитектуры приложения и можем перейти непосредственно к рассмотрению всех тем!

Основные задачи при разработке клиент-серверных приложений

Так в чем же заключается сложность создания клиент-серверных Android-приложений, которые бы удовлетворяли всем принципам, которые были описаны ранее? Есть 2 крупные проблемы, каждую из которых на самом деле можно разбить еще на большее число проблем:

  • Реализация клиент-серверного взаимодействия. Казалось бы, в чем здесь проблема? Мы все умеем выполнять запросы к серверу с использованием различных средств, обрабатывать результат и показывать его пользователю. И да, и нет. Здесь существует масса факторов. Во-первых, нужно уметь корректно обрабатывать ошибки, которые могут быть самыми разными: от отсутствия интернета и неправильных параметров в запросе, до не отвечающего сервера и ошибках в ответе. Во-вторых, в вашем приложении может быть не один запрос, а много, и вполне возможна ситуация, что вам придется комбинировать результаты этих запросов сложным образом: выполнять их параллельно, использовать результат предыдущего запроса для выполнения следующего и так далее. В-третьих, и это самое неприятное – запросы могут занимать значительное время, а пользователь часто не самый терпеливый и тихий человек – он может крутить устройство (и тогда вы потеряете текущие данные в Activity), а может и вовсе закрыть приложение, и тогда вы можете получить рассинхронизацию в данных (когда на сервере данные обновились, а приложение не знает об этом и отображает некорректную или устаревшую информацию). И все это нужно каким-то образом решать.
  • Обеспечение возможности тестирования классов, содержащих бизнес-логику приложения. Это также подразумевает под собой немало внутренних проблем. Во-первых, нужно обеспечить модульность классов. Это следует из самой сути и из самого названия Unit-тестов. Чтобы обеспечить модульность, нужно разделять классы по логическим слоям для каждого экрана. То есть вместо того, чтобы писать весь код, относящийся к одному экрану, в одной активити, нужно грамотно разделить его на несколько классов, каждый из которых будет иметь свою зону ответственности. Во-вторых, если говорить о тестах с помощью JUnit, то нужно понимать, что тестируемые таким образом классы должны содержать минимальное количество зависимостей от Android-классов, так как Java и ее виртуальная машина об этих классах не знает ничего (подробнее этот момент будет описан в лекции про тестирование). В-третьих, самая сложная логика приложения почти всегда связана с работой с данными от сервера. Мы должны протестировать различные возможные ситуации, такие как ожидаемый ответ сервера, ошибка сервера и разные ответы, приводящие к разному поведению приложения. Но при выполнении теста мы не можем по своему желанию “уронить” сервер или заставить его отдать нужные нам данные. К тому же, серверные запросы выполняются долго и асинхронно, а тесты должны работать последовательно. Все эти проблемы можно решить, если подменять реализацию сервера на определенном слое, к которому будут обращаться тестируемые классы. Все это также будет рассмотрено далее.

Эти проблемы и приходится решать при создании грамотной и правильной архитектуры, и это всегда не очень просто. Более того, иногда невозможно добиться желаемого результата, и у каждого способа есть как свои недостатки, так и достоинства. Рассмотрением всех этих способов мы и будем заниматься на протяжении курса, и после вы сможете решить, как именно вы хотите разрабатывать клиент-серверные приложения.

Продолжение:

Часть 2 Лекции 1 Курса по архитектуре андроид-приложений

Лекция 1. Введение в архитектуру клиент-серверных андроид-приложений. Часть 1 обновлено: Август 16, 2017 автором: admin

Добавить комментарий