Разработка BytePace
44

Parse & Android: рекомендации начинающим разработчикам

В данной статье Android-разработчик BytePace, Андрей Ващенко, хотел бы поделиться общими впечатлениями от использования BaaS — решения под названием Parse для разработки бэкэнда Android-приложения, рассказать о всех «подводных камнях», с которыми пришлось столкнуться в период разработки.

В закладки

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

Андрей Ващенко
Android-разработчик

Проблема №1: Использование Parse Server в связке с PostgreSQl

Использование данной конфигурации было обусловлено тем, что сервер был развернут на VDS-хостинге, и использование удаленной базы MLab было нецелесообразно, ибо на момент разработки Роскомнадзор пытался блокировать Telegram в России, и были проблемы с соединением без VPN. Настраивать VPN на консольной Linux времени не было, а проект горел, поэтому решил использовать локальную базу на сервере. Я выбрал PostgreSQL, потому что с ней у меня был хороший опыт работы.

Лайфхак № 1: чтобы база работала без ошибок в типах данных, при установке postgres нужно установить postgis. После чего необходимо создать базу и сразу же после создания создать подключить все расширения postgis. О том, как подключить расширения postgis к базе, можно почитать тут. После того, как все расширения подключены, можно подключать базу к серверу, открыть dashboard и посмотреть, что таблицы созданы без ошибок.

Лайфхак № 2: используйте версию Parse server >= 2.7.2. Когда я скачал тестовый проект с Git, там был сервер версии 2.2.5, и вроде всё работало, но впоследствии вылез один баг: при сохранении координат геолокации lat и lng менялись местами.

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

В течение двух суток я искал решения. Чего я только не находил на разнообразных форумах и в github issues: переворот координат в Cloud-функции (не работает!); переворот координат в PostgresStorageAdapter (после изменений возникло много ошибок). На следующий день заглянул в релизы и увидел, что в версии 2.7.2 был пофикшен баг в PostgresStorageAdapter. Быстро исправил версию в package. json, и «о, чудо!», всё заработало как надо.

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

Проблема №2: Не отписываются LiveQuery

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

Изначально архитектура была примерно такой:

