Сообщество - Android Developers

Android Developers

90 постов 2 043 подписчика

Популярные теги в сообществе:

15

Волна по "Честной цене" - тру приложение для Android ч.3

Здорово, пикубушники и пикабушницы.

Мы продолжаем нашу замечательную историю про обновление бесплатного приложения, придуманного единомышленниками, тут, на Пикабухе, для удобного подсчета цен в магазинах.

Напомню, давным давно мы заловились с товарищем @Stich.626 чтобы сделать единообразное и бесплатное мобильное приложение + сайт для расчета ценников в магазинах, которое решили не бросать, любить и лелеять, насколько это возможно.

0 - Что делаем сегодня?

В данном посте мы добавляем функционал сравнения ценников, смотрим отзывы пикабушников из магазинов приложений, и конечно же внедряем новые удобства на основе этих самых отзывов (иначе зачем мы их читаем). Готовы? Поехали!

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

Если вы просто хотите посмотреть программу, скачать / потыкать ее в действии, или просто посмотреть что это за штука, пожалуйста посмотрите обзорный скриншот выше, или проследуйте по ссылкам, которые я публикую без зазрения совести - потому что само приложение бесплатное, и таковым и останется:

https://play.google.com/store/apps/details?id=ru.oneclickstu...

https://www.rustore.ru/catalog/app/ru.oneclickstudio.fairpri...

1 - Разбираемся с понятием сравнения

Итак, в самом начале давайте поразмышляем, зачем вообще нам может быть полезен такой раздел со сравнением, и обусловлена ли необходимость в нем как такового (потому что у нас уже есть прекрасный список, который хоть чуть чуть, но выполняет эти задачи):

  • Когда мы сравниваем один (или несколько) ценников на товарах, обычно мы смотрим их по схожим характеристикам. Например, мы хотим узнать, какое самое дешевое молоко из представленного в магазине. Или пиво. Или хлеб. Или что вы там вечером едите :)

  • В связи с этим у нас формируется некий "паттерн" поведения пользователя в реальной жизни. Ага, я увидел пельмени по 450 рублей за 500 грамм, теперь я хочу сравнить другие пельмени, и посмотреть, что дешевле в пересчете на килограмм.

  • По такой логике наше сравнение обуславливается тем, что у нас есть заведомо известные параметры товара (раз уж взяли пельмени, то это вес в граммах и цена), а также то, что сравнивается один товар из нескольких производителей, или марок.

  • Другими словами, было бы очень удобно "на лету" подсвечивать самое выгодное предложение (каким нибудь зеленым цветом), но при этом ограничить расчеты по одному типу (к сожалению, пельмени не измеряются в литрах, а жаль)

1.1 - Макет, рисуйся!

Как обычно, перед тем, чтобы где то нашкодить сверстать, надо представить "экран" будущей активности, чтобы прикинуть ее на существующих формах, и оценить внешний вид. И конечно нужно понять где будет "точка входа" для этой самой активности, и насколько это удобно для обывателей.

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

Для этого немного немного поправим данный слой, добавим новую кнопку, и иконки для наглядности, и выделим функцию шеринга (если ей еще кто то пользуется в этом веке). Действия объясняются буквально одной строкой для вьюх типа Button:

android:drawableLeft="@drawable/back_48px"

1.2 Экран активности

Дальше интереснее. В текущем виде наше сравнение товаров, это как отдельная сущность, которая не должна нас связывать с основной функцией приложения. То есть, в данный момент мы именно сравниваем цены одинакового типа, поэтому экран тоже должен быть отдельный.

Для этого появляется активность CompareActivity со своими слоями, слушателями на физические (или виртуальные) кнопки, а также функцией передачи из одной активности в другую.

Передаем мы, напомню, сведения из последнего расчета

Экран с тестовыми сведениями для наглядности

Экран с тестовыми сведениями для наглядности

Что мы можем тут узреть:

  1. Приветственная карточка, которая повторяет функционал из главной формы, и служит напоминанием, что тут вообще происходит. Кстати, считается плохой практикой учить пользователя что делать (программа должна быть сразу интуитивно понятной), но для самых маленьких я не могу не оставить такую подсказку. И конечно, ее можно скрыть, посредством sharedPreferences, и она не будет надоедать.

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

  3. Еще одна карточка для ввода значений, и добавления их к нашему списку. Постарался уместить ее в одну строку для быстроты ввода, при этом не нарушая читабельности (везде ясно что где вводится)

