Какой движок баз данных используется в ОС Android?
Какой язык разметки используется для описания иерархии компонентов графического пользовательского интерфейса Android-приложения?
Для решения каких задач в Android используется класс MediaPlayer?
Для решения каких задач в Android используется класс MediaRecorder ?
Ядро какой операционной системы использовалось в качестве базы для ОС Android?
Какой элемент в архитектуре Android служит уровнем абстракции между аппаратным обеспечением и программным стеком?
Какой вид компоновки определяет табличный способ расположения компонентов графического интерфейса пользователя в приложениях под Android?
Какие библиотеки позволяют использовать возможности, появившиеся в какой-то версии ОС Android, на более ранних версиях платформы?
Какой компонент архитектуры Android позволяет любому приложению использовать уже реализованные возможности других приложений, к которым разрешен доступ?
Способы проектирования баз данных в Android
Работая Android разработчиком мне пришлось столкнуться с двумя различными подходами к проектированию баз данных в мобильных приложениях. Возможно кому-то излагаемое здесь покажется очевидным, а возможно кому-то даст новую идею или убережет от ошибок. В общем, без длинных предисловий переходим к делу…
Два взгляда на проблему
Как известно, в университетах учат строить базы данных по всем правилам: декомпозировать предметную область на сущности, выделить атрибуты и определить первичные ключи, определить отношения между сущностями, привести все это, как минимум, к 3-ей нормальной форме и т.д. Один из “побочных” эффектов такого подхода — падение производительности на операциях чтения, при достаточно сильной декомпозиции и нормализации, так как в запросах необходимо выполнять большее количество джойнов. И чем больше у вас записей в таблицах, тем дольше они выполняются.
Добавим сюда сильно ограниченные аппаратные возможности мобильных платформ, в частности крохотный объем оперативной памяти. Ее и без того мало, так в дополнение к этому, Android ограничивает количество доступной RAM на процесс в зависимости от версии ОС от 16 до 48 МБ. И даже из этих нескольких мегабайт СУБД получает лишь часть, ведь есть еще и само приложение. Ну и в заключение, сам SQLite, в виду своих особенностей поддерживает только два уровня изолированности транзакций. Они либо сериализуются, либо вообще отключены!
В ситуации когда производительность приложения начинает упираться в производительность СУБД на помощь и может прийти альтернативный подход, назовем его key-value ориентированным. Вместо декомпозиции сущности на атрибуты и создания отдельных полей в таблице на каждый атрибут, сущность сохраняется “как есть” в одно единственное поле типа BLOB, иначе говоря сериализуется.
Рассмотрим пример для полной ясности. Пусть наша модель данных в Java-коде выглядит следующим образом:
class Group < private Long _id; private String number; private Liststudents; // getters and setters . > class Student < private Long _id; private String name; private String surname; private Group group; // getters and setters . >
Таким образом, в “стандартном” варианте мы получим две таблицы с соответствующими наборами атрибутов.
create table Group( _id primary key integer autoincrement, number text); create table Student( _id primary key integer autoincrement, name text, surname text, group_id integer foreign key);
В настоящем проекте сущностей и атрибутов значительно больше, плюс сюда добавляются различные служебные поля, типа дата последней синхронизации с сервером или флаг-указатель, требуется ли отправка сущности на сервер для обновления измененных данных и т.д.
При применении же key-value подхода таблицы будут выглядеть так
create table Group( _id primary key integer autoincrement, value blob); create table Student( _id primary key integer autoincrement, value blob, group_id integer foreign key);
при этом группы и студенты сериализуются отдельно по разным таблицам. Либо вообще вот так:
create table Group( _id primary key integer autoincrement, value blob);
когда группа сериализуется прямо со всеми студентами в одну таблицу.
Рассмотрим достоинства и недостатки обоих подходов и какую выгоду из этого можно извлечь.
Сравнение подходов, плюсы и минусы
Возможности реляционной алгебры
При использовании стандартного подхода мы получаем все преимущества к которым так привыкли при использовании реляционного подхода, а именно язык SQL для удобной выборки, фильтровки и сортировки данных, а также модификации схемы БД. Для того чтобы получить коллекцию сущностей нам необходимо лишь сформировать требуемое условие и забрать наши данные из БД. В key-value же подходе, задача по фильтрованию или упорядочиванию данных лежит на плечах разработчика.
Объем файла БД
При использовании стандартного подхода файл БД как правило имеет меньший объем. Это обуславливается отсутствием избыточности при хранении данных, в следствии нормализации. В теории чем выше степень нормализации, тем меньше избыточность, однако, возрастает нагрузка на БД при чтении этих данных. Значительные ресурсы тратятся на джойны таблиц. При использовании key-value подхода степень избыточности данных выше, так как, как правило, уровень нормализации значительно меньше, что приводит к увеличению размера файла БД.
Гибкость при изменении схемы БД
Обычно с развитием проекта схема БД преобразуется не однократно, добавляются новые поля, удаляются ранее используемые, сущности могут дробиться на несколько новых или наоборот проводиться их денормализация и объединение нескольких таблиц в одну. В случае, если при обновлении схемы мы можем пожертвовать накопленными в БД данными, то все просто: мы создаем новый файл БД каждый раз когда обновляем схему, а старый удаляем. Но что делать, если данные должны быть сохранены и преобразованы к новому формату?
В таком варианте стандартный подход имеет преимущества. Достаточно написать соответствующие апдейт-скрипты, которые преобразуют схему БД к необходимому виду и обновят новые поля значениями по умолчанию или высчитают их с применением той или иной логики. При использовании же сериализации, обновление схемы БД уже не такая простая задача. Необходимо преобразовать схему с сохранением всех данных, а так же обновить сами данные, десиреализовав их, инициализировав новые поля и сериализовав обратно. Возрастает как логическая сложность операции, так и временные затраты необходимые на обновление.
Синхронизация доступа к экземплярам сущностей
Один из основных недостатков key-value подхода, как мне кажется, то что для того чтобы изменить всего одно поле в сущности нам необходимо десериализовать весь объект целиком. Это значительно усложняет доступ к объектам. Например, в случае когда группа сериализуется в базу вместе со всеми студентами, то для того, чтобы изменить фамилию одного из студентов, нам необходимо вынуть из БД всю группу, поменять одну фамилию и сохранить обратно. В случае, если в приложении несколько потоков, сервисов и/или контент провайдеров, которые могут работать с одними и теми же сущностями, то задача многократно усложняется. Чем больше потенциальных “писателей”, тем больше блокировок будет возникать и тем сложнее нам будет обеспечивать синхронизацию доступа к объектам. В случае же стандартного подхода эта задача решается на уровне СУБД.
Производительность
С одной стороны, key-value подход позволяет добиться более высокой производительности при выборке небольших объемов данных. Количество джойнов сокращается, конкретный запрос да и СУБД в целом работает быстрее. С другой, при больших объемах данных, если нам необходимо выполнять фильтрацию или сортировку этих данных по полю которое сериализуется вместе со всем объектом, то для выполнения этой операции нам сначала будет необходимо причитать все сущности, а только потом уже отфильтровать все лишнее, что может привести не к выигрышу производительности, а к ее еще большему ухудшению. В качестве альтернативы можно хранить поля участвующие в запросе фильтровки или сортировки стандартным подходом, а всю остальную сущность в виде BLOBa, но такую кашу потом будет сложно поддерживать.
Объем кода
В стандартном подходе возрастает количество SQL кода, различные скрипты создания и модификации схемы БД, запросы и условия, DAO-объекты и т.д. В key-value, количество подобного кода сокращается, зато возрастает количество кода выполняющего различные сортировки, группировки и фильтрацию по условиям ведь все это приходится выполнять “вручную”, когда при стандартном подходе это делает СУБД, а нам необходимо только написать требуемый запрос.
Сериализация
Минус key-value подхода может состоять в падении производительности связанном с использованием стандартной Java сериализации / десериализации, которая как известно не отличается высокой скоростью. Здесь в качестве альтернативы можно использовать одну из библиотек решающих эту проблему, например protobuf от Google. Помимо скорости дополнительным плюсом, в случае исползьования protobuf’a, будет версионность, т.к. данный протокол поддерживает версионирование объектов.
Заключение
Вышло немного сумбурно, но в целом, что хотелось сказать: оба подхода хороши, необходимо выбирать по ситуации, рассматривая все перечисленные плюсы и минусы. Как правило, если проблем с производительностью нет, то лучше использовать стандартный подход, обладающий большей гибкостью. Если эти проблемы начинают возникать, попробуйте использовать денормализацию. Возможно, если критических участков в программе всего несколько, то это может все решить. При возникновении же постоянных проблем с производительностью, когда денормализация уже не спасает, стоит присмотреться к key-value подходу.
- android development
- sqlite
Применение SQLiteOpenHelper и Database Inspector в Android-разработке
Автор статьи, перевод которой мы публикуем сегодня, хочет рассказать об использовании баз данных SQLite в Android-разработке. В частности — он коснётся тут двух вопросов. Во-первых — речь пойдёт о классе SQLiteOpenHelper , который применяется для работы с базами данных в коде приложений. Во-вторых — он уделит определённое внимание инструменту Database Inspector, инспектору баз данных, встроенному в Android Studio.
Что такое SQLite?
SQLite — это опенсорсная реляционная СУБД, похожая на MySQL. SQLite входит в состав стандартной библиотеки Android, где реализован движок базы данных, не нуждающийся в применении клиент-серверной архитектуры, не требующий особой настройки, поддерживающий транзакции. Для работы SQLite не нужен сервер баз данных. Всё, что нужно, хранится в обычных файлах. Полноценная БД SQLite, в которой имеется множество таблиц, триггеров, индексов и представлений, содержится в единственном, самом обыкновенном файле. Стандартная поддержка СУБД SQLite имеется во всех мобильных телефонах и в большинстве компьютеров. При этом работа с SQLite не требует решения задач по администрированию или настройке баз данных.
Android-разработчик, для выполнения CRUD-операций из своего приложения, может пользоваться пакетом android.database.sqlite.SQLiteDatabase , в котором реализованы все необходимые API.
Класс SQLiteOpenHelper
SQLiteOpenHelper — это класс, встроенный в пакет android.database.sqlite.SQLiteDatabase . Это — вспомогательный класс, который отвечает за создание баз данных SQLite и за управление их версиями. Для того чтобы воспользоваться возможностями этого класса, нужно создать его подкласс, в котором надо переопределить два метода — onCreate() и onUpgrade() . Этот класс позволяет открывать существующие базы данных, создавать новые базы данных и обновлять версии баз данных.
▍Метод onCreate()
Метод onCreate() вызывается при создании базы данных. Он, в процессе жизненного цикла приложения, вызывается лишь один раз. А именно, его вызов производится при первом обращении к методу getReadableDatabase() или getWritableDatabase() . Эти методы принадлежат классу SQLiteOpenHelper .
В следующем примере показано создание экземпляра класса DatabaseHelper , являющегося наследником SQLiteOpenHelper :
DatabaseHelper dbh = new DatabaseHelper(getApplicationContext());
Вот код конструктора DatabaseHelper :
public DatabaseHelper(Context context)
Класс SQLiteOpenHelper вызывает метод onCreate() после создания базы данных и создания экземпляра класса SQLiteDatabase . Этот метод, напомним, вызывается лишь один раз, при создании базы данных:
@Override public void onCreate(SQLiteDatabase db) < try < db.execSQL(CREATE_TABLE); >catch (Exception e) < >>
▍Метод onUpgrade()
Метод onUpgrade() вызывается в тех случаях, когда нужно обновить версию существующей базы данных:
@Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)
▍Пример
Создадим простое Android-приложение и разберём практический пример работы с классом SQLiteOpenHelper . Это приложение, применяя данный класс, позволит нам добавлять записи о работниках некоей компании в таблицу Empdata базы данных Company.db .
Приложение имеет весьма простой интерфейс, описанный в файле activity_main.xml .
Интерфейс приложения
Вот содержимое файла MainActivity.java :
package www.sqliteopenhelper.sqliteopenhelper; import androidx.appcompat.app.AppCompatActivity; import android.os.Bundle; import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.Toast; public class MainActivity extends AppCompatActivity < @Override protected void onCreate(Bundle savedInstanceState) < super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); EditText edtEmpName = findViewById(R.id.edtName); EditText edtDesig = findViewById(R.id.edtDesig); EditText edtSalary = findViewById(R.id.edtSalary); Button btnSave = findViewById(R.id.btnSave); DatabaseHelper dbh= new DatabaseHelper(getApplicationContext()); btnSave.setOnClickListener(new View.OnClickListener() < @Override public void onClick(View view) < Employee objEmp = new Employee(edtEmpName.getText().toString(),edtDesig.getText().toString(),Integer.parseInt(edtSalary.getText().toString())); if(dbh.InsertEmployee(objEmp)) Toast.makeText(getApplicationContext(),"Record inserted successfully",Toast.LENGTH_LONG).show(); else Toast.makeText(getApplicationContext(),"Record not inserted",Toast.LENGTH_LONG).show(); >>); > >
Вот содержимое Employee.java :
package www.sqliteopenhelper.sqliteopenhelper; public class Employee < private String employeeName; private String employeeDesig; private int employeeSalary; public Employee() < >public Employee(String employeeName, String employeeDesig, int employeeSalary) < this.employeeName = employeeName; this.employeeDesig = employeeDesig; this.employeeSalary = employeeSalary; >public String getEmployeeName() < return employeeName; >public String getEmployeeDesig() < return employeeDesig; >public int getEmployeeSalary() < return employeeSalary; >public void setEmployeeName(String employeeName) < this.employeeName = employeeName; >public void setEmployeeDesig(String employeeDesig) < this.employeeDesig = employeeDesig; >public void setEmployeeSalary(int employeeSalary) < this.employeeSalary = employeeSalary; >>
Вот файл DatabaseHelper.java :
package www.sqliteopenhelper.sqliteopenhelper; import android.content.ContentValues; import android.content.Context; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; public class DatabaseHelper extends SQLiteOpenHelper < public static final int version = 1; public static String dbName="Company.db"; public static final String TABLE_NAME ="Empdata"; public static final String COL1 = "id"; public static final String COL2 = "name"; public static final String COL3 = "designation"; public static final String COL4 = "salary"; private static final String CREATE_TABLE="create table if not exists "+ TABLE_NAME + "(" + COL1 + " INTEGER PRIMARY KEY AUTOINCREMENT,"+COL2+" TEXT NOT NULL," + COL3 + " TEXT, " +COL4 + " INTEGER);"; private static final String DROP_TABLE = "DROP TABLE IF EXISTS "+ TABLE_NAME; private Context context; public DatabaseHelper(Context context) < super(context,dbName,null,version); context=this.context; >@Override public void onCreate(SQLiteDatabase db) < try < db.execSQL(CREATE_TABLE); >catch (Exception e) < >> @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) < db.execSQL(DROP_TABLE); onCreate(db); >public boolean InsertEmployee(Employee objEmp) < SQLiteDatabase db=this.getWritableDatabase(); ContentValues cv = new ContentValues(); cv.put(COL2,objEmp.getEmployeeName()); cv.put(COL3,objEmp.getEmployeeDesig()); cv.put(COL4,objEmp.getEmployeeSalary()); long result = db.insert(TABLE_NAME,null,cv); if(result == -1) return false; else return true; >>
В коде DatabaseHelper.java видно, что класс DatabaseHelper является наследником класса SQLiteOpenHelper .
После того, как база данных создана, можно приступать к работе с ней. В частности — создавать и обновлять поля её таблиц. При решении этих задач можно пользоваться объектом типа ContentValues , который позволяет хранить пары ключ-значение. Именно такие объекты используются для добавления новых записей в таблицы базы данных (с помощью метода insert() объекта SQLiteDatabase ) и для обновления существующих записей (с помощью метода update() ).
Database Inspector в Android Studio
Инструмент Database Inspector позволяет исследовать базы данных, используемые в приложениях, выполнять запросы к ним, модифицировать их, делая всё это во время работы приложений. Database Inspector имеется в Android Studio начиная с версии 4.1. Этот инструмент особенно полезен при отладке механизмов программ, ответственных за работу с базами данных. Database Inspector работает и с обычной реализацией SQLite, и с библиотеками, построенными на её основе, вроде Room. Database Inspector работает лишь с библиотекой SQLite, входящей в состав операционных систем Android, уровень API которых не ниже 26.
Для того чтобы открыть базу данных в Database Inspector нужно выполнить следующие действия:
- Запустите приложение в эмуляторе или на устройстве, подключённом к компьютеру. На эмуляторе или на устройстве должно присутствовать API не ниже 26 уровня.
- Выполните команду меню View > Tool Windows > Database Inspector .
- Выберите процесс выполняющегося приложения из выпадающего меню.
- В панели Databases появятся записи о базах данных, которые имеются в выбранном приложении. Теперь можно работать с этими базами данных.
Исследование базы данных с помощью Database Inspector
Обратите внимание на то, что есть одна проблема, связанная с аварийным завершением работы программ на эмуляторе Android 11 при подключении к Database Inspector. Если вы с этой проблемой столкнётесь — здесь вы можете найти сведения о том, как с ней справиться.
Применяете ли вы SQLite в Android-разработке?
Какой движок баз данных используется в ос android
С какой целью инструмент Intel* Hardware Accelerated Execution Manager (Intel* HAXM) используется в среде разработки Intel* Beacon Mountain?
(Отметьте один правильный вариант ответа.)
Вариант 1 для ускорения работы эмулятора в среде разработки
Вариант 2 для эффективного распараллеливания С++ мобильных приложений
Вариант 3 для оптимизированной обработки данных и изображений
Вариант 4 для оптимизации загрузки системы при использовании процедур OpenGL
С какой целью инструмент Intel* Graphics Performance Analyzers (Intel* GPA) System Analyzer используется в среде разработки Intel* Beacon Mountain?
(Отметьте один правильный вариант ответа.)
Вариант 1 позволить разработчикам эффективно распараллелить С++ мобильные приложения
Вариант 2 позволить разработчикам оптимизировать загруженность системы при использовании процедур OpenGL
Вариант 3 для ускорения работы эмулятора в среде разработки
Вариант 4 для оптимизированной обработки данных и изображений
Тестирование на реальных устройствах приложений, разработанных в Intel XDK
(Отметьте один правильный вариант ответа.)
Вариант 1 невозможно
Вариант 2 можно проводить, установив на устройство специальную программу или собрав проект и установив результат сборки на устройство
Вариант 3 можно проводить, установив на устройство специальную программу
Вариант 4 можно проводить, собрав проект и установив результат сборки на устройство
Эмулятор среды разработки Intel XDK:
(Отметьте один правильный вариант ответа.)
Вариант 1 не требует больших системных ресурсов
Вариант 2 позволяет проверить работу приложений, использующих акселерометр
Вариант 3 позволяет моделировать исполнение приложение на ряде устройств
Вариант 4 все варианты ответа верны
Intel XDK поддерживает разработку под:
(Отметьте один правильный вариант ответа.)
Вариант 1 JavaFX Mobile
Вариант 2 Apple iOS, BlackBerry OS
Вариант 3 Android, Apple iOS, Microsoft Windows 8, Tizen
Вариант 4 MtkOS, Symbian OS, Microsoft Windows 8
Какая графическая библиотека входит в набор библиотек ОС Android?
(Отметьте один правильный вариант ответа.)
Вариант 1 OpenCV
Вариант 2 DirectX
Вариант 3 Open GL
Вариант 4 OpenCL
Приложения, не имеющие GUI и выполняющиеся в фоновом режиме — это
(Отметьте один правильный вариант ответа.)
Вариант 1 Intents
Вариант 2 Content Providers
Вариант 3 Activities
Вариант 4 Services
Удобное средство обмена между двумя NFC-устройствами:
(Отметьте один правильный вариант ответа.)
Вариант 1 Wi-Fi Direct
Вариант 2 Dalvik
Вариант 3 AndroidBeam
Вариант 4 Bluetooth
Преимуществом эмуляторов является:
(Отметьте один правильный вариант ответа.)
Вариант 1 невозможность полноценной отладки
Вариант 2 необходимость дополнительных системных ресурсов
Вариант 3 низкая стоимость
Вариант 4 медленный запуск
Фоновые приложения .
(Отметьте один правильный вариант ответа.)
Вариант 1 после настройки не предполагают взаимодействия с пользователем, большую часть времени находятся и работают в скрытом состоянии
Вариант 2 небольшие приложения, отображаемые в виде графического объекта на рабочем столе
Вариант 3 выполняют свои функции и когда видимы на экране, и когда скрыты другими приложениями
Вариант 4 большую часть времени работают в фоновом режиме, однако допускают взаимодействие с пользователем и после настройки
Какая папка в структуре Android-приложения содержит файлы с исходным кодом на языке Java?
(Отметьте один правильный вариант ответа.)
Вариант 1 gen
Вариант 2 src
Вариант 3 bin
Вариант 4 res
Приложение какого вида имеет смысл использовать для отображения динамической информации, такой как заряд батареи, прогноз погоды, дата и время?
(Отметьте один правильный вариант ответа.)
Вариант 1 фоновое приложение
Вариант 2 приложение переднего плана
Вариант 3 виджет
Вариант 4 смешанное приложение
Что находится в папке gen?
(Отметьте один правильный вариант ответа.)
Вариант 1 ресурсы приложения
Вариант 2 аудио файлы
Вариант 3 изображения
Вариант 4 автоматически сгенерированные java-файлы
Каждый приемник широковещательных сообщений является наследником класса .
(Отметьте один правильный вариант ответа.)
Вариант 1 ContentProvider
Вариант 2 BroadcastReceiver
Вариант 3 ViewReceiver
Вариант 4 IntentReceiver
Выберите верные утверждения относительно объекта-намерения (Intent).
(Ответ считается верным, если отмечены все правильные варианты ответов.)
Вариант 1 представляет собой структуру данных, содержащую описание операции, которая должна быть выполнена, и обычно используется для запуска активности или сервиса
Вариант 2 используются для передачи сообщений между основными компонентами приложений
Вариант 3 используется для получения инструкций от пользователя
Вариант 4 используется для передачи сообщений пользователю
Какие методы может использовать активность для запуска сервиса?
(Ответ считается верным, если отмечены все правильные варианты ответов.)
Вариант 1 bindService()
Вариант 2 openService()
Вариант 3 startService()
Вариант 4 createService()
Выберите верную последовательность действий, необходимых для создания в приложении контент-провайдера.
(Отметьте один правильный вариант ответа.)
Вариант 1 Проектирование способа хранения данных; Создание класса-наследника от класса ContentProvider; Определение строки авторизации провайдера, URI для его строк и имен столбцов
Вариант 2 Создание класса наследника от класса ContentProvider; Заполнение контент-провайдера данными; Определение способа работы с данными
Вариант 3 Проектирование способа хранения данных; Определение способа организации данных; Определение способа работы с данными
Вариант 4 Создание класса наследника от класса ContentProvider; Определение способа организации данных; Заполнение контент-провайдера данными