public class Subclass extends ParseObject { // init columns, create getters & setters } public class QueryHelper { public static ParseQuery getQuery(Param... params) { ParseQuery query = ParseQuery.getQuery(Subclass.class); // init query by params return query; } } public class MainActivity extends Activity { public void onResume() { ParseLiveQueryClient.getClient().subscribe(QueryHelper.getQuery(), callback); } public void onPause() { ParseLiveQueryClient.getClient().unsubscribe(QueryHelper.getQuery(), callback); } }

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

В методе подписки возвращается объект Subscriber, но этот объект абсолютно бесполезен, потому что метода «отписаться» он не содержит, и сам LiveQueryClient не содержит метода «отписаться» с параметром Subscriber. Включив дебаг, начал пошагово заходить в тот самый метод «отписаться». В самом клиенте приватно хранится лист подписок. В методе разработчики проходятся по этому листу циклом и сравнивают запрос из параметра с непереопределенной функцией equals, которая соответствует обычному ==, и которая сравнивает адреса у сложных объектов. Запрос хранится в объекте подписки.

И это всё объясняло. В моем проекте был класс с функциями, который создавал мне нужный запрос. И так как объект запроса всегда создавался заново, следовательно, адреса у запросов были разные, equals не срабатывал, и отписка не происходила. Решил я эту проблему следующим образом: сделал singleton, и всё заработало.

Выглядеть это стало примерно так:

public class Subclass extends ParseObject { // init columns, create getters & setters } public class QueryHelper { public static final ParseQuery query; public static ParseQuery getQuery(Param... params) { if (query == null) { query = ParseQuery.getQuery(Subclass.class); // init query by params } return query; } } public class MainActivity extends Activity { public void onResume() { ParseLiveQueryClient.getClient().subscribe(QueryHelper.getQuery(), callback); } public void onPause() { ParseLiveQueryClient.getClient().unsubscribe(QueryHelper.getQuery(), callback); } }

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

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

Больше статей на:

Материал опубликован пользователем. Нажмите кнопку «Написать», чтобы поделиться мнением или рассказать о своём проекте.

Написать
{ "author_name": "BytePace", "author_type": "self", "tags": [], "comments": 0, "likes": 1, "favorites": 0, "is_advertisement": false, "subsite_label": "dev", "id": 79141, "is_wide": false, "is_ugc": true, "date": "Tue, 13 Aug 2019 14:43:43 +0300" }
{"average":26040,"one":95,"ten":75}
Сколько денег вы откладываете в месяц?
Ответьте и узнаете, сколько копят другие.
0 ₽
70 000+ ₽
0 ₽
{ "id": 79141, "author_id": 331975, "diff_limit": 1000, "urls": {"diff":"\/comments\/79141\/get","add":"\/comments\/79141\/add","edit":"\/comments\/edit","remove":"\/admin\/comments\/remove","pin":"\/admin\/comments\/pin","get4edit":"\/comments\/get4edit","complain":"\/comments\/complain","load_more":"\/comments\/loading\/79141"}, "attach_limit": 2, "max_comment_text_length": 5000, "subsite_id": 235819, "last_count_and_date": null }
Комментариев нет

Популярные

По порядку

0
{ "page_type": "article" }

Прямой эфир

[ { "id": 1, "label": "100%×150_Branding_desktop", "provider": "adfox", "adaptive": [ "desktop" ], "adfox_method": "createAdaptive", "auto_reload": true, "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "bugf", "p2": "ezfl" } } }, { "id": 2, "label": "1200х400", "provider": "adfox", "adaptive": [ "phone" ], "auto_reload": true, "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "bugf", "p2": "ezfn" } } }, { "id": 3, "label": "240х200 _ТГБ_desktop", "provider": "adfox", "adaptive": [ "desktop" ], "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "bugf", "p2": "fizc" } } }, { "id": 4, "label": "240х200_mobile", "provider": "adfox", "adaptive": [ "phone" ], "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "bugf", "p2": "flbq" } } }, { "id": 5, "label": "300x500_desktop", "provider": "adfox", "adaptive": [ "desktop" ], "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "bugf", "p2": "ezfk" } } }, { "id": 6, "label": "1180х250_Interpool_баннер над комментариями_Desktop", "provider": "adfox", "adaptive": [ "desktop" ], "adfox": { "ownerId": 228129, "params": { "pp": "h", "ps": "bugf", "p2": "ffyh" } } }, { "id": 7, "label": "Article Footer 100%_desktop_mobile", "provider": "adfox", "adaptive": [ "desktop", "tablet", "phone" ], "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "bugf", "p2": "fjxb" } } }, { "id": 8, "label": "Fullscreen Desktop", "provider": "adfox", "adaptive": [ "desktop", "tablet" ], "auto_reload": true, "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "bugf", "p2": "fjoh" } } }, { "id": 9, "label": "Fullscreen Mobile", "provider": "adfox", "adaptive": [ "phone" ], "auto_reload": true, "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "bugf", "p2": "fjog" } } }, { "id": 10, "disable": true, "label": "Native Partner Desktop", "provider": "adfox", "adaptive": [ "desktop", "tablet" ], "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "clmf", "p2": "fmyb" } } }, { "id": 11, "disable": true, "label": "Native Partner Mobile", "provider": "adfox", "adaptive": [ "phone" ], "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "clmf", "p2": "fmyc" } } }, { "id": 12, "label": "Кнопка в шапке", "provider": "adfox", "adaptive": [ "desktop" ], "adfox": { "ownerId": 228129, "params": { "p1": "bscsh", "p2": "fdhx" } } }, { "id": 13, "label": "DM InPage Video PartnerCode", "provider": "adfox", "adaptive": [ "desktop", "tablet", "phone" ], "adfox_method": "createAdaptive", "adfox": { "ownerId": 228129, "params": { "pp": "h", "ps": "bugf", "p2": "flvn" } } }, { "id": 14, "label": "Yandex context video banner", "provider": "yandex", "yandex": { "block_id": "VI-223676-0", "render_to": "inpage_VI-223676-0-1104503429", "adfox_url": "//ads.adfox.ru/228129/getCode?pp=h&ps=bugf&p2=fpjw&puid1=&puid2=&puid3=&puid4=&puid8=&puid9=&puid10=&puid21=&puid22=&puid31=&puid32=&puid33=&fmt=1&dl={REFERER}&pr=" } }, { "id": 15, "label": "Плашка на главной", "provider": "adfox", "adaptive": [ "desktop", "tablet", "phone" ], "adfox": { "ownerId": 228129, "params": { "p1": "byudx", "p2": "ftjf" } } }, { "id": 16, "label": "Кнопка в шапке мобайл", "provider": "adfox", "adaptive": [ "tablet", "phone" ], "adfox": { "ownerId": 228129, "params": { "p1": "byzqf", "p2": "ftwx" } } }, { "id": 17, "label": "Stratum Desktop", "provider": "adfox", "adaptive": [ "desktop" ], "auto_reload": true, "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "bugf", "p2": "fzvb" } } }, { "id": 18, "label": "Stratum Mobile", "provider": "adfox", "adaptive": [ "tablet", "phone" ], "auto_reload": true, "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "bugf", "p2": "fzvc" } } }, { "id": 19, "label": "Тизер на главной", "provider": "adfox", "adaptive": [ "desktop", "tablet", "phone" ], "auto_reload": true, "adfox": { "ownerId": 228129, "params": { "p1": "cbltd", "p2": "gazs" } } } ]
Нейронная сеть научилась читать стихи
голосом Пастернака и смотреть в окно на осень
Подписаться на push-уведомления
{ "page_type": "default" }