1.3 Принимаем и передаем сведения

Чтобы информация о последнем расчете попала в нашу активность, ее нужно передать из предыдущей. Для этого мы назначаем отдельный метод для кнопки "Начать сравнение", который остается в главной форме:

Intent intent = new Intent(MainActivity.this, CompareActivity.class);
intent.putExtra("get_compare_price", compare_price);
intent.putExtra("get_compare_weight", compare_weight);
intent.putExtra("get_compare_type", compare_type);
intent.putExtra("get_compare_result", compare_result);
startActivity(intent);

А в следующей активности мы уже запрашиваем эти сведения в onCreate, дополнительно убеждаемся в том что запрошенные поля не пустые (чтобы не получить NullPointerException, если приложение будет выгружено из памяти), и помещаем их на форму через метод добавления первого пункта.

get_compare_price = intent.getStringExtra("get_compare_price");
get_compare_weight = intent.getStringExtra("get_compare_weight");
get_compare_type = intent.getStringExtra("get_compare_type");
get_compare_result = intent.getStringExtra("get_compare_result");

Bundle extra = intent.getExtras();
if (extra !=null) {
SetFirstCompareItem();
}

1.4. Возвращаем значения расчетов к изначальному виду

Когда мы передавали результат из одного экрана на другой, то мы поневоле изменили тип передаваемых сведений. Был double при передаче, а стал String.

Нам нужно вернуть все обратно, чтобы приложение могло сравнивать одинаковые типы данных, а также для сохранения цен (у нас остается две цифры после запятой). Если оставить типы смешанными, расчет может производится некорректно, и подсвечиваться тоже

Поэтому мы меняем типы обратно, посредством

Double.parseDouble(имя_переменной)

1.5. Считаем!

Теперь, когда мы готовы к подсчетам, выполняем стандартные действия из предыдущего экрана, ограничивая размер списка 5 элементами.

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

Способ крайне неудобный, но быстрый в написании, и если бы элементов списка было.. ну штук 50, приложение бы офигело, и сожрало всю память вашего устройства.

При этом, обновился тип для формирования цены - раньше он указывался без привязки с региону, и в некоторых местах цена была с запятой (123,45), а где то с точкой (123.45)

Если для простого списка на главной пофиг, что показывать, то для сравнения такое нельзя оставлять, так как приложение сразу вылетит, информируя нас о несопоставимых типах данных, поэтому приведем их к общему знаменателю вообще везде, оставив в качестве разделителя точку, и указав регион:

String.format(Locale.ENGLISH, "%.2f", price) + " ₽"

1.6 Новые удобства

Чтобы считать было еще удобнее, добавим метод, который после каждого добавления по кнопке (или по клавише с клавиатуры) вновь активирует текстовые поля. Вроде мелочь, а необходимости тыкать по новой в графу цены больше не будет:

private void ClearInputs() {
getInputPriceItemCompare = findViewById(R.id.getInputPriceItemCompare);
getInputWeightGRCompare = findViewById(R.id.getInputWeightGRCompare);

getInputPriceItemCompare.setText("");
getInputWeightGRCompare.setText("");

getInputWeightGRCompare.clearFocus();
getInputPriceItemCompare.requestFocus();
}

2. Новые удобства из отзывов

Так как отзывы я читаю (и даже иногда отвечаю), то будем руководствоваться сразу скриншотами. Итак, рассмотрим вопрос, о котором я раньше не думал:

Действительно, если на большом телефоне начинать тыкать в поля, которые выше клавиатуры, может быть неудобно. С заключениями соглашусь, поэтому пойдем доделаем этот момент.

Чтобы добавить такой способ на нажатие из экранной клавиатуры, воспользуемся объявлением слушателя для наших полей, которые уже есть в форме:

TextInputEditText.OnEditorActionListener

Он добавит в класс новый метод, которому мы будем говорить, что делать после нажатия определенных клавиш на клавиатуре.

К сожалению, устройств (и версий андроида) настолько много, что у одних может показываться галочка (ОК), а у кого то стрелка вперед (NEXT). Чтобы этого избежать, дайте два определим реакцию на обе клавиши:

public boolean onEditorAction (TextView v, int actionId, KeyEvent event) {
if (actionId == EditorInfo.IME_ACTION_DONE || actionId == EditorInfo.IME_ACTION_NEXT) {
AfterStart();
return true;
}
return false;
}

