Все три рассмотренные библиотеки имеют какие-либо преимущества и недостатки, Mobile Vision API обеспечивают быструю и точную проверку штрих-кода в различной ориентации камеры.
Выводы по главе
Было проведено исследования рынка приложений, направленных на помощь при выборе продуктов питания в продуктовом магазине. Проанализирован функционал существующих аналогов. В результате сформулирован состав выполняемых функций приложения. Помимо этого, описана архитектура, структура и компоненты приложений Android, с учетом которых необходимо подходить к разработке. Проведен обзор существующих библиотек для распознавания штрих-кода в Android приложениях. Произведен сравнительный анализ достоинств и недостатков каждой из них и в итоге Mobile Vision API выбрана для реализации считывания и распознавания штрих-кода.
Глава 2. Проектирование приложения
В данной главе описано проектирование архитектуры и моделей данных для решения поставленных задач.
2.1 Архитектура приложения
MVP (Model-View-Presenter) выбрана в качестве архитектуры приложения. Назначение данного архитектурного паттерна - отделение пользовательского интерфейса от данных приложения и методов их обработки (бизнес-логики). Это достигается путем введения дополнительного объекта - презентора. Диаграмма взаимодействия всех трех частей показана на рис.12.
Рисунок 12. Архитектура MVP [10]
Model - бизнес-логика приложения, полностью независима. Выполняет обращения к базе данных для получения информации.
View - отвечает за визуализацию данных, полученных от модели.
Presenter - связующее звено между моделью и представлением. Отвечает за обработку событий в представлении.
Приложение разделено на независимые блоки для работы с HTTP запросами, моделями, адаптерами списков и самих экранов.
Каждое activity является наследником базового класса BaseActivity, которое предоставляет возможность показывать всплывающие подсказки, являющиеся одинаковыми для всех экранов приложения.
Выполнение таких функций как поиск продукта в базе и сопоставление распознанного состава продукта со стоп-листом пользователей реализуется через взаимодействие с базой данных. Так как напрямую из клиента взаимодействовать с базой данных нельзя, необходимо разделить архитектуру на три уровня (рис.13).
Рисунок 13. Трехуровневая архитектура приложения
2.2 Работа с HTTP запросами
Необходимо учитывать, что при запуске приложения система создает поток, который называется основным потоком, и обрабатывает все изменения и события пользовательского интерфейса. Поэтому для того, чтобы при работе с запросами на сервер приложение не блокировалось, необходимо их обрабатывать в отдельном потоке. Для этого предусмотрен класс AsyncTask. AsyncTask содержит четыре метода, которые можно переопределить:
· Метод doInBackground(): выполняется в фоновом потоке, должен возвращать определенный результат
· Метод onPreExecute(): вызывается из главного потока перед запуском метода doInBackground()
· Метод onPostExecute(): выполняется из главного потока после завершения работы метода doInBackground()
· Метод onProgressUpdate(): позволяет сигнализировать пользователю о выполнении фонового потока
При обработке результатов запросов необходимо учесть обработку ошибок в результатах запросов, чтобы приложение не закрывалось аварийно.
Запросы реализованы с помощью REST модели, приложение обменивается с сервером данными, используя формат JSON. Для каждой единицы информации (info) определено 5 действий:
1. GET /info/ - получает список всех объектов.
2. GET /info/{id}- получает информацию о объекте.
3. PUT /info/ или POST /info/- создает новый объект.
4. POST/info/{id} или PUT /info/{id}- изменяет данные с идентификатором {id}.
5. DELETE /info/{id} - удаляет данные с идентификатором {id}.
Модель базы данных
Были выделены следующие необходимые сущности, которые необходимо хранить для реализации функций приложения (табл.6):
Таблица 6
Сущности базы данных
|
Название сущности |
Название поля |
Тип |
Описание |
|
|
User |
ID |
integer |
Пользователь |
|
|
|
varchar |
|||
|
password |
varchar |
|||
|
Ingredient |
ID |
integer |
Ингредиент |
|
|
Name |
varchar |
|||
|
Product |
ID |
integer |
Продукт |
|
|
Name |
varchar |
|||
|
Category |
varchar |
|||
|
Barcode |
varchar |
|||
|
Prod_User |
ID |
integer |
Для хранения истории поиска также необходимо учесть, что один продукт может быть просмотрен несколькими пользователями и пользователь может просматривать много продуктов. Необходимо разделить связь много ко много. |
|
|
Prod_ID |
varchar |
|||
|
User_ID |
varchar |
|||
|
Prod_Ingr |
ID |
integer |
Каждый пользователь может добавить себе в стоп-лист много ингредиентов и каждый ингредиент может быть добавлен несколькими пользователями, также возникает связь много ко много. |
|
|
Prod_ID |
varchar |
|||
|
Ingr_ID |
varchar |
|||
|
Ingr_User |
ID |
integer |
Каждый пользователь может добавить себе в стоп-лист много ингредиентов и каждый ингредиент может быть добавлен несколькими пользователями, также возникает связь много ко много. |
|
|
Ingr_ID |
varchar |
|||
|
User_ID |
varchar |
Рисунок 14. Схема базы данных
Наполнение базы данных производится, используя каталог магазина ВкусВилл с официального сайта [11].
2.3 Проектирование взаимодействия с пользователем
Исходя из функциональных требований к приложению спроектирован макет окон для взаимодействия с пользователем (рис.15).
Рисунок 15. Макет окон взаимодействия с клиентом
Выводы по главе
Спроектирована архитектура приложения, рассмотрены особенности работы с HTTP-запросами, спроектирована база данных приложения, спроектировано взаимодействия с пользователем
Глава 3. Разработка мобильного приложения
В данной главе представлено описание библиотек, инструментов и технологий, использованных при разработке и описаны особенности реализации.
3.1 Используемые средства и технологии
Приложение разрабатывалось в среде Android Studio. Данное программное обеспечение рекомендует компания Google для разработки приложений для платформы Android и предоставляет все технологии и языки, которые необходимы для создания программ на устройства под управлением данной операционной системы. Основным языком разработки клиентской части был Java, для реализации серверной части использовался язык php.
Существует множество способов, с помощью которых может быть реализовано сетевое взаимодействие в Android приложении. Можно использовать встроенную в платформу стандартную библиотеку [12], а также и сторонние библиотеки. Самой большой популярностью из них пользуются OkHttp [13], Volley [14] и Retrofit [15]. Помимо непосредственного выполнения сетевых запросов, необходимо также выполнять сериализацию / десериализацию данных и обеспечить асинхронность. Так как стандартные средства и OkHttp предоставляют лишь базовые возможности, выбор происходил между Volley и Retrofit. Данные библиотеки дают возможность легко выполнять http - запросы, обеспечивают асинхронное выполнение и предоставляют средства для синхронизации, однако Retrofit также имеет встроенные инструменты для сериализации и десериализации данных в формат JSON [16]. Для этого используется библиотека GSON [17] от компании Google. Помимо этого, Retrofit обладает большей производительностью [18].
Для реализации хранилища данных была выбрана СУБД MySQL [19] - свободная реляционная система управления базами данных.
Для работы над дизайном приложения стоял поиск аналога Sketch на Windows. В результате используется приложение Lunacy, которое имеет ограниченный функционал, но достаточный для разрабатываемого приложения. Позволяет быстро создавать базовые элементы, такие как текстовые поля, кнопки, аватары, иконки и импортировать это в двух форматах на выбор SVG или PNG.
3.3 Реализация сканирования штрих-кода
При нажатии на кнопку проверки состава в главной Activity инициализируется Intent для сканирования штрих-кода и запускается с ожиданием результата.
public void scanBarcode(View v)
{
Intent intent = new Intent(this,ScanBarcodeActivity.class);
startActivityForResult(intent,0);
}
После запуска камеры и получении отсканированного штрих-кода данные передаются в главную форму.
@Override
public void receiveDetections(Detector.Detections<Barcode> detections) {
final SparseArray<Barcode> barcodes =detections.getDetectedItems();
if (barcodes.size()>0){
Intent intent= new Intent();
intent.putExtra("barcode",barcodes.valueAt(0)); //get latest barcode
setResult(CommonStatusCodes.SUCCESS,intent);
finish();
}
}
Главная Форма получает результат сканирования штрихкода, сохраняет переданный штрих-код в строковую переменную и вызывает метод для получения списка ингредиентов данного продукта.
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode==0)
{
if (resultCode== CommonStatusCodes.SUCCESS)
{
if (data!=null){
Barcode barcode= data.getParcelableExtra("barcode");
barcodeResult.setText("Barcode value: "+barcode.displayValue);
barcodevalue=barcode.displayValue;
getprod();
}
else {
barcodeResult.setText("No barcode found");
}
}
}
3.4 Сетевое взаимодействие
Обычные Java - методы в интерфейсе описывают сетевые запросы. С помощью аннотаций создается спецификация адреса, заголовков и параметров запроса. Ниже приведен пример описания интерфейса, предназначенного для получения списка ингредиентов продукта.
public interface Request {
@GET("GetProdIngr.php/")
Call<Object> getpi(@Query("barcode") String BarcodeId);
}
Вызов осуществляется асинхронно в новом потоке, с помощью объекта наследующего данный интерфейс класса. После завершения запроса происходит вызов методов onResponse() или onFailure(). Через вызов метода response.body() можно получить десериализованный из JSON в Java - объект ответ сервера. Пример получения ингредиентов продукта с сервера представлен ниже.
public void getprod()
{
String barc=barcodevalue;
Call<Object> call = req.getpi(barc);
System.out.println(call.toString());
call.enqueue(new Callback<Object>() {
@Override
public void onResponse(Call<Object>call, Response<Object> response) {
System.out.println(response.body().toString());
Gson gson = new Gson();
ProductList Ingredients = gson.fromJson(response.body().toString(), ProductList.class);
for (Product i : Ingredients.getProduct()) {
adapter.add(i.getName());
System.out.println(i.getName());
}
}
@Override
public void onFailure(Call<Object> call, Throwable t) {
System.out.println("Request error");
}
});
}
3.5 Отображение результата поиска
В результате сканирования штрих-кода отображаются такие данные как название продукта, категория продукта, его штрих-код и список входящих в него ингредиентов. Первые три отображаются в простых текстовых полях, а реализация отображения списка продуктов реализуется с помощью компонента ListView. Подробнее будет рассмотрена работа с этим компонентом.
Сначала мы создаем xml-разметку добавляем ListView на форму.
<ListView
android:id="@+id/IngrList"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginEnd="4dp"
android:layout_marginTop="30dp"
android:choiceMode="multipleChoice"
tools:layout_editor_absoluteY="20dp">
</ListView>
Для формирования списка используются Адаптеры для связи массива данных с набором элементов. Наш список ингредиентов ListView состоит из множества элементов TextView. ArrayAdapter<T> представляет собой один из возможных адаптеров для работы с ListView. У каждого объекта из массива данных он вызывает метод toString()и размещает полученный результат в отдельных элементах TextView.
ArrayList<String> Ingr = new ArrayList<>();
ArrayAdapter<String> adapter;
ListView IngrList;
// получаем экземпляр элемента ListView
IngrList = findViewById(R.id.IngrList);
adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_multiple_choice, Ingr);
IngrList.setAdapter(adapter);
Для начала получаем экземпляр элемента ListView и далее создаем для него адаптер:
adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_multiple_choice, Ingr);
Конструктор, который используется для создания адаптера состоит из 3 элементов:
· this - текущий объект активити.
· android.R.layout.simple_list_item_multiple_choice - разметка с множественным выбором элементов.
· Ingr - массив данных из которых наполняется ListView.
В завершении устанавливаем адаптер для ListView, используя метод SetAdapter().
Наполнение списка происходит при получение результата с сервера в методе onResponse(). Про сетевое взаимодействие написано выше.
ProductList Ingredients = gson.fromJson(response.body().toString(), ProductList.class);
for (Product i : Ingredients.getProduct()) {
// наполнение списка
adapter.add(i.getName());
}
Метод getName()получает имя ингредиента из десериализованного JSON и адаптер добавляет его в ListView.
Сервер
Серверное приложение реализовано на языке PHP. В качестве хранилища данных используется СУБД MySQL. Для администрирования базы данных используется phpMyAdmin [20].
Сервер выполняет следующие функции:
1. Регистрация и авторизация пользователей.
2. Получение списка ингредиентов.
3. Получение, обновление списка ингредиентов для конкретного пользователя.
4. Получение списка ингредиентов для конкретного продукта.
5. Получения списка продуктов для пользователя.
6. Сопоставления списка ингредиентов пользователя со списком ингредиентов продукта.
7. Сопоставление списка ингредиентов пользователя со списком ингредиентов продуктов из конкретной категории.
В трехуровневой архитектуре сервер выступает в роли связующего звена между базой данных и клиентом приложения. Каждый запрос к серверу связан с взаимодействием с базой данных. Поэтому прежде всего необходимо подключиться к базе данных. Далее представлен пример подключения сервера к базе данных.