[:ru]Представляем курс по архитектуре клиент-серверных андроид-приложений на основе материалов курса Артура Василова, который проходил на 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 Курса по архитектуре андроид-приложений

[:en]We present the course on the architecture of client-server android applications based on the materials of the course of Arthur Vasilov, which was held at Google Developers Group 2016 in Kazan.

Introduction to the architecture of android applications

Before proceeding directly to the study of how to build the architecture of client-server Android-applications, it would be good to know why this topic is important at all.

And this question is logical. First, the user is completely indifferent to the architecture of your application. Seriously, how many of you, when using programs and applications, often think about whether they did MVP or MVC? The answer is no one. Secondly, working with architecture requires additional efforts: it needs to be created, it needs to delve into and teach people how to work on it. But in order to create a clearer picture for answering this question, we need to return to the relatively recent past, namely in 2007 and 2008, when the first versions of iOS and Android devices were released, respectively.

We must admit that Google managed to lag behind Apple in terms of entering the mobile market and this led to some consequences, namely to the rush when the first version of Android was released. There is nothing surprising in that Google aspired first of all to finish the basic user functions, and care of convenience of developers was the second priority. Therefore, along with the first version of Android, Google did not provide developers with any standard recommendations for either development, or design and UX. That led to the fact that every developer or every company was forced to write as they wanted and as they could.

Of course, we must admit that in the future Google did a great job of popularizing the Android system among developers. But at the same time the original problems were not completely solved.

What are these problems? In terms of design, it is clear that completely different styles of applications confuse the user of the Android system, and it can be difficult to navigate. But what’s wrong with the lack of standards in the code itself? After all, as it was said just above, the user can not know in any way how good the code of your application is, and this does not affect its use. The problem is that not all developers have a good knowledge of design patterns and are able to develop a good application architecture. If you do not follow clear principles in architecture, you will very soon receive a code that:

  • Unable to support. The code will have a lot of complex logic, it will not be located in strictly defined classes, it will be unclear how this or that part of your application works. From this it follows that when you add a new functional, you have to either long and hard to understand the written code, refactor it and do it right, or to do the task somehow, that is, figuratively speaking, through crutches. Due to the fact that not all developers understand the need for refactoring and are able to convince the leadership of this need, and not every manager agrees to delay the release of the new version and incur additional expenses due to refactoring, the second option is much more often chosen. This often leads to horrific consequences. Personally, I have repeatedly seen huge applications consisting of three Activity files. Of course, each of these activities consisted of thousands, and even tens of thousands of lines, which made them absolutely impossible to read. Moreover, every new functional implemented through crutches is the cause of additional bugs and crashes.
  • It is impossible to test. This problem flows smoothly from the first. You will not be able to write unit tests if the entire application is one large module. Moreover, due to the peculiarities of writing tests for Android-applications on JVM, with a large number of dependencies from Android classes in the tested classes, you can not write tests. And the absence of tests:
    1. Gives you much less confidence that your code is working correctly.
    2. You will not be able to quickly check that the added changes do not break the work of the rest of your application.

This situation lasted long enough. Applications for Android continued to be written in different styles with completely different approaches to design and architecture. Someone was taking a design from the iOS system, and design patterns from Web development (in particular, attempts to use MVC in Android owe their existence to the Web developers who have switched to Android). And it’s hard to say why there were no attempts to fix this situation, Android is a very young system, and by the time of its release all the design patterns and architectural patterns were already widely known.

In general, everything went on as usual until 2014, when two important events happened at once. The first is well known to everyone — this is the presentation of the Material Design concept on Google I / O. You can treat this concept differently, someone considers it unsuccessful, someone says that in this way Google restricts the freedom of developers in choosing a design. But the fact that the emergence of this concept greatly improved the situation on average, is unquestionable.

It’s clear that the Google I / O conference is being monitored by everyone and that Google has put a lot of effort into popularizing the Material Design philosophy, so that Material Design was doomed to be used by everyone. But another significant event happened with less popularity, as it was just an article. This article is «Architecting Android … The clean way?» By Fernando Cejas. In fact, these are just an adaptation of the principles of Clean Architecture from «Uncle Bob» (Robert Martin) for use in Android. This article gave a huge push (and it is quite possible that this is just a coincidence and the article came out at a time when developers were already ready to look for better solutions) in the development of application architecture.

Fernando Cejas

To put it briefly (and we will take a closer look at the course), a good architecture should allow writing tests for classes containing business logic and should build application modules independent of almost all external elements. And if to speak even easier, your code should be testable and it should be easy to apply and pleasant to read. The quality of the application code can even be measured by the standard unit of measure — the amount of WTF per minute (from Robert Martin’s book “Clean Code”).

Now we can roughly imagine what is required of us when building the application architecture and can go directly to the consideration of all topics!

The main tasks in the development of client-server applications

So what is the complexity of creating client-server Android-applications that would satisfy all the principles that were described earlier? There are 2 major problems, each of which in fact can be broken down into even more problems:

  • Implementation of client-server interaction. It would seem, what is the problem here? We all know how to execute requests to the server using various tools, process the result and show it to the user. Yes and no. There are many factors. First, you need to be able to correctly handle errors, which can be very different: from the absence of the Internet and the wrong parameters in the query, to the non-responding server and errors in the response. Secondly, there can be more than one query in your application, and a lot, and it is quite possible that you will have to combine the results of these queries in a complex way: execute them in parallel, use the result of the previous query to perform the next and so on. Thirdly, and this is the most unpleasant — requests can take a considerable time, and the user is often not the most patient and quiet person — he can twist the device (and then you lose the current data in Activity), and maybe completely close the application, and then you You can get desync in the data (when the data on the server is updated, and the application does not know about it and displays incorrect or outdated information). And all this needs to be solved in some way.
  • Provide the ability to test the classes that contain the business logic of the application. It also implies a lot of internal problems. First, you need to ensure the modularity of classes. This follows from the very essence and from the very name of Unit-tests. To ensure modularity, you need to separate classes into logical layers for each screen. That is, instead of writing all the code pertaining to one screen in one activity, it is necessary to divide it competently into several classes, each of which will have its own zone of responsibility. Secondly, if we talk about tests using JUnit, then we need to understand that the classes tested in this way should contain the minimum number of dependencies from Android-classes, since Java and its virtual machine know nothing about these classes (in more detail this moment will be described In a lecture about testing). Third, the most complex application logic is almost always associated with working with data from the server. We need to test various possible situations, such as the expected server response, server error, and various responses that lead to different behavior of the application. But when performing the test, we can not «drop» the server at will or force it to give us the data we need. In addition, server requests are executed long and asynchronously, and the tests must work consistently. All these problems can be solved if you substitute the implementation of the server for a certain layer, which will be accessed by the tested classes. All this will also be considered below.

These problems also have to be solved when creating a competent and correct architecture, and it is always not very simple. Moreover, sometimes it is impossible to achieve the desired result, and each method has its own shortcomings and advantages. We will study all these methods throughout the course, and after that you will be able to decide exactly how you want to develop client-server applications.

Continuation:

Part 2 Lectures 1 Course on the architecture of android applications

[:]

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