Тут стоить дать несколько слов о методе AfterStart(), ведь ранее мы использовали другой способ BtnStart(View view)

Как не трудно догадаться, методы сами по себе отличаются, и один унаследован (и объявлен) в слое (View), где мы верстали экран, поэтому вызвать метод, который объявлен в коде мы не можем.

Но и добавлять дополнительные строки, с присвоением кнопки в коде, и создание для нее отдельного слушателя тоже не самая хорошая практика. Ну то есть хорошая конечно, но по времени устаревшая.

Поэтому было решено сделать "костыль" - метод, объявленный через View, вызывает программный метод в коде. Все довольны, добавлено.. ну три строчки кода

3. Что по багам?

3.1 Уже передав версии в релиз, я обнаружил, что часть сведений, которые мы меняли для нового экрана остались в старом формате. Это видно по скриншоту в самом начале:

Этот тип данных поступает в формате String, поэтому часть текста будет обрезаться, если число само по себе ровное (без копеек)

А если же наоборот, передать сумму с копейками, то в новом экране покажется правильный формат, но он по все равно не тот, публикуется как String, а должен быть double

Так как эта часть кода не участвует в методе расчета, то ничего не поломается, но внутренний перфекционист недоволен, и я обязательно поправлю в будущем.

3.2 Для экрана сравнения хорошо бы добавить (в заголовке) что мы вообще сравниваем - килограммы, литры, или штуки. Думаю будет полезно, и главное наглядно

3.3. И возможно, стоит удалять элементы из списка, чтобы переписать что нибудь. Об этом я не подумал, но вы напомните в отзывах

И конечно же предлагайте свои варианты, смотрите, оценивайте, считайте.

Спасибо, что дочитали эту портянку текста, спасибо тем, кто оставляет теплые слова, а также тем, кто закидывает на пачку чипсов.

Всем бобра!

P.S. На обзорном скриншоте я ненароком засветил функцию, которую буду делать потом. Визуально она очень интересна, а сама по себе.. ну такое есть в любой программе, которыми вы могли пользоваться.

Теперь точно пока!

Показать полностью 5
3

Визуальная новелла о Русско-японской войне в стиле... фэнтези

Миноносец Яростный.
Проект начал писать как историческую ролевую текстовую игру, где пользователь выбирая варианты ответов сможет продвигаться по сюжету будучи капитаном одного из кораблей Второго Тихоокеанского флота, выдвигаясь в сторону Порт-Артур для помощи в снятии осады (спойлер: не успели).

А по мере написания игра обрастала новыми идеями, и теперь тут есть ресурсы, которые можно потерять или получить в случайных событиях, есть КНИГИ, да книги, пока две, покупка каждой из которых откроет новую ветку сюжета, отводя игрока от исторических событий к альтернативной истории.

Затем я решил добавить события типа "бой" в которых проходила бы калькуляция ресурсов команды игрока и команды NPC, с возможностью использовать карточки силы, помогающие перевесить чашу весов в сторону игрока.

да, одна из книг Некрономикон

да, одна из книг Некрономикон

В итоге игрок выходит из боя победителем, либо game over и часть накопленных очков игры он сможет использовать при очередной попытке пройти игру (тот самый рогалик). Эта идея еще не реализована, а только на бумаге.

Игра на сегодняшний день совсем не закончена, написана на 15% от начальной задумки. Проект остановился, когда я выходил на финишный этап в своей учебе. Пришлось отоложить, чтобы не провалить учебные проекты. Учебу я недавно закончил и вот зачем пишу сейчас тут на Пикабу - требуется вдохновения писать новые сюжетные арки, местами не хватает опыта правильно собрать проект такого типа.

// скажи что-нибудь на програмистовском: Стек не игровой, вместо unity стандартный набор в андроид студии для приложенек на котлин: MVVM, SingleActivity(+Fragments), Koin, Jetpack Navigation Component и тд. Почему таковы выбор стека? Учеба моя и планы на будущее связаны с продуктовой разработкой приложений, а не игровой, по этому это приложение как практика в основном направлении. На данном этапе мне понятно, что работать на таком стеке оно может и закончить его я смогу. Возможно стоит добавить dagger hilt.

Резюме: Кто хочет попробовать - скачайте и пробуйте, игра бесплатная и без рекламы. Кто шарит за разработку - можем продолжать развивать приложение в команде. Кто шарит за лирику - писать сюжетные арки тоже нужны талантливые люди) Написать мне можно на гитхабе в комментариях к проекту, или тут в комментариях. Спасибо всем, кто послушал.

скачать в плеймарет: https://play.google.com/store/apps/details?id=com.pavlovalexey.torpedo
код проекта в открытом доступе: https://github.com/AlexeyJarlax/Visual_Novel_Torpedo_boat_Fury

Показать полностью 4
49

А заблокируют ли gmail?1

А заблокируют ли gmail?

Без ютуба кое-как справимся, я вот уже практически полностью мигрировал на сервисы Яндекс, а что если заблокируют сервис gmail? Почтой их не пользуюсь, но все андроид устройства живут на этих аккаунтах.
Как быть, есть ли какое-то решение, можно ли уже как-то заранее подготовиться? Ведь вероятность такая есть, и все к этому стремится.

Показать полностью 1
8

Мое решение 3-х проблем MVx

Автор текста: Lynnfield

Итак, в прошлый раз я описал три проблемы, которыми, на мой взгляд, страдают все MVx и даже некоторые не MVx архитектуры. Если коротко, то это:

  • проблема остатка — при делении фичи на заявленные компоненты архитектуры остаётся либо «неделимая» часть фичи, либо лишние компоненты архитектуры;

  • проблема масштабирования — при расширении фичи компоненты архитектуры начинают раздуваться, что усложняет дальнейшую поддержку;

  • и проблема разрывов логики, когда из-за взаимодействия с UI логика разрывается на части, что тоже не помогает нам делать систему более цельной, предсказуемой и тестируемой.

Описание проблем это, конечно, хорошо, но вопрос в том, как их решать? Об этом я бы и хотел поразмышлять в этом тексте. Спойлер: когда я нашел решение проблемы разрывов, я понял, что оно может решить и все остальные проблемы.

❯ Проблема остатка (Remainder issue)


Первый вопрос: что делать с остатком? Все просто — взять делитель поменьше, потому что чем меньше делитель, тем меньше остаток. Этому меня еще в школе научили. Но я столкнулся с тем, что это не работает с MVx архитектурами, потому что мой делитель, обычно, это набор определенных компонент, и введение новых — значит изменение архитектуры.

Возможно и вы с этим сталкивались, когда вводили всякие мапперы, делегаты, интеракторы (те, что репозитории репозиториев) и прочее. Помогли ли они мне? Нет. Лучшее решение, что я видел — это Flux- и ELM-like архитектуры, которые заявляют «чистую» функцию как единицу деления логики, но со всеми вытекающими отсюда удобствами и следующими за ними «эффектами».

Но решение проблемы остатка, даже если бы оно у меня было, не помогает мне решить проблему масштабирования.

❯ Проблема масштабирования (Scalability issue)


В прошлый раз я упоминал «интуитивный» подход к решению задачи масштабирования и рассказывал почему он не работает. По крайней мере не у меня.

А какой не интуитивный?

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

Так и в MVx архитектурах: можно было бы попробовать реализовывать доработки отдельно, а уже потом объединять их с существующей фичей, вместо того, чтобы вносить изменения в уже написанные компоненты. Что в прошлом не раз приводило меня к череде переписываний тестов, судорожному протыкиванию приложения на предмет того, что ничего не поменялось, и мольбам о том, чтобы очередной баг-репорт был не по моим изменениям.Но вот что я заметил, ведь именно такой подход, когда мы предпочитаем композицию изменениям, я и мои коллеги используем для Data-слоев. Например, новые источники данных оборачиваются в Репозитории, а потом комбинируются в Интеракторы. Но почему-то чем ближе мы подходим к UI-слою, тем больше начинаем изменять, а не комбинировать.

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

Такой подход, где мы предпочитаем покомпонентную композицию фичи и доработок, вместо прямых изменений какого-нибудь компонента, действительно будет не интуитивным, потому что потребует реализовывать доработки как самостоятельную фичу. Я имею ввиду, что надо будет имплементировать доработки используя наш архитектурный подход, а потом написать еще одну пачку компонент, которая уже будет склеивать существующий функционал с новым. Но каким бы правильным мне это не казалось, я никак не могу отделаться от мысли, что такой подход приведет к написанию большого количества кода, который на первый взгляд будет казаться бесполезным.

Еще мне показалось интересным, что тут нам может помешать проблема остатка, которая по идее должна привести к ситуации, когда такой подход не сработает, потому что надо будет внедрить доработки в “середину” компонента из уже существующей фичи, а значит придется делить существующие компоненты на новые, более мелкие. Чем крупнее делитель, тем крупнее остаток, да?

В итоге, даже использовав “неинтуитивный” подход к масштабированию, я все-равно не могу до конца понять как решить проблему масштабирования. А между прочим еще остается проблема разрывов.

❯ Проблема разрывов (Gaps issue)


И вот тут становится интересно. Все дело в Hello World. Мне все никак не дает покоя вопрос: какая у него архитектура?

Hello world


Я видел примеры Hello World в разных языках, фреймворках и архитектурах (кроме Open GL, конечно же), и у них не было проблем с его реализацией. Если не считать проблемой то, сколько усилий надо приложить, чтобы написать изначальный шаблон. Но, если результат одинаковый, не значит ли это, что разница только в том, сколько обвязок надо написать, чтобы Hello World работал? И нужны ли они? Тогда я стал думать: а что общего у всех этих реализаций Hello World в разных архитектурах? И как-то я пришел к мысли, что скорее всего правильный ответ — Алгоритм. И он до безобразия тривиален.

И что интересно, у самого алгоритма нигде не написана архитектура в которой он должен быть имплементирован. Но это Hello World. Как я и сказал: он чересчур прост.

Более интересные примеры


Давайте лучше взглянем на следующий пример, который используют в учебниках по программированию — Hello %username%. У него все та же проблема с архитектурами — его можно написать в любой из них, и общее между всеми реализациями в разных архитектурах — алгоритм.

А вот еще интересное наблюдение: если мы немного обобщим алгоритм Hello World, отделив show от Hello World, то увидим, что он дважды появляется в алгоритме этого примера.

Все еще слишком просто, правда? Следующий учебный пример — работа со структурами данных. И в самом простом виде — это CRUD плюс “показать все” с хранением в списке (он же List). Этот пример, не очень интересный с точки зрения реализации, интересен тем, что он добавляет в предыдущий алгоритм композицию.

По сути здесь мы первый раз сталкиваемся с тем, что нам надо создать пять независимых программ, а потом объединить их под управлением шестой. А еще эти шесть программ делят между собой один блок памяти — сам список структур. И мне кажется, что это уже напоминает решение одной из наших проблем, не так ли?

Появление Gaps issue


Но что происходит даже с этими простыми программами, когда мы пытаемся перенести их в UI-среду?

Легче всего Hello World, потому что он просто обрастает кучей компонент, которые помогают ему “жить” в новой среде. Даже не интересно.

А вот Hello %user name% приходится куда сложнее. Беднягу размазывает по компонентам системы или архитектуры: в одном месте мы слушаем ввод имени, в другом показываем приветствие, а в третьем прописываем реакцию на введенное имя.

Я даже боюсь говорить о том, что же происходит с CRUD-примером. В зависимости от того, какой макет нам нарисуют, мы будем писать совершенно разные приложения. Вот представьте, что вас попросили сделать такую программу как несколько разных экранов, а потом попросили переделать так, чтобы это был один экран. С часто используемым подходом к декомпозиции, когда один экран — один набор MVx-компонент, мы получим бессонную ночь переписывания кода, потому что части нашей логики разорваны и раскиданы по всей реализации.


Но ведь изначально “не было ни единого разрыва”, а алгоритм остался тем же. Почему все стало так плохо?

Причина — асинхронность


На этот вопрос некоторые уже дали ответ в комментариях к предыдущим статье и видео, и я с ними полностью согласен. Причина — асинхронность. И я был искренне удивлен, когда пришел к этому выводу.

Многие, если не все GUI-системы построены вокруг event loop, потому что нам надо одновременно и экран рисовать, и ввод от пользователя слушать. А чтобы сюда добавить еще и наш алгоритм, его придется разделить так, чтобы он хорошо встраивался в этот event loop.

Я уже не говорю о том, что мы вообще-то еще должны взаимодействовать с другими асинхронными системами. Кстати, с ними то, обычно, проблем и не возникает. А почему?

Решение — закрытие разрыва

Обратим внимание на графикоподобную картинку, на которой я объяснял проблему разрывов в прошлый раз.

Напомню как всё было, и в этот раз уже не буду лукавить: путь нашей логики начинается в каком-то из callback’ов, а не в абстрактном “начале”. По мере выполнения мы продвигаемся все глубже по стеку вызовов, выполняем одну за другой функции, и в самой верхней точке нашего графика мы обращаемся к источнику данных: бэкенду, файлу, какой-то системе хранения. И что здесь обычно находится?

Обычно это вызов какой-то “асинхронной” функции: корутины, async- или suspend- функции, уж простите мой котлинский, или какой-то функции с callback’ом, или функции возвращающей какой-нибудь Future, Promise или Single.

И вот вопрос: вызывая эту функцию с callback’ом, как часто мы задумываемся, что эта операция может вообще никогда не вернуться в этот callback? Лично я до недавнего времени считал, что управление гарантированно будет передано в наш callback. Не считая случаев “отмены”, конечно же. Но откуда у нас такая гарантия? Возможно все дело в реализации системы? Давайте “заглянем под капот” и посмотрим что же там на самом деле происходит.

Наша функция формирует наш запрос к базе, запрос в сеть или еще что-то. В общем случае формирует какой-то контекст, с помощью которого надо выполнить запрос, и вместе с callback’ом, в который надо вернуть результат, отправляет его на другой поток, и там происходит все выполнение. После завершения выполнения, тот поток вызывает callback, передавая в него результат, что для нас, разработчиков, выглядит как своего рода возврат в точку вызова. Таким образом логика выглядит более цельной, и такого разрыва, как в случае с UI не происходит.

Так вот вопрос: а почему бы нам не повторить этот же трюк с UI?

На картинке это будет выглядеть как параллельный перенос: мы просто поднимем нашу линию, а вот тут, в нижней точке, вместо того чтобы разрывать логику и отдельно задавать на UI какое-то состояние и callback’и, сделаем функцию, которая отправляет запрос на UI и ждет от него ответа. Таким образом мы закрываем разрыв и теперь наша логика будет выглядеть как единое целое.

Ничего сложного, мы даже не изобретаем что-то новое, но такой подход поможет нам взаимодействовать с UI, как с любой другой внешней системой, а не как с чем-то особенным. И вот моё видение того, как это могло бы работать и решать проблемы, описанные выше.

❯ Proposal


Давайте писать функции…

Да, может звучать нелогично, тем более, что я уже говорил о том, что это не помогает Flux- и ELM-like архитектурам, но я объясню. Начнем с проблемы остатка.

Влияние на решение проблемы остатка


Добавлю небольшую аналогию с математикой. Как я и говорил «архитектура» — это делитель. А чтобы при делении не оставалось остатка — нам нужно найти наибольший общий делитель. Чем и является функция, на мой взгляд. Чем больше я смотрю на реализации всех наших «архитектур», тем больше я вижу, что все они, по сути, просто один из способов удобнее объединять и специализировать функции (и тут я подразумеваю, что метод класса — это функция, которая иногда неявно принимает дополнительный аргумент).

Дальше лучше — проблема масштабирования.

Решение проблемы масштабирования


Функции очень хорошо масштабируются, потому что, с точки зрения реализации, они могут вмещать в себя любой набор инструкций и в том числе вызовы других функций, а с точки зрения пользователя функции это всегда просто вызов функции: имя, параметры, результат, независимо от того как она реализована.

А что с разрывами?

Решение проблемы разрывов


До тех пор, пока мы будем поддерживаем нашу логику, как композицию функций, проблема разрывов будет естественным образом «выталкиваться наружу», за пределы нашей логики. А это в свою очередь будет гарантировать нам последовательность выполнения, тестируемость и как следствие — стабильность.

❯ Концепт


Так что же я предлагаю?

Во-первых, реализовывать логику, как функции и их композицию, а не как компоненты какой-нибудь архитектуры. Это позволит нам гарантировать и поддерживать последовательность выполнения, позитивно скажется на масштабируемости и тестируемости нашего кода, да и понимать такой код будет проще.

Во-вторых, у этой функции (композиции функций) должна быть возможность работать независимо от «сторонних» систем, поэтому я предлагаю вынести ее в свой поток, чтобы у нее была возможность спокойно выполняться, блокироваться при необходимости, параллелиться и тому подобное. Пусть будет “синхронной”. Обычно мне не надо, чтобы логика продолжала свое выполнение, когда она ждет какой-то ресурс. А если такое поведение нужно, то почему-бы его не описать его явно? И назвать бы этот поток Main, но имя уже “занято”. =)

В-третьих, хотя это и самый важный пункт, а у меня видимо есть тенденция оставлять все важное на потом, я предлагаю сосредоточиться на реализации логики приложений, а не экранов и виджетов. Как видите, с таким подходом нет разницы между слоем данных и пользователем. Есть только наша детерминированная логика и внешние системы, которые обмениваются данными через нее. Логика просто ходит между ними и предоставляет им некий контекст для принятия решения и набор возможных действий, а внешние системы в свою очередь “отвечают” нашей логике руководствуясь представленным контекстом. Это похоже на игру в шахматы, где внешние системы — игроки, а логика — доска с фигурами и правила игры.

Логика наших фич зачастую не зависит от представления, а как раз наоборот: представление является способом, который помогает логике взаимодействовать с пользователем в определенной среде, как какой-нибудь адаптер. Я думаю, что такой адаптер должен быть плагином к логике, а не фактором определяющим ее.

В конце концов, применив этот концепт, я хочу помочь всем нам проектировать и писать кроссплатформенные приложения в самом широком смысле этого слова: разрабатывая и реализуя end-to-end алгоритмы которые прозрачно перескакивают с бэкенда на фронт и обратно, которые не видят различия между разными видами фронтов, будь то Web, iOS или Android, или разными типами вроде UI, CLI, или TalkBack.

Но пока это только концепт и виденье. Пожалуй пора посмотреть на код, но он будет в следующий раз. А сейчас есть время порефлексировать на эту тему. Дайте этим идеям время перевариться. Прочитайте еще раз. Задайте вопросы. И может вы сможете написать код еще до того, как я опубликую продолжение. Хотите посоревноваться?

Увидимся.

Показать полностью 14
1

Опрос разработчиков: Вам это нужно ?

Опрос разработчиков: Вам это нужно ?

Пишу службу печати под Android (ps402d). Нужно ли для Вас добавить способ отправить на задание принтеру сразу в мой драйвер ? Если да, то какой вариант предпочитаете ?

1) Через startActivity

2) BindService

3) Общаться через ws (вебсокет)

Показать полностью 1
9

Релиз моей игры!

Это детективная визуальная новелла. На её разработку у меня ушло чуть больше года. Но вот она закончена!

Игра называется "Меньше знаешь". Её свободно можно найти в Google play.
https://play.google.com/store/apps/details?id=thelessyouknow...
Описание сюжета игры:

"В городе происходят серийные убийства мужчин. Единственное, что связывает преступления - алкоголь. Жертвы, доверяя убийце, приводят его в свой дом.

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

------------------------------------------------------------------

• Игра бесплатная, но вышла лишь для устройств Android
• Донатов нет
• Монетизации тоже пока нет.
• Игру создавала одна без команды.

Стараюсь пиарить игру как могу. Денег на рекламу пока нет, поэтому всё своими силами.

Показать полностью 4

Кто разбирается в мобильных приложениях, подскажите

Некоторые приложения (типа "Мой налог" или "NetSchool"), когда жмешь вход через Госуслуги, вместо того, чтобы открыть Госуслуги и запросить, например, только смс, открывают форму полной авторизации на Госуслугах, причем, все ручками надо вбивать.
И вот вопрос: ПОЧЕМУ? Может, надо еще в настройках что-то сделать?

9

Мое первое приложение на android

Всем привет) Вот и я решил написать о своем приложении. Самое интересное никогда не понимал программирование и в школе и в универе. Но сам того не знаю в голову пришла идея попробовать сделать что-то интересное. И вот что получилось 👇👇👇

От рождения идеи до загрузки в Google Play прошло 3 месяца.

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

Хотя к этому времени уже написал код который криво но работает по поиску имен из базы. Т.е на пустом экране строка поиска с выводом информации.

Но желание закончить начатое было сильнее. И уже сначала полностью создаю примерный дизайн и основные функции и как они должны работать прямо на блокноте ручкой).

А когда понимаешь что должно быть в конце и писать код стало гораздо интереснее.

А ещё сложнее оказалось сделать аккаунт разработчика в Google Play) Тот ещё квест с потерей денег и времени.

Буду рад критике и за новые идеи.

Ссылка на приложение: https://play.google.com/store/apps/details?id=com.bashkir_ta...

Показать полностью 2 1
Отличная работа, все прочитано!