Министерство по развитию информационных технологий и коммуникаций Республики Узбекистан

ТАШКЕНТСКИЙ УНИВЕРСИТЕТ ИНФОРМАЦИОННЫХ ТЕХНОЛОГИЙ ИМЕНИ МУХАММАДА АЛ-ХОРАЗМИЙ

 

 

Бабомурадов О.Ж., Дощанова М.Ю.

 

 

 

 

РАЗРАБОТКА МОБИЛЬНЫХ ПРИЛОЖЕНИЙ

 

УЧЕБНИК

для студентов высших учебных заведений по направлениям 

5330600 – “Программный инжиниринг”,

5330500 – “Компьютерный инжиниринг (Мультимедиа технологиялари)”

 

 

 

 

 

 

 

 

 

Ташкент  – 2021

Авторы: О.Ж.Бабомурадов, М.Ю.Дощанова.Разработка мобильных приложений.ТУИТ. 344 с. Ташкент, 2021.

 

В учебнике рассмотрены вопросы разработки мобильных приложений для мобильных устройств под управлением операционной системы Android и IOS. Приведеныбазовые сведения о платформе Android. Описаны программные средства необходимые для разработки Android и iOS приложений. Рассмотрены основные компоненты приложений, использование базовых виджетов, разработка мобильных приложений с использованием языков программирования Java,XML и Swift, создание пользовательских приложений, создание приложений для загрузки изображений, использование меню, подменю и контент провайдеров, приложений для отправки и обмена сообщениями,  даны работы с базами данных, подключение к серверу, использование сервисов GoogleMaps, определение местоположения пользователя и публикация приложения в интернете.

В учебнике также имеются контрольные вопросы, глоссарий и список использованной литературы.

Учебник предназначен для студентов высших учебных заведенийпо направлениям образования  5330600 – “Программный инжиниринг” и5330500 – “Компьютерный инжиниринг (Мультимедиа технологиялари)”, а также для тех кто занимается  программированием для мобильных устройств.

Напечатано по решению учебно – методического совета ТУИТ (протокол № от «____» ________2021 г.)

 

 

Рецензенты:

 

А.Ахатов

- Заместитель директора Джизакского филиала Национального университета Узбекистана, д.т.н., профессор;

Б.Б.Муминов

- Заведующий кафедрой «Основы информатики» Ташкентского университета информационных технологий имени Мухаммада ал-Хоразмий, д.т.н. профессор.

 

                                              

 

 

 

 

 

Ташкентский университет информационных технологий

имени Мухаммада ал-Хоразмий

ВВЕДЕНИЕ

 

Большое стремление каждого человека достигнуть максимального комфорта в каждой из сфер жизни, затронуло и международную паутину Интернет. Пользователь, желая всегда оставаться в сети, использует в качестве коммуникатора телефон.

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

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

Учебник посвящен основам разработки мобильных приложений под Android и iOSв интегрированных средах разработкиAndroidStudio, XCode c использованием языков программирования Java и Swift. Эти средства доступны и универсальны по своим характеристикам. Они обладают широкими возможностями и достаточно просты в освоении. Работа в этих программных средствах с помощью этой книги станет намного проще, легче и гораздо интереснее. Например, в разработке мобильных приложений задействованы сразу несколько языков программирования и разметки (Java, Xml, Sql, Swift).

Первая глава этого учебника посвящена основам разработки мобильных приложений и жизненный цикл мобильных приложений, а также понятия разработки мобильных приложений и среды разработки мобильных приложений. Рассматриваются языки программирования мобильных приложений Javaи Dart для операционных систем Аndroid и iOS. Подробно описываются жизненный цикл мобильных приложений, управление жизненным циклом, состояние активностей, процессы и потоки приложений.

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

Третья глава учебника направлена на разработкумобильных приложений на языке Java для операционной системы Аndroid. Рассматриваются основные конструкцииязыка программированияJavа, типы данных, специальные классы и функции, классы и объекты, конструкторы, инициализаторы, а также способы разработки мобильных приложений на языке Javа.

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

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

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

Последняя глава посвящена разработке приложений на языке Swift для ОСiOS. Приведены материалы по архитектуре и принципам проектированияв программной средеXCode. Рассматриваются основные принципы работы на языке Swift, переменные и константы,типы данных, условные конструкции, тернарные операторы, циклы, функции, классы и объекты, статические свойства и методы, структуры, наследование, полиморфизм, коллекции, массивы, множества, словари, сабскрипты. После прохождения нескольких уроков изученные функции объединяются в одно полноценное приложение. Завершает книгу итоговый проект, в котором с помощью всех полученных навыков создается мобильное приложение.

Развернутое оглавление книги поможет легко ориентироваться в расположении описания изученных функций.

 

 

 

 

 

 

 

 

 

 

 

1. ОСНОВЫ РАЗРАБОТКИ И ЖИЗНЕННЫЙ ЦИКЛ МОБИЛЬНЫХ ПРИЛОЖЕНИЙ

1.1. Среды программирования для разработки мобильных приложений

1.1.1.  Программные средства разработки мобильных приложений

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

Самыми распространенными средствами разработки мобильных приложений являются следующие:JDK, NetBeans IDE,  Eclipse IDE,  Android Studio, Intel XDK, IntelliJ IDEA, JDeveloper,  BlueJ, Geany,Marmalade SDK и др.

Java Development Kit (JDK) является одним из трех основных технологий, используемых в программировании на языке Java. К ним также относятся JVM (Java Virtual Machine) и JRE (Java Runtime Environment). JVM отвечает за исполнение Java-программы. JRE представляет собой пакет инструментов для запуска Java-кода, создает и запускает JVM. JRE может использоваться, как отдельный компонент для простого запуска Java-программ или быть частью JDK.JDK позволяет разработчикам создавать программы, которые могут выполняться и запускаться посредством JVM и JRE. JDK представляет собой пакет инструментов для разработки программного обеспечения. JDK требуется JRE, потому что запуск программ является неотъемлемой частью их разработки.КаждыйJDK содержит компилятор Java.

КомпиляторJava — это программа, способная принимать исходные файлы с расширением .java, которые являются обычным текстом и превращать их в исполняемые файлы с расширением .class.

NetBeans IDE - эта интегрированная среда разработки с открытым кодом для разработчиков программного обеспечения. NetBeans IDE предоставляет все средства для создания приложений рабочей среды, корпоративных, мобильных и веб-приложений на языках Java, C/C++, а также на других динамических языках. NetBeans IDE может работать на разных платформах, как Windows, Linux, Solaris и Mac.

NetBeans IDE отличается простотой установки и удобством использования и не требует дополнительной настройки. NetBeans IDE содержит все, что нужно для разработки плагинов и приложений на основе NetBeans Platform.NetBeans Platform — платформа для разработки модульных настольных Swing-приложений. Приложения могут динамически загружать другие модули. Любое приложение может включить модуль Обновления, чтобы позволить пользователям загружать обновления для программ и модулей в работающее приложение.

Eclipse - это интегрированная среда разработки (IDE) для разработки приложений с использованием языка программирования Java и других языков программирования, таких как C / C ++, Python, PERL, Ruby и т. д.Платформа Eclipse обеспечивает основу для Eclipse IDE, состоит из плагинов и предназначена для расширения с помощью дополнительных плагинов. Eclipse может использоваться для разработки многофункциональных клиентских приложений, интегрированных сред разработки и других инструментов.

Eclipse может использоваться в качестве IDE для любого языка программирования, для которого доступен плагин.Проект Java Development Tools (JDT) предоставляет плагин, который позволяет использовать Eclipse в качестве Java IDE, PyDev - это плагин, который позволяет использовать Eclipse в качестве Python IDE, C / C ++ Development Tools (CDT) - это плагин -в этом позволяет использовать Eclipse для разработки приложений с использованием C / C ++, плагин Eclipse Scala позволяет Eclipse использовать IDE для разработки приложений Scala, а PHPeclipse представляет собой плагин для затмения, который предоставляет полный инструмент разработки для PHP.

JDeveloper –интегрированная среда разработки программного обеспечения. Предоставляет возможность для разработки на языках программирования Java, JavaScript, BPEL, PHP, SQL, PL/SQL и на языках разметки HTML, XML. JDeveloper покрывает весь жизненный цикл разработки программного обеспечения от проектирования, кодирования, отладки, оптимизации, профилирования  и развёртывания.

BlueJ – среда разработки программного обеспечения на языке Java, созданная в основном для использования в обучении, но также подходящая для разработки небольших программ.BlueJ была разработана для поддержки обучения объектно-ориентированному программированию и её дизайн отличается от других сред разработки. Главный экран показывает структуру классов разрабатываемого приложения в графическом виде (на UML-подобной диаграмме), а объекты можно создавать и тестировать интерактивно. Подобная интерактивность совместно с ясным, простым интерфейсом пользователя позволяет легко экспериментировать с разрабатываемыми объектами. Концепции объектно-ориентированной разработки (классы, объекты, сообщение через вызов методов) интерактивны и наглядно представлены в интерфейсе программы.

Geany – свободная среда разработки программного обеспечения, написанная с использованием библиотеки GTK2. Geany не включает в свой состав компилятор. Для создания исполняемого кода используется GNU Compiler Collection или любой другой компилятор. Доступна для следующих операционных систем: BSD, Linux, Mac OS X, Solaris и Windows.

IntelliJ IDEA –интегрированная среда разработки программного обеспечения для языков программирования Java, JavaScript, Python, разработанная компанией JetBrains.Дизайн среды ориентирован на продуктивность работы программистов, позволяя сконцентрироваться на функциональных задачах.

1.1.2. Android IDE

В Android IDE реализован полный цикл разработки редактирование-компиляция-выполнение.  Автодополнение кода, проверка ошибок в реальном времени, навигация по коду и запуск вашего приложения в одно касание, возможна разработка приложений для Андроида прямо на устройствах с ОС Андроид:

v андроид-планшет с клавиатурой может стать полноценным местом разработки;

v можно просматривать и редактировать код прямо на смартфоне;

v поддерживает разработку с использованием Java/Xml и C/C++;

v полностью совместима с проектами Eclipse;

v поддерживает профессиональную разработку приложений;

Android Studio — среда разработки под операционную систему Андроид, на базе IntelliJ IDEA. Программное обеспечение вышло в 2013 году и развивается до сих пор. В каждой новой версии Android Studio разработчик увеличивает функционал, оптимизирует процессы и другое.В комплекте Android Studio есть эмулятор, проверяющий корректную работу уже написанных утилит, приложений на разных конфигурациях. Возможности Android Studio:

v позволяет редактировать приложение в реальном времени, отображая его поведение одновременно на устройствах с различными диагоналями экранов;

v доступно мгновенное переключение на различные типы верстки и размеры экранов;

v раздел с подсказками и советами по оптимизации с тематическими разделами;

v средство взаимодействия с бета-тестерами;

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

В Android SDK приложение состоит из 4 компонентов:

1.                Activity – основная единица графического интерфейса (аналог окна или экранной формы).

2.                ContentProviders управляет распределенным множеством данных приложения. Например, контент-провайдер в системе Android, управляющий информацией о контактах пользователя:

              данные могут храниться в файловой системе, в базе данных SQLite, в сети;

              позволяет другим приложениям при наличии у них соответствующих прав делать запросы или даже менять данные.

3.                Intents– системные сообщения, позволяющие приложениям обмениваться информацией между собой и с операционной системой:

              поступление телефонного звонка;

              приход sms-сообщения;

              вставлена SD-карта;

              запущена новая активность;

              Intents – рекомендованный способ взаимодействия компонентов приложения.

4.                Приложения, не имеющие GUI и выполняющиеся в фоновом режиме. Примеры сервисов:

              проверка электронной почты;

              получение гео-информации.

1.1.3. IntelXDK

Intel XDK - это среда разработки HTML5, которая поможет вам создавать приложения для Интернета с использованием HTML5 и распространять их.Он позволяет быстросоздавать HTML5-приложения для устройств, работающих под управлением iOS, Android и Windows. Возможности Intel XDK:

v позволяет легко разрабатывать кроссплатформенные приложения;

v включает в себя инструменты для создания, отладки и сборки ПО, а также эмулятор устройств;

v поддерживает разработку для  Android, Apple iOS, Microsoft Windows 8;

v языки разработкиHTML5 и JavaScript.

Intel XDK позволяет разрабатывать на любой платформе, потому что компиляция выполняется в облаке. Intel XDK не ограничен мобильными платформами. Intel XDK поставляется с редактором скобок с открытым исходным кодом от Adobe. В состав XDK также входит графический редактор, которого очень не хватает на всех мобильных платформах HTML5.  Использованиекомпонентов HTML принесет пользу от редактора WYSIWYG. Intel XDK также поддерживает такие платформы, как Bootstrap и jQuery Mobile . Эти компоненты пользовательского интерфейса позволяют быстро создать интерфейс приложения.

1.1.4. Marmalade SDK

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

В Marmalade SDK есть эмулятор и можно тестировать приложение выбрав определенную модель устройства и разрешение экрана. Поддерживает взаимодействие с камерой, микрофоном, акселерометром, GPS модулем. Также Marmalade SDK позволяет использовать С/С++ код для создания мобильных приложений. Разработчики могут использовать одну базу кода на максимальном количестве платформ, что означает выход на большыепроекты и больший потенциал. Marmalade позволяет делиться, интегрировать и снова использовать существующие наработки, технологии или сторонние инструменты, предоставляя высокоэффективную инфраструктуру для разработки игр, которая открыта, гибка и одинаково подходит для больших и малых проектов.

1.2.  Языкыпрограммирования для разработки мобильных приложений

1.2.1. Язык Java  для операционной системы Android

Java – объектно-ориентированный язык программирования, разработанный компанией Sun Microsystems. Приложения Java обычно транслируются в специальный байт-код, поэтому они могут работать на любой виртуальной Java-машине вне зависимости от компьютерной архитектуры. Дата официального выпуска – 23 мая 1995 года.

Программы на Java транслируются в байт-код, выполняемый виртуальной машиной Java (JVM) – программой, обрабатывающей байтовый код и передающей инструкции оборудованию как интерпретатор.

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

Часто к недостаткам концепции виртуальной машины относят снижение производительности. Ряд усовершенствований несколько увеличил скорость выполнения программ на Java:

·                     применение технологии трансляции байт-кода в машинный код непосредственно во время работы программы (JIT-технология) с возможностью сохранения версий класса в машинном коде,

·                     широкое использование платформенно-ориентированного кода (native-код) в стандартных библиотеках,

·                     аппаратные средства, обеспечивающие ускоренную обработку байт-кода (например, технология Jazelle, поддерживаемая некоторыми процессорами фирмы ARM).

Внутри Java существуют несколько основных семейств технологий:

·                     Java SE — Java Standard Edition, основное издание Java, содержит компиляторы, API, Java Runtime Environment; подходит для создания пользовательских приложений, в первую очередь — для настольных систем.

·                     Java EE — Java Enterprise Edition, представляет собой набор спецификаций для создания программного обеспечения уровня предприятия.

·                     Java ME — Java Micro Edition, создана для использования в устройствах, ограниченных по вычислительной мощности, например, в мобильных телефонах, КПК, встроенных системах;

·                     JavaFX — технология, являющаяся следующим шагом в эволюции Java как Rich Client Platform; предназначена для создания графических интерфейсов корпоративных приложений и бизнеса.

·                     Java Card — технология предоставляет безопасную среду для приложений, работающих на смарт-картах и ​​других устройствах с очень ограниченным объёмом памяти и возможностями обработки.

Компанией Microsoft была разработана собственная реализация JVM (MSJVM), включавшаяся в состав различных операционных систем, начиная с Windows 98.

Microsoft JVM имела существенные отличия от Sun Java, во многом ломающие основополагающую концепцию переносимости программ между разными платформами:

·                     отсутствие поддержки программного интерфейса вызова удаленных методов (RMI);

·                     отсутствие поддержки технологии JNI;

·                     наличие нестандартных расширений, таких, как средства интеграции Java и DCOM, работающих только на платформе Windows.

Язык Java активно используется для создания мобильных приложений под операционную систему Android. При этом программы компилируются в нестандартный байт-код, для использования их виртуальной машиной Dalvik. Для такой компиляции используется дополнительный инструмент, а именно Software Development Kit, разработанный компанией Google.

Разработку приложений можно вести в среде Android Studio, NetBeans, в среде Eclipse, используя при этом плагин Android Development Tools (ADT) или в IntelliJ IDEA. Версия JDK при этом должна быть 5.0 или выше.

8 декабря 2014 года Android Studio признана компанией Google официальной средой разработки под ОС Android.

Некоторые платформы предлагают аппаратную поддержку выполнения для Java. К примеру, микроконтроллеры, выполняющие код Java на аппаратном обеспечении вместо программной JVM, а также основанные на ARM процессоры, которые поддерживают выполнение байткода Java через опцию Jazelle. Основные возможности:

·                     автоматическое управление памятью;

·                     расширенные возможности обработки исключительных ситуаций;

·                     богатый набор средств фильтрации ввода-вывода;

·                     набор стандартных коллекций: массив, список, стек и т. п.;

·                     наличие простых средств создания сетевых приложений (в том числе с использованием протокола RMI);

·                     наличие классов, позволяющих выполнять HTTP-запросы и обрабатывать ответы;

·                     встроенные в язык средства создания многопоточных приложений;

·                     унифицированный доступ к базам данных:

·                     на уровне отдельных SQL-запросов — на основе JDBC, SQLJ;

·                     на уровне концепции объектов, обладающих способностью к хранению в базе данных — на основе Java Data Objects (англ.) и Java Persistence API;

·                     поддержка обобщений (начиная с версии 1.5);

·                     параллельное выполнение программ

 

 

1.2.2. Язык Dart  для операционной системы Android

Dart - язык программирования, созданный Google. Dart позиционируется в качестве замены/альтернативы JavaScript. Один из разработчиков языка Марк Миллер написал, что JavaScript имеет фундаментальные изъяны, которые невозможно исправить, поэтому и был создан Dart.

10 октября 2011 была проведена официальная презентация языка Google Dart.

Dart представляет язык программирования общего назначения от компании Google, который предназначен прежде всего для разработки веб-приложений (как на стороне клиента, так и на стороне сервера) и мобильных приложений. Это значит, что одну и ту же программу на Dart можно компилировать под различные платформы - Windows, Android, iOS.

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

В своем развитии Dart испытал влияние более ранних языков, таких как Smalltak, Java, JavaScript. Его синтаксис похож на синтаксис других языков.

Dart быстро развивается и текущая версия - 2.12.Для работы с Dart необходимо установить Dart SDK. Для этого нужно загрузить zip-архив с SDK с адреса https://dart.dev/tools/sdk/archive и распаковать его на жестком диске.На странице загрузки есть пакеты для Windows, Linux, MacOS. Также доступны различные сборки для разработчиков.

Любое приложение на языке Dart должно иметь функцию, которая называется main. Эта функция имеет тип void и не принимает никаких параметров, поэтому после названия функции идут пустые скобки.

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

С помощью утилиты dart.exe можно запустить программу, однако можно создать исполняемый файл, чтобы в любое время его можно было запускать без обращения к dart.exe и переносить на другой компьютер с той же операциионной системой. Для этого в SDK есть другая утилита - dart2native.exe, которая позволяет скомпилировать нативный исполняемый файл программы. В качестве параметра она принимает исходный файл, который надо скомилировать. А после флага -o можно указать путь и название файла, который будет скомпилирован.

1.3. Жизненный  цикл мобильных приложений

1.3.1. Жизненный цикл приложения

Активность – окно, несущее графический интерфейс пользователя. Окно активности обычно занимает весь экран устройства, однако вполне возможно создавать полупрозрачные или плавающие диалоговые окна. Мобильные приложения обычно являются многооконными, т. е. содержат несколько активностей, по одной на каждое окно. Одна из активностей определяется как "главная", и именно ее пользователь видит при первом запуске приложения.

Каждый экран приложения является наследником класса Activity. Для создания активности необходимо создать класс-наследник класса Activity напрямую или через любого его потомка. В этом классе необходимо реализовать все методы, вызываемые системой для управления жизненным циклом активности. Таких методов семь:

 

onCreate()

- метод, вызываемый системой при создании активности. В реализации метода необходимо инициализировать основные компоненты активности и в большинстве случаев вызвать метод setContentView() для подключения соответствующего XML-файла компоновки (layoutfile). После метода onCreate() всегда вызывается метод onStart().

onRestart()

- метод, вызываемый системой при необходимости запустить приостановленную активность. После этого метода всегда вызывается метод onStart().

onStart()

- метод, вызываемый системой непосредственно перед тем, как активность станет видимой для пользователя. После этого метода вызывается onResume().

onResume()

- метод, вызываемый системой непосредственно перед тем, как активность начнет взаимодействовать с пользователем. После этого метода всегда вызывается onPause().

onPause()

- метод, вызываемый системой при потере активностью фокуса. В этом методе необходимо фиксировать все изменения, которые должны быть сохранены за пределами текущей сессии. После этого метода вызывается onResume(), если активность вернется на передний план, или onStop(), если активность будет скрыта от пользователя.

onStop()

- метод, вызываемый системой, когда активность становится невидимой для пользователя. После этого метода вызывается либо onRestart(), если активность возвращается к взаимодействию с пользователем, либо onDestroy(), если активность уничтожается.

onDestroy()

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

МетодonRestoreInstanceState

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

МетодonSaveInstanceState

Метод onSaveInstanceState вызывается после метода onPause(),  до вызова onStop(). В onSaveInstanceState производится сохранение состояния приложения в передаваемый в качестве параметра объект Bundle.

Рис. 1.1.Жизненный цикл активности

 

Переходмежду состояниями activity можно выразить следующей схемой:

 

Рис. 1.2.Переход между состояниями activity

 

Расмотрим несколько ситуаций. Если  работаем с Activity и затем переключаемся на другое приложение, либо нажимаем на кнопку Home, то у Activity вызывается следующая цепочка методов: onPause -> onStop. Activity оказывается в состоянии Stopped. Если пользователь решит вернуться к Activity, то вызывается следующая цепочка методов: onRestart -> onStart -> onResume.

Другая ситуация, если пользователь нажимает на кнопку Back (Назад), то вызывается следующая цепочка onPause -> onStop -> onDestroy. В результате Activity уничтожается. Если  вдруг захотим вернуться к Activity через диспетчер задач или заново открыв приложение, то activity будет заново пересоздаваться через методы onCreate -> onStart -> onResume

Запускприложения:

onCreate() → onStart() →  onResume()

Нажата кнопку Назад для выхода из приложения:

onPause() → onStop() → onDestroy()

Нажата кнопка Домой:

onPause() → onStop()

После нажатия кнопки Домой, когда приложение запущено из списка недавно открытых приложений или через значок:

onRestart() → onStart() → onResume()

Когда запускается другое приложение из области уведомлений или открывается приложение Настройки:

onPause() → onStop()

Нажата кнопка Назад в другом приложении или в Настройках и ваше приложение стало снова видимым:

onRestart() → onStart() → onResume()

Открывается диалоговое окно:

onPause()

Диалоговое окно закрывается:

onResume()

Кто-то звонит на телефон:

onPause() → onResume()

Пользователь отвечает на звонок:

onPause()

Разговор окончен:

onResume()

Экран телефона гаснет:

onPause() → onStop()

Экран снова включён:

onRestart() → onStart() → onResume()

При повороте активность проходит через цепочку различных состояний. Порядокследующий.

onPause()->onStop()->onDestroy()->onCreate()->onStart()->onResume()

Порядоквызова методов:

после onCreate() - onStart()

после onRestart() - onStart()

после onStart() - onResume() или onStop()

после onResume() - onPause()

после onPause() - onResume() или onStop()

после onStop() - onRestart() или onDestroy()

после onDestroy() – ничего

1.3.2. Управление жизненным циклом

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

packagecom.example.viewsapplication;

 importandroid.support.v7.app.AppCompatActivity;

importandroid.os.Bundle;

importandroid.util.Log;

 publicclassMainActivity extendsAppCompatActivity {

     privatefinalstaticString TAG = "MainActivity";

     protectedvoidonCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main);

        Log.d(TAG, "onCreate");

    }

     @Override

    protectedvoidonDestroy(){

        super.onDestroy();

        Log.d(TAG, "onDestroy");

    }

    @Override

    protectedvoidonStop(){

        super.onStop();

        Log.d(TAG, "onStop");

    }

    @Override

    protectedvoidonStart(){

        super.onStart();

        Log.d(TAG, "onStart");

    }

    @Override

    protectedvoidonPause(){

        super.onPause();

        Log.d(TAG, "onPause");

    }

    @Override

    protectedvoidonResume(){

        super.onResume();

        Log.d(TAG, "onResume");

    }

     @Override

    protectedvoidonRestart(){

        super.onRestart();

        Log.d(TAG, "onRestart");

    }

     @Override

    protected  voidonSaveInstanceState(Bundle outState){

        super.onSaveInstanceState(outState);

        Log.d(TAG, "onSaveInstanceState");

    }

     @Override

    protected  voidonRestoreInstanceState(Bundle savedInstanceState){

        super.onRestoreInstanceState(savedInstanceState);

        Log.d(TAG, "onRestoreInstanceState");

    }

}

Для логгирования событий здесь используется класс android.util.Log.

В примере обрабатываются все ключевые методы жизненного цикла. Вся обработка сведена к вызову метода Log.d(), в который передается TAG - случайное строковое значение и строка, которая выводится в консоли logcat внизу Android Studio в окне Android Monitor, выполняя роль отладочной информации. Если эта консоль по умолчанию скрыта, то можно перейти к ней через пункт меню View -> Tool Windows -> Android Monitor.

И при запуске приложения  сможно увидеть в окне logcat отладочную информацию, которая определяется в методах жизненного цикла activity:

Рис. 1.3. Окно logcat

 

При создании экранов графического интерфейса пользователя наследуется класс Activity и используются представления (View) для взаимодействия с пользователем.

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

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

Для создания новой Активности наследуется класс Activity. Внутри реализации класса необходимо определить пользовательский интерфейс и реализовать требуемый функционал. Базовый каркас для новой Активности показан ниже:

package com.example.myapplication;

import android.app.Activity;

import android.os.Bundle;

public class MyActivity extends Activity {
/** Вызывается при создании Активности */

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
}

Базовый класс Activity представляет собой пустой экран, который не особенно полезен, поэтому первое, что вам нужно сделать, это создать пользовательский интерфейс с помощью Представлений (View) и разметки(Layout).

Представления (View) – это элементы UI, которые отображают информацию и обеспечивают взаимодействие с пользователем. Android предоставляет несколько классов разметки (Layout), называемых также View Groups, которые могут содержать внутри себя несколько Представлений, для создания пользовательского интерфейса приложения.

Чтобы назначить пользовательский интерфейс для Активности, внутри обработчика onCreate используется метод setContentView:

@Override
public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

TextView textView = new TextView(this);

setContentView(textView);

}

В этом примере в качестве UI для Активности выступает объект класса TextView.

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

Самое главное, что такая адаптация приложения к новым условиям не требует каких-либо изменений в коде приложения, нужно только обеспечить необходимые ресурсы (картинки, локализованные строки и т. п..). Это стандартный для Android подход показан ниже:

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.main);

}

Для использования Активности в приложении ее необходимо зарегистрировать в Манифесте путем добавления элемента <activity> внутри узла <application>, в противном случае ее невозможно будет использовать. Ниже показано, как создать элемент <activity> для Активности MyActivity:

<activity android:label="@string/app_name"

android:name=".MyActivity">

</activity>

В теге <activity> можно добавлять элементы <intent-filter> для указания Намерений (Intent), которые Активность будет отслеживать. Каждый «Фильтр Намерений» определяет одно или несколько действий (action) и категорий (category), которые поддерживаются Активностью. Важно знать, что Активность будет доступна из главного меню запуска приложений только в случае, если в Манифесте для нее указан <intent-filter> для действия MAIN и категории LAUNCHER, как показано на примере:

<activity android:label="@string/app_name"
android:name=".MyActivity">

<intent-filter>

<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />

</intent-filter>

</activity>

Для создания приложений, правильно управляющих ресурсами предоставляющих пользователю удобный интерфейс, важно хорошее понимание жизненного цикла Активности. Это связано с тем, что приложения Android не могут контролировать свой жизненный цикл, ОС сама управляет всеми процессами и, как следствие, Активностями внутри них. При этом, состояние Активности помогает ОС определить приоритет родительского для этой Активности Приложения (Application). А приоритет Приложения влияет на то, с какой вероятности его работа (и работа дочерних Активностей) будет прервана системой.

Состояние каждой Активности определяется ее позицией в стеке (LIFO) Активностей, запущенных в данный момент. При запуске новой Активности представляемый ею экран помещается на вершину стека. Если пользователь нажимает кнопку «назад» или эта Активности закрывается каким-то другим образом, на вершину стека перемещается (и становится активной) нижележащая Активность. Данный процесс показан на диаграмме:

http://yosic.kz/uploads/posts/2013-02/1360234841_001.jpg

Рис 1.4. Состояние активности

На приоритет приложения влияет его самая приоритетная Активность. Когда диспетчер памяти ОС решает, какую программу закрыть для освобождения ресурсов, он учитывает информацию о положении Активности в стеке для определения приоритета приложения.

1.3.3. Состояниеактивностей

Активности могут находиться в одном из четырех возможных состояний:

·                   активное (аctive). Активность находится на переднем плане (на вершине стека) и имеет возможность взаимодействовать с пользователем. Android будет пытаться сохранить ее работоспособность любой ценой, при необходимости прерывая работу других Активностей, находящихся на более низких позициях в стеке для предоставления необходимых ресурсов. При выходе на передний план другой Активности работа данной Активности будет приостановлена или остановлена.

·                   приостановленное (рaused). Активность может быть видна на экране, но не может взаимодействовать с пользователем: в этот момент она приостановлена. Это случается, когда на переднем плане находятся полупрозрачные или плавающие (например, диалоговые) окна. Работа приостановленной Активности может быть прекращена, если ОС необходимо выделить ресурсы Активности переднего плана. Если Активность полностью исчезает с экрана, она останавливается.

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

·                   неактивное (inactive). Когда работа Активности завершена, и перед тем, как она будет запущена, данная Активности находится в неактивном состоянии. Такие Активности удаляются из стека и должны быть (пере)запущены, чтобы их можно было использовать.

Изменение состояния приложения – недетерминированный процесс и управляется исключительно менеджером памяти Android. При необходимости Android вначале закрывает приложения, содержащие неактивные Активности, затем остановленные и, в крайнем случае, приостановленные.

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

Обработчики событий класса Activity позволяют отслеживать изменения состояний соответствующего объекта Activity во время всего жизненного цикла:

http://yosic.kz/uploads/posts/2013-02/1360299504_111.jpg

Рис. 1.5. Отслеживание изменения состояний активности

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

package com.example.myapplication;

import android.app.Activity;

import android.os.Bundle;
public class MyActivity extends Activity {
// Вызывается при создании Активности

@Override
public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);
// Инициализирует Активность.

}
// Вызывается после завершения метода onCreate
// Используется для восстановления состояния UI

@Override
public void onRestoreInstanceState(Bundle savedInstanceState) {

super.onRestoreInstanceState(savedInstanceState);
// Восстановить состояние UI из объекта savedInstanceState.
// Данный объект также был передан методу onCreate.

}
// Вызывается перед тем, как Активность снова становится видимой

@Override
public void onRestart(){

super.onRestart();
// Восстановить состояние UI с учетом того,
// что данная Активность уже была видимой.

}
// Вызывается когда Активность стала видимой

@Override
public void onStart(){

super.onStart();
//Проделать необходимые действия для
// Активности, видимой на экране

}
// Должен вызываться в начале видимого состояния.
// На самом деле Android вызывает данный обработчик только
// для Активностей, восстановленных из неактивного состояния

@Override
public void onResume(){

super.onResume();
// Восстановить приостановленные обновления UI,
// потоки и процессы, «замороженные, когда
// Активность была в неактивном состоянии

}
// Вызывается перед выходом из активного состояния,
// позволяя сохранить состояние в объекте savedInstanceState

@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
// Объект savedInstanceState будет в последующем
// передан методам onCreate и onRestoreInstanceState

super.onSaveInstanceState(savedInstanceState);

}
// Вызывается перед выходом из активного состояния

@Override
public void onPause(){
// «Заморозить» обновления UI, потоки или
// «трудоемкие» процессы, ненужные, когда Активность
// не на переднем плане

super.onPause();

}
// Вызывается перед выходом из видимого состояния

@Override
public void onStop(){
// «Заморозить» обновления UI, потоки или
// «трудоемкие» процессы, ненужные, когда Активность
// не на переднем плане.
// Сохранить все данные и изменения в UI, так как
// процесс может быть в любой момент убит системой

super.onStop();

}
// Вызывается перед уничтожением активности

@Override
public void onDestroy(){
// Освободить все ресурсы, включая работающие потоки,
// соединения с БД и т. д.

super.onDestroy();}
}

1.3.4. Процессы и потоки

Когда компонент приложения запускается при отсутствии других работающих компонентов , система Android запускает новый процесс Linux для приложения с одним потоком выполнения. По умолчанию все компоненты одного приложения работают в одном процессе и потоке (называется «главным потоком»). Если компонент приложения запускается при наличии процесса для этого приложения (так как существует другой компонент из приложения), тогда компонент запускается в этом процессе и использует тот же поток выполнения. Однако можно организовать выполнение других компонентов приложения в отдельных процессах и создавать дополнительный поток для любого процесса.

В этом документе обсуждается работа процессов и потоков в приложении Android.

Процессы

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

Запись манифеста для каждого типа элементов компонента — <activity>, <service>, <receiver> и <provider> — поддерживает атрибут android:process, позволяющий задавать процесс, в котором следует выполнять этот компонент.

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

Можно также настроить процесс android:process так, чтобы компоненты разных приложений выполнялись в одном процессе, при условии что приложения совместно используют один идентификатор пользователя Linux и выполняют вход с одним сертификатом.

Элемент <application> также поддерживает атрибут android:process, позволяющий задать значение по умолчанию, которое применяется ко всем компонентам.

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

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

Жизненный цикл процесса

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

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

1.      Процесс переднего плана

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

•        он содержит действие Activity, с которым взаимодействует пользователь (вызван метод Activity onResume()).

•        он содержит службу Service, связанную с действием, с которым взаимодействует пользователь.

•        он содержит службу Service, которая выполняется "на переднем плане", — службу, которая называется startForeground().

•        он содержит службуService, которая выполняет один из обратных вызовов жизненного цикла (onCreate(), onStart() или onDestroy()).

•        он содержит ресивер BroadcastReceiver, который выполняет метод onReceive().

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

2.      Видимые процессы

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

•        Он содержит действие Activity, которое не находится на переднем плане, но видно пользователю (вызван метод onPause()). Например, это может происходить, если действие на переднем плане запустило диалоговое окно, которое позволяет видеть предыдущее действие позади него.

•        Он содержит службу Service, связанную с видимым действием или действием переднего плана.

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

3.      Служебный процесс

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

4.      Фоновый процесс

Процесс, содержащий действия, которые не видны пользователю в настоящее время (вызван метод onStop() действия). Эти процессы не оказывают непосредственного воздействия на работу пользователя, и система может удалить их в любой момент, чтобы освободить память для процессов переднего плана, видимых или служебных процессов. Обычно выполняется множество фоновых процессов, поэтому они хранятся в списке LRU (недавно использованные), чтобы процессы, содержащие самые недавние действия, которые видел пользователь, удалялись в последнюю очередь. Если для действия правильно реализованы методы жизненного цикла, и действие сохраняет текущее состояние, удаление процесса этого действия не оказывает видимого воздействия на работу пользователя, так как когда пользователь возвращается к этому действию, оно восстанавливает все элементы своего видимого состояния.

5.      Пустой процесс

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

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

Кроме того, уровень процесса может быть повышен, поскольку имеются другие процессы, зависимые от него. Например, процесс, обслуживающий другой процесс, не может иметь уровень ниже уровня обслуживаемого процесса. Например, если поставщик контента в процессе A обслуживает клиента в процессе B или служебный процесс A связан с компонентом в процессе B, процесс A всегда считается не менее важным, чем процесс B.

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

Потоки

При запуске приложения система создает поток выполнения для приложения, который называется «главным». Этот поток очень важен, так как он отвечает за диспетчеризацию событий на виджеты соответствующего интерфейса пользователя, включая события графического представления. Он также является потоком, в котором приложение взаимодействует с компонентами из набора инструментов пользовательского интерфейса Android (компонентами из пакетов android.widget и android.view). По существу, главный поток — это то, что иногда называют потоком пользовательского интерфейса.

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

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

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

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

Кроме того, набор инструментов пользовательского интерфейса Android не является потокобезопасным. Поэтому, вы не должны работать с пользовательским интерфейсом из рабочего потока. Манипуляции с пользовательским интерфейсом необходимо выполнять из потока пользовательского интерфейса. Таким образом, существует только два правила однопоточной модели Android:

1.      Не блокируйте поток пользовательского интерфейса

2.      Не обращайтесь к набору инструментов пользовательского интерфейса Android снаружи потока пользовательского интерфейса

 

Контрольные вопросы

1.                Какие платформы используются для создания ОС мобильных приложений?

2.                Абстрактные классы и их использование

3.                Анонимные  классы и их использование

4.                Использование шаблонов и контейнеров

5.                Зачем нужны абстрактные классы?

6.                Какие платформы используются для создания ОС мобильных приложений?

7.                Абстрактные классы и их использование

8.                Анонимные  классы и их использование

9.                Использование шаблонов и контейнеров

10.           Зачем нужны абстрактные классы?

 

 

 

 

 

 

 

 

 

 

 

 

 

 

2. РАБОТА С МОБИЛЬНЫМИ УСТРОЙСТВАМИ

2.1.Понятие мобильнойоперационной системы

2.1.1.Понятие ОС в мобилных устройствах

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

Мобилная операционная система – это системы предназначенные для управления мобильных устройств. Самые  распространенные операционные системы для мобильных устройств:

    Symbian OS

    Windows Mobile

    Google Android

    Apple iOS

    BlackBerry OS

2.1.2. ОС Symbian

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

Каждый разработчик создавал свой дистрибутив этой операционной системы в зависимости от ограничений аппаратной платформы, под которую она разрабатывалась. Так появились версии Series 60, Series 80, Series 90, UIQ и MOAP. Каждая версия обладала своими особенностями, что делало необходимым под каждую версию разрабатывать свои приложения. Это было неудобно, поэтому после появления Windows Mobile, Android и iPhoneOS утратила свою популярность среди производителей мобильных девайсов. Так компании Sony Ericsson и Samsung объявили что не будут больше поддерживать эту операционную систему. На данный момент из крупных производителей мобильных девайсов только компания Nokia использует эту ОС для своих смартфонов.

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

Недостатки ОС Symbian: для связи с ПК нужно устанавливать дополнительный софт, несовместимость программ для старых и новых версий.

2.1.2. Windows Mobile

Операционная система Windows Mobile разработана мировым лидером в производстве операционных систем – компанией Microsoft. Эта система использует такой же программный интерфейс, что и настольная версия. Это делает написание программ более простым, а пользователям нравится удобный и понятный интерфейс, знакомый им с настольной Windows. Windows Mobile является компонентной, многозадачной, много поточной и много платформенной операционной системой. Благодаря этому она сыскала широкое распространение на мобильных устройствах.

Достоинства ОС Windows Mobile: схожесть с настольной версией, удобная синхронизация, в комплекте идут офисные программы, многозадачность.

Недостатки ОС Windows Mobile: высокие требования к оборудованию, наличие большого числа вирусов, нестабильности в работе.

2.1.3. ОС Android 

ОС Android — одна из самых молодых мобильных ОС, основанная на базе операционной системы Linux и разрабатываемая Open Handset Alliance (OHA) при поддержке Google. Исходный код находится в открытом доступе, благодаря чему любой разработчик может создать свою версию этой мобильной ОС. Разработчикам  приложений выдвинуто небольшое количество ограничений, благодаря чему существует множество как платных, так и бесплатных приложений, которые можно удобно загрузить с Android Market.

Достоинства ОС Android: гибкость, открытые исходные коды, множество программ, высокое быстродействие, удобное взаимодействие с сервисами от Google, многозадачность.

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

2.1.4. OС iPhone

OС iPhone мобильная операционная система от компании Apple. Данная система получила распространение только на продуктах компании Apple. Применяется в iPhone, iPod, iPad а также телевизионной приставке AppleTV.

Достоинства OС iPhone: удобство пользования, качественная служба поддержки, регулярные обновления, устраняющие многие проблемы в работе, возможность купить в App Store множество различных программ.

Недостатки iPhone: необходимость джайлбрейка для установки неофициальных приложений, заблокированный характер ОС, отсутствие многозадачности, нет встроенного редактора документов.

2.1.5. ОС Palm

Операционная система Palm появилась в 1996 году. Применялась в КПК. Была очень распространена из-за широких возможностей и удобства пользователей. К настоящему моменту практически не применялась, но в этом году разработчика поглотила компания HP. Благодаря этому появились надежды на воскрешение некогда популярной среди КПК операционной системы.

Достоинства ОС Palm: нетребовательна к ресурсам, очень удобный интерфейс пользователя, удобная синхронизация с ПК, надежность.

Недостатки ОС Palm: отсутствует полноценная многозадачность, не развиты мультимедийные функции, система не развивается.

2.1.6. BlackBerry

Операционная система BlackBerry работает исключительно на устройствах, выпускаемых компанией Research In Motion Limited (RIM). Ориентирована на корпоративных пользователей. Свое название получила от смартфонов для которых создавалась, так как клавиатура смартфонов были похожи на ягоду ежевики. Смартфоны с этой операционной системой получили распространение в корпоративной среде, благодаря сложности перехвата сообщений.

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

Недостатки ОС BlackBerry: оптимизирована для вывода только текстовой информации, качество работы с графикой не очень хорошее, не очень удобный браузер.

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

 

2.2. Платформа и архитектура мобильных операционных систем

2.2.1. Платформа Андроид и его основа

Androidоперационная система для мобильных устройств: смартфонов, планшетных компьютеров, КПК. В настоящее время именно Android является самой широко используемой операционной системой для мобильных устройств.

В 2003 году в городе Пало Альто Энди Рубин с Рич Майнер, Ник Сирс и Крис Уайтом основали компанию Android Inc. Поначалу в компании занимались проектированием мобильных гаджетов, которые на основе геолокационных данных автоматически подстраивались под нужды пользователей.

В августе 2005 года Android Inc. стала дочерней компанией Google. Энди Рубин, Рич Майнер и Крис Уайт остались в Android Inc. и начали работать над операционной системой, базирующейся на ядре Linux. В Google задумали реализовать мощнейшую платформу, пригодную к использованию на тысячах различных моделей телефонов. В связи с этим был создан Open Handset Alliance (OHA) - консорциум, состоящий из более 80 компаний, направляющий свои усилия на разработку открытых стандартов для мобильных устройств. Всостав OHA входяттакиегиганты, как Google, HTC, Sony, Dell, Intel, Motorola, Qualcomm, Texas Instruments, Samsung Electronics, LG Electronics, T-Mobile, Sprint Corporation, NVIDIA имногиедругие.

Первая версия Android была представлена 23 сентября 2008 года, версии было дано название Apple Pie. Далее так повелось, что название каждой очередной версии представляет какой-либо десерт, при этом первые буквы наименований в порядке версий соответствуют буквам латинского алфавита по порядку.

Платформа Android объединяет операционную систему, построенную на основе ядра ОС Linux, промежуточное программное обеспечение и встроенные мобильные приложения. Разработка и развитие мобильной платформы Android выполняется в рамках проекта AOSP (AndroidOpen Source Project) под управлением OHA (OpenHandset Alliance), руководит всем процессом поисковый гигант Google.

Android поддерживает фоновое выполнение задач; предоставляет богатую библиотеку элементов пользовательского интерфейса; поддерживает 2D и 3D графику, используя OpenGL стандарт; поддерживает доступ к файловой системе и встроенной базе данных SQLite.

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

·       базовый уровень (Linux Kernel) - уровень абстракции между аппаратным уровнем и программным стеком;

·       набор библиотек и среда исполнения (Libraries & Android Runtime) обеспечивает важнейший базовый функционал для приложений, содержит виртуальную машину Dalvik и базовые библиотеки Java необходимые для запуска Android приложений;

·       уровень каркаса приложений (Application Framework) обеспечивает разработчикам доступ к API, предоставляемым компонентами системы уровня библиотек;

·       уровень приложений (Applications) - набор предустановленных базовых приложений.

Наглядное изображение архитектуры на рисунке 2.1.

В основании компонентной иерархии лежит ядро ОС Linux 2.6, служит промежуточным уровнем между аппаратным и программным обеспечением, обеспечивает функционирование системы, предоставляет системные службы ядра: управление памятью, энергосистемой и процессами, обеспечение безопасности, работа с сетью и драйверами.

Уровнем выше располагается набор библиотек и среда исполнения. Библиотеки реализуют следующие функции:

·       предоставляют реализованные алгоритмы для вышележащих уровней;

·       обеспечивает поддержку файловых форматов;

·       осуществляет кодирование и декодирование информации (например, мультимедийные кодеки);

выполняет отрисовку графики и т.д

Архитектура Android

Рис. 2.1.Архитектура Android

Библиотеки реализованы на С/С++ и скомпилированы под конкретное аппаратное обеспечение устройства, вместе с которым они и поставляются производителем в предустановленном виде. Рассмотрим некоторые библиотеки:

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

Media Framework – библиотеки, реализованные на базе PacketVideo OpenCORE. Используются для записи и воспроизведения аудио и видео контента, а также для вывода статических изображений. Поддерживаются форматы: MPEG4, H.264, MP3, AAC, AMR, JPG и PNG.

SQLite – легковесная  и производительная реляционная СУБД, используется в Android в качестве основного движка для работы с базами данных.

3D библиотеки – используются  для высокооптимизированной отрисовки 3D-графики, при возможности используют аппаратное ускорение. Библиотеки реализованы на основе API OpenGL|ES. OpenGL|ES (OpenGL for Embedded Systems) - подмножество графического программного интерфейса OpenGL, адаптированное для работы на встраиваемых системах.

FreeType – библиотека  для работы с битовыми картами, для растеризации шрифтов и осуществления операций над ними.

LibWebCore – библиотеки браузерного движка WebKit, используемого также в известных браузерах Google Chrome и Apple Safari.

SGL (Skia Graphics Engine) – открытый движок для работы с 2D-графикой. Графическая библиотека является продуктом Google и часто используется в других программах.

SSL – библиотеки для поддержки одноименного криптографического протокола.

Libc – стандартная библиотека языка С, а именно ее BSD реализация, настроенная для работы на устройствах на базе Linux.

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

Для исполнения на виртуальной машине Dalvik Java-классы компилируются в исполняемые файлы с расширением .dex с помощью инструмента dx, входящего в состав AndroidSDK. DEX (Dalvik EXecutable) - формат исполняемых файлов для виртуальной машины Dalvik, оптимизированный для использования минимального объема памяти. При использовании IDE Eclipse и плагина ADT (AndroidDevelopmentTools) компиляция классов Java в формат .dex происходит автоматически.

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

На еще более высоком уровне располагается каркас приложений (Application Framework), архитектура которого позволяет любому приложению использовать уже реализованные возможности других приложений, к которым разрешен доступ. В состав каркаса входят следующие компоненты:

·                     богатый и расширяемый набор представлений (Views), который может быть использован для создания визуальных компонентов приложений, например, списков, текстовых полей, таблиц, кнопок или даже встроенного web-браузера;

·                     контент-провайдеры (Content Providers), управляющие данными, которые одни приложения открывают для других, чтобы те могли их использовать для своей работы;

·                     менеджер ресурсов (Resource Manager), обеспечивающий доступ к ресурсам без функциональности (не несущим кода), например, к строковым данным, графике, файлам и другим;

·                     менеджер оповещений (Notification Manager), позволяющий приложениям отображать собственные уведомления для пользователя в строке состояния;

·                     менеджер действий (Activity Manager), управляющий жизненными циклами приложений, сохраняющий историю работы с действиями, предоставляющий систему навигации по действиям;

·                     менеджер местоположения (Location Manager), позволяющий приложениям периодически получать обновленные данные о текущем географическом положении устройства.

Application Framework предоставляет в распоряжение приложений в ОС Android вспомогательный функционал, благодаря чему реализуется принцип многократного использования компонентов приложений и ОС. Естественно, в рамках политики безопасности.

Самый высокий и самый близкий к пользователю это – уровень приложений. Именно на этом уровне пользователь взаимодействует со своим устройством, управляемым ОС Android. Здесь представлен набор базовых приложений, который предустановлен на ОС Android. Например, браузер, почтовый клиент, программа для отправки SMS, карты, календарь, менеджер контактов и др. Список интегрированных приложений может меняться в зависимости от модели устройства и версии Android. К этому уровню также относятся все пользовательские приложения.

Разработчик обычно взаимодействует с двумя верхними уровнями архитектуры Android для создания новых приложений. Библиотеки, система исполнения и ядро Linux скрыты за каркасом приложений.

2.2.2. Основные виды Android-приложений

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

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

2) Фоновые приложения после настройки не предполагают взаимодействия с пользователем, большую часть времени находятся и работают в скрытом состоянии. Примерами таких приложений могут служить, службы экранирования звонков, SMS-автоответчики. В большинстве своем фоновые приложения нацелены на отслеживание событий, порождаемых аппаратным обеспечением, системой или другими приложениями, работают незаметно. Можно создавать совершенно невидимые сервисы, но тогда они будут неуправляемыми. Минимум действий, которые необходимо позволить пользователю: санкционирование запуска сервиса, настройка, приостановка и прерывание его работы при необходимости.

3) Смешанные приложения большую часть времени работают в фоновом режиме, однако допускают взаимодействие с пользователем и после настройки. Обычно взаимодействие с пользователем сводится к уведомлению о каких-либо событиях. Примерами таких приложений могут служить мультимедиа-проигрыватели, программы для обмена текстовыми сообщениями (чаты), почтовые клиенты. Возможность реагировать на пользовательский ввод и при этом не терять работоспособности в фоновом режиме является характерной особенностью смешанных приложений. Такие приложения обычно содержат как видимые активности, так и скрытые (фоновые) сервисы, и при взаимодействии с пользователем должны учитывать свое текущее состояние. Возможно, потребуется обновлять графический интерфейс, если приложение находится на переднем плане, или же посылать пользователю уведомления из фонового режима, чтобы держать его в курсе происходящего. И эти особенности необходимо учитывать при разработке подобных приложений.

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

2.2.3. Архитектура приложения, основные компоненты

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

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

Когда система запускает компонент, она запускает процесс приложения, которому принадлежит компонент, если он еще не запущен, и создает экземпляры классов, необходимых компоненту. Поэтому в отличие от большинства других систем, в системе Android приложения не имеют единой точки входа (нет метода main(), например). В силу запуска каждого приложения в отдельном процессе и ограничений на доступ к файлам, приложение не может напрямую активировать компонент другого приложения. Таким образом для активации компонента другого приложения необходимо послать системе сообщение о намерении запустить определенный компонент, система активирует его.

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

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

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

Контент-провайдеры (Content providers). Контент-провайдер управляет распределенным множеством данных приложения. Данные могут храниться в файловой системе, в базе данных SQLite, в сети, в любом другом доступном для приложения месте. Контент-провайдер позволяет другим приложениям при наличии у них соответствующих прав делать запросы или даже менять данные. Например, в системе Android есть контент-провайдер, который управляет информацией о контактах пользователя. В связи с этим, любое приложение с соответствующими правами может сделать запрос на чтение и запись информации какого-либо контакта. Контент-провайдер может быть также полезен для чтения и записи приватных данных приложения, не предназначенных для доступа извне.

Приемники широковещательных сообщений (Broadcast Receivers). Приемник - компонент, который реагирует на широковещательные извещения. Большинство таких извещений порождаются системой, например, извещение о том, что экран отключился или низкий заряд батареи. Приложения также могут инициировать широковещание, например, разослать другим приложениям сообщение о том, что некоторые данные загружены и доступны для использования. Хотя приемники не отображают пользовательского интерфейса, они могут создавать уведомление на панели состояний, чтобы предупредить пользователя о появлении сообщения. Такой приемник служит проводником к другим компонентам и предназначен для выполнения небольшого объема работ, например, он может запустить соответствующий событию сервис.

Все рассмотренные компоненты являются наследниками классов, определенных в AndroidSDK.

2.2.4. Установка и настройка инструментальных средств

Большинство приложений для OS Android написано на Java. Одной из самых популярных сред разработки является Eclipse с установленным плагином ADT и Android SDK. Раньше приходилось ставить все компоненты отдельно. Сейчас появилась версия среды Eclipse с уже настроенными дополнениями - ADT Bundle. Здесь есть минимум инструментов, необходимый для разработки приложений. С этой версией мы и будем работать. Однако в ней есть далеко не всё, поэтому, если при разработке какого-либо проекта вам потребуются инструменты, не входящие в ADT Bundle, вы можете скачать их с сайта разработчиков и дополнить свою среду.

Сайт разработчикаСкачивание среды

Рис. 2.2.Сайт разработчика

Для того, чтобы скачать среду необходимо принять условия лицензионного соглашения и выбрать вашу версию Windows (32-bit или 64-bit).

После скачивания распакуйте архив в ту папку, где собираетесь работать (среда не требует специальной установки). После распаковки зайдите в папку и запустите Eclipse. Здесь возможна небольшая проблема: если у вас не установлен JDK, среда не запустится и потребует указать путь к папке с JDK или установить его. Скачать JDK можно с сайта Oracle.

Сайт компании Oracle

Рис. 2.3.Сайт компании Oracle

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

Скачивание JDK

Рис. 2.4.Скачивание JDK

После скачивания запустите setup-файл и установите JDK.

 

Установка JDK

Рис. 2.5.Установка JDK

После установки JDK среда должна запуститься. Далее вам необходимо выбрать (или создать новое) рабочее пространство, т.е. место, где будут находиться ваши проекты. Если поставить галочку, то это рабочее пространство будет выбираться по умолчанию, а противном случае это окно будет появляться при каждом запуске Eclipse.

Выбор рабочего пространства

Рис. 2.6.Выбор рабочего пространства

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

 

Отправка статистики

Рис. 2.7.Отправка статистики

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

Android SDK Manager

Рис. 2.8.Android SDK Manager

 

Этапы создания приложений

Чтобы создать приложение, зайдите в меню File->New->Android Application Project.

Создание проекта

Рис. 2.9.Создание приложения

В появившемся окне обязательно нужно прописать имя приложения, имя проекта, а также имя пакета (package). Лучше не оставлять его именем example, т.к. пакет с таким именем нельзя разместить в Google Play. Конечно, учебные приложения туда не загружают, однако, следует иметь это в виду на будущее.

Наименование проекта

Рис. 2.10.Наименование приложения

Minimum Required SDK - минимальная версия Android, которую будет поддерживать приложение. Чаще всего по умолчанию указывается версия 2.2, чтобы поддерживать как можно больше устройств. Если определенная функция вашего приложения работает только на более новых версиях Android, и это не является критическим для основного набора функций приложения, вы можете включить ее в качестве опции на версиях, которые поддерживают его.

Target SDK - версия Android, под которую будет написано ваше приложение; определяет максимальную версию Android, на которой вы тестировали приложение. Это нужно для режимов совместимости.

Compile With определяет, возможности какой версии Android будет использовать приложение.

Следующее окно можно пропустить без изменений. Здесь: Create custom launcher icon - создать значок приложения.Create activity - создать Activity (активность, деятельность). Markthisprojectaslibrary - создать проект, как библиотеку. Сейчас в этом нет необходимости, наше приложение в других проектах использоваться не будет. CreateProjectinWorkspace - создать проект в папке Workspace. В этой папке будут храниться все наши проекты.

Конфигурация проекта

Рис. 2.11.Конфигурация приложения

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

Создание иконки приложения

Рис. 2.12.Создание иконки приложения

Большинство приложений на Android имеют свой экран (форму, окно), которое называется активностью или деятельностью (Activity). Следующее два окна создают пустую активность. В первом ничего пока менять не нужно. Во втором вы можете переименовать свою активность. Blank Activity - шаблон, предназначенный для мобильных телефонов. Fullscreen Activity - шаблон, позволяющий растянуть приложение на весь экран (без навигационной панели и статус-бара). Master/Detail Flow - шаблон, предназначенный для планшетных компьютеров.

 

Создание активностиПереименование активности

Рис. 2.13.Создание активности и переименование активности

Итак, вы создали свой первый проект. Конечно, это всего лишь встроенное в среду приложение для проверки корректной установки инструментария, однако множество приложений создаются именно из него.  Посмотрим на его структуру. Она показана в области слева. В первую очередь нас интересует файл активности. Он находится в папке src в вашем пакете. Он имеет расширение .java.

Активность

Рис. 2.14.Активность

В папке res в подпапке layout находится xml-файл, который является оболочкой нашей активности. Именно этот файл будет виден на экране устройства. C xml-файлами можно работать как в режиме графического редактора, так и непосредственно редактировать код.

Xml-файл. Графический редактор

Рис. 2.15.Xml-файл. Графический редактор

 

Xml-файл

Рис. 2.16.Xml-файл

Для запуска приложения на эмуляторе нужно создать эмулятор устройства. Это можно сделать, нажав на кнопку на панели инструментов, изображающую смартфон. Если кнопки нет на панели, ее можно найти в меню Window.

Запуск проекта

Рис. 2.17.Запуск приложения

ОткроетсяAndroidVirtualDeviceManager. Пока в нем нет ни одного виртуального устройства.

Android Virtual Device Manager

Рис. 2.18.AndroidVirtualDeviceManager

Чтобы создать виртуальное устройство, нажмите кнопку New. Появится окно создания. Вам нужно назвать устройство и выбрать обязательные характеристики: Device - модель вашего устройства, и Target - версия Android. Также можно изменять дополнительные параметры: размер sd-карты, встроенной памяти и т.п.

Создание AVD

Рис. 2.19.Создание AVD

Теперь можно запускать приложение. Для этого нужно нажать на кнопку Run (белый треугольник в зеленом кружочке) на панели инструментов. Проблемы с запуском можно отследить в консоли. Если приложение не запускается, попробуйте нажать на черный треугольник справа от кнопки Run, выбрать Run Configurations, затем во вкладке Target выбрать созданное устройство и запустить проект снова.

Запуск приложения

Рис. 2.20. Запуск приложения

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

Запуск эмулятораЗапущенный эмулятор
Рис. 2.21.Запуск эмулятора

 

Приложение Hello, world!Меню приложений

Рис. 2.22.Приложение «Hello world» и меню приложений

Если ваше приложение сразу не запустилось, его можно найти в меню приложений устройства.

2.3. Языки  программирования соответствующие платформам мобильных операционных систем

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

2.3.1. Языки OCAndroid

Java

Java является самым популярным языком программирования по состоянию на июнь 2021 года. Для ОС Android язык Java считается самым основным. Естьбольшое и развитое сообщество разработчиков, которые выполняють техническую поддержку и помощь.

Преимущества:

·                   естественный код для Android. ОС частично тоже написана на Java, а ядро составляют Linux и собственная виртуальная машина Virtual Machine.

·                   универсальный — запускается на всех платформах.

·                   позволяет легко масштабировать и обновлять проекты за счет объектно-ориентированного кода. То есть, тут код легче читается, пишется и обновляется, что ускоряет все процессы.

·                   большое количество готовых инструментов, которые по умолчанию совместимы с Java, что тоже увеличивает скорость.

Недостатки:

·                   требует большого объема оперативной памяти.

·                   платные обновления для коммерческого использования.

 

Kotlin

Kotlin спроектирован и разработан компанией JetBrains, известной своей популярной IDE, IntelliJ IDEA. КомандаAndroid от Google объявила, что официально добавляет поддержку языка программирования Kotlin.

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

Преимущества:

·                   позволяет обходиться меньшим количеством кода, чем на Java. Чем меньше текста, тем меньше в нем ошибок.

·                   Kotlin взаимозаменяем с Java, поэтому разные части интерфейса могут быть написаны на разных языках, но при этом отлично работать. Это помогает создавать более эффективные и высокопроизводительные программы.

·                   безопасность, все синтаксические ошибки и баги, связанные с неправильным обращением к объектам, можно найти и исправить во время сборки. Это упрощает тестирование.

·                   программы Kotlin используют фреймворки и библиотеки Java.

Недостатки:

·                   скорость сборки программы часто колеблется от быстрой до очень медленной.

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

2.3.2. ЯзыкиOC iOS

Swift

Если вы хотите разработать для iOS, Swift может стать для вас подходящим языком. Внедренный в 2014 году и объявленный открытым исходным кодом в 2015 году, Swift быстро покоряет мобильных разработчиков. Он очень популярен, особенно среди новых начинающих разработчиков iOS.

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

Преимущества:

·                   высокая скорость — доходит до уровня C++.

·                   простой для чтения. По логике он напоминает английский, а еще у него простой синтаксис и код.

·                   повышенная безопасность, если сравнивать с Objective С.

·                   упрощенный способ исправления ошибок в коде.

·                   стабильность за счет библиотек, которые автоматически связываются с обновленной версией и присоединяются к приложению.

·                   обеспечивает безопасное управление памятью.

Недостатки:

он развивается и меняется, поэтому работа может замедляться — нужно изучать и применять информацию об обновлениях.

мосты синхронизации с файлами Objective С тормозят сборку проекта.

Objective-C

Objective-C был оригинальным языком разработки для iOS. ЯзыкSwift является будущим развитием iOS, многие продвинутые проекты все еще полагаются на Objective-C. Переходот Objective-C к Swift ожидается несколько медленным, и вам может понадобиться оба из них для некоторых проектов.

Преимущества:

·        существует много документации, которая упрощает работу.

·        совместим со Swift.

Недостатки:

·        невысокая производительность по сравнению со Swift.

·        сложный синтаксис.

 

Rust

Rust начал создаваться в 2006 году разработчиком Грейдоном Хором, который хотел соединить в нем скорость C++ и надежность Haskell. В 2009-ом к нему присоединилась Mozilla. Сейчас Rust является одним из самых популярных среди разработчиков кроссплатформенных приложений.

Преимущества:

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

·                   ошибки во время компиляции видны сразу + предлагаются варианты исправления.

·                   сопоставим по скорости с C++.

·                   надёжный API для организации сетевого взаимодействия с использованием библиотек.

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

Недостатки:

·                   относительно новый и быстро развивается, поэтому нет подходящей литературы и выбора специалистов.

·                   строгий компилятор, который требует самостоятельного заполнения большого объема данных и замедляет процесс разработки.

2.3.3. Межплатформенные языки

JavaScript

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

Сегодня существует несколько инфраструктур JavaScript, специально предназначенных для мобильных платформ разработки, таких как Ionic 2 и React Native. С помощью этих фреймворков и библиотек очень легко разрабатывать кросс-платформенные мобильные приложения. Это означает, что вам нужно написать только одну версию приложения, и она будет работать на iOS или Android.

TypeScript

TypeScript - это надмножество JavaScript и обеспечивает лучшую безопасность, добавляя необязательную статическую типизацию. Он также обеспечивает лучшую поддержку для разработки крупномасштабных приложений. Разработанный и поддерживаемый Microsoft, TypeScript позволяет разработчикам писать кросс-платформенные мобильные приложения с использованием таких фреймворков, как NativeScript.

C#

C# - этоязыкWindowsMobile. Он очень похож на C ++ и Java. Microsoft применила некоторые функции Java для упрощения своей архитектуры, сохраняя при этом дизайн похожий на C ++. Он также имеет большое и активное сообщество разработчиков, которые всегда дружелюбны и полезны.

Преимущества:

·          Windows уделяет особое внимание поддержке, регулярно выпускает обновления и выявляет баги, поэтому работать с C# можно комфортно и быстро.

·          некоторые организации и индивидуальные разработчики могут пользоваться инструментами бесплатно.

·          ответы практически на все вопросы, связанные с работой в C#, можно найти в интернете или профессиональных сообществах.

·          большой набор инструментов и средств для работы C# позволяет пользоваться только одним языком.

·          автоматический режим очистки памяти от объектов, которые не используются.

·        корректно работает даже при переходе продукта на новую версию.

Недостатки:

·          работает практически на всех ОС, но все же приоритет основан на платформе Windows.

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

С

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

Созданный Dennis Ritchie, работая в Bell Labs, C - широко распространенный и мощный язык, который позволяет вам напрямую манипулировать низкоуровневыми операциями компьютера. Если вы хотите использовать Android NDK (Native Development Kit), вам нужно будет ознакомиться с языком C.

Плюсы:

·                   Вместе с Java позволяет сократить код, что ускорит работоспособность программы.

Минусы:

·                   Непростой в освоении;

·                   Не позволяет полноценно создать приложение, а всего лишь подвязывать библиотеки к приложению.

C++

C++ - это расширение C, с более высокоуровневыми функциями и поддержкой объектно-ориентированного программирования. C++ также является любимым языком разработчиков Android NDK. C++ можно использовать для разработки приложений для Windows Mobile. C++ идет вместе с Java в области разработки программного обеспечения.

Плюсы:

·                   поддерживает объективно-ориентированное программирование, процедурное программирование и обобщенное.

·                   улучшает производительность при работе с Objective-C.

Минусы:

·                   не предназначен для полноценной разработки;

·                   непростой в изучении.

Python

Python - язык, который легко усвоить и легко читать. Создатели языка приложили дополнительные усилия, чтобы синтаксис был максимально простым и понятным. Это помогает начинающим разработчикам поддерживать высокий уровень производительности с первого дня. Если вам удобно писать код Python, вы можете использовать такие фреймворки, как Kivy, для разработки кросс-платформенных мобильных приложений.

Плюсы:

·                   подходит как для нативных, так и для веб-приложений;

·                   позволяет строить нативные интерфейсы;

·                   легко читаемый синтаксис;

·                   Прост в изучении.

Минусы:

·                   не официальный язык Android и не поддерживает его без фреймворка Kivy;

·                   не особо востребованный.

Ruby

Ruby - это объектно-ориентированный язык сценариев, созданный под влиянием Ada, C ++, Perl, Python и Lisp. RubyMotion - платформа для разработки нативных и кросс-платформенных мобильных приложений в Ruby.

 

Контрольные вопросы:

1.                Для чего предназначена мобильная ОС?

2.                Опишите достоинства и недостатки ОС  Symbian.

3.                Опишите достоинства и недостатки ОС WindowsMobile.

4.                Опишите достоинства и недостатки ОС Palm.

5.                Опишите достоинства и недостатки ОС iPhoneOS.

6.                Каково устройство платформы Android?

7.                Что представляет собой Android SDK?

8.                Назовите основные средства разработки под Android.

9.                Перечислите достоинства и недостатки эмуляторов Android.

10.           Какая версия платформы наиболее популярна в настоящее время?

11.           Виды приложений.

12.           Как можно изменять свойства приложений?

13.            Какую архитектуру имеет приложения Андроид?

14.           Опишите фоновые приложения.

15.           Как работают смешанные приложения?

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

3. РАЗРАБОТКА ПРИЛОЖЕНИЙ НА ЯЗЫКЕПРОГРАММИРОВАНИЯ JAVA  ДЛЯ ANDROID

3.1.  Основные конструкции языка программированияJava

3.1.1. Типы данных

В языке Java только 8 примитивных (скалярных, простых) типов: boolean, byte, char, short, int, long, float, double. Существует также вспомогательный девятый примитивный тип — void, однако переменные и поля такого типа не могут быть объявлены в коде, а сам тип используется только для описания соответствующего ему класса, для использования при рефлексии.

Длины и диапазоны значений примитивных типов определяются стандартом, а не реализацией, и приведены в таблице. Тип char сделали двухбайтовым для удобства локализации (один из идеологических принципов Java): когда складывался стандарт, уже существовал Unicode-16, но не Unicode-32. Поскольку в результате не осталось однобайтового типа, добавили новый тип byte, причем в Java, в отличие от других языков, он не является беззнаковым. Типы float и double могут иметь специальные значения  и «не число» (NaN). Для типа double они обозначаются Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY, Double.NaN; для типа float — так же, но с приставкой Float вместо Double. Минимальные и максимальные значения, принимаемые типами float и double, тоже стандартизованы.

 

Тип

Длина

(в байтах)

Диапазон или набор значений

boolean

1 в массивах, 4 в переменных

true, false

byte

1

−128..127

char

2

0..216−1, или 0..65535

short

2

−215..215−1, или −32768..32767

int

4

−231..231−1, или −2147483648..2147483647

long

8

−263..263−1, или примерно −9.2·1018..9.2·1018

float

4

-(2-2−23)·2127..(2-2−23)·2127, или примерно −3.4·1038..3.4·1038, а также -\infty, \infty, NaN

double

8

-(2-2−52)·21023..(2-2−52)·21023, или примерно −1.8·10308..1.8·10308, а также , -\infty, \infty, NaN

 

Такая жёсткая стандартизация была необходима, чтобы сделать язык платформенно-независимым, что является одним из идеологических требований к Java. Тем не менее, одна небольшая проблема с платформенной независимостью всё же осталась. Некоторые процессоры используют для промежуточного хранения результатов 10-байтовые регистры или другими способами улучшают точность вычислений. Для того, чтобы сделать Java максимально совместимой между разными системами, в ранних версиях любые способы повышения точности вычислений были запрещены. Однако это приводило к снижению быстродействия. Выяснилось, что ухудшение точности ради платформенной независимости мало кому нужно, тем более если за это приходится платить замедлением работы программ. После многочисленных протестов этот запрет отменили, но добавили ключевое слово strictfp, запрещающее повышение точности. Преобразования при математических операциях в языке Java действуют следующие правила:

1.                Если один операнд имеет тип double, другой тоже преобразуется к типу double.

2.                Иначе, если один операнд имеет тип float, другой тоже преобразуется к типу float.

3.                Иначе, если один операнд имеет тип long, другой тоже преобразуется к типу long.

4.                Иначе оба операнда преобразуются к типу int.

Данный способ неявного преобразования встроенных типов полностью совпадает с преобразованием типов в C++.

В языке Java имеются только динамически создаваемые объекты. Причем переменные объектного типа и объекты в Java — совершенно разные сущности. Переменные объектного типа являются ссылками, то есть неявными указателями на динамически создаваемые объекты. Это подчёркивается синтаксисом описания переменных. Так, в Java нельзя писать:

double a[10][20];

Foo b(30);

анужно:

double[][] a = new double[10][20];

Foo b = new Foo(30);

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

Объектными являются переменные любого типа, кроме примитивного. Явных указателей в Java нет. В отличие от указателей C, C++ и других языков программирования, ссылки в Java в высокой степени безопасны благодаря жёстким ограничениям на их использование, в частности:

·                     нельзя преобразовывать объект типа int или любого другого примитивного типа в указатель или ссылку и наоборот.

·                     над ссылками запрещено выполнять операции ++, −−, +, − или любые другие арифметические операции.

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

·                     в Java нет операций взятия адреса (&) или взятия объекта по адресу (*). Амперсанд (&) означает всего лишь «побитовое и» (двойной амперсанд — «логическое и»). Однако для булевых типов одиночный амперсанд означает «логическое и», отличающееся от двойного тем, что цепь проверок не прекращается при получении в выражении значения false (напр. a == b && foo() == bar() не повлечёт вызовов foo() и bar() в случае, если a != b, тогда как использование & повлечёт в любом случае.

Благодаря таким специально введенным ограничениям в Java невозможно прямое манипулирование памятью на уровне физических адресов (хотя определено значение ссылки, не указывающей ни на что: null).

Если нужен указатель на примитивный тип, используются классы-обёртки примитивных типов: Boolean, Byte, Character, Short, Integer, Long, Float, Double.

Из-за того, что объектные переменные являются ссылочными, при присваивании не происходит копирования объекта. Так, если написать то произойдет копирование адреса из переменной foo в переменную bar. То есть foo и bar будут указывать на одну и ту же область памяти, то есть на один и тот же объект; попытка изменить поля объекта, на который ссылается переменная foo, будет менять объект, с которым связана переменная bar, и наоборот.

Foo foo, bar;

bar = foo;

,

Если же необходимо получить именно ещё одну копию исходного объекта, пользуются или методом (функцией-членом, в терминологии C++) clone(), создающим копию объекта, или (реже) копирующим конструктором (конструкторы в Java не могут быть виртуальными, поэтому экземпляр класса-потомка будет неправильно скопирован конструктором класса-предка; метод клонирования вызывает нужный конструктор и тем самым позволяет обойти это ограничение).

Метод clone() требует, чтобы класс реализовывал интерфейс Cloneable (об интерфейсах см. ниже). Если класс реализует интерфейс Cloneable, по умолчанию clone() копирует все поля (мелкая копия). Если требуется не копировать, а клонировать поля (а также их поля и так далее), надо переопределять метод clone(). Определение и использование метода clone() часто является нетривиальной задачей.

В языке Java невозможно явное удаление объекта из памяти — вместо этого реализована Сборка мусора. Традиционным  приёмом, дающим сборщику мусора «намёк» на освобождение памяти, является присваивание переменной пустого значения null. Это, однако, не значит, что объект, заменённый значением null, будет непременно и немедленно удалён, но есть гарантия, что этот объект будет удалён именно в будущем. Данный приём всего лишь устраняет ссылку на объект, то есть отвязывает указатель от объекта в памяти. При этом следует учитывать, что объект не будет удален сборщиком мусора, пока на него указывает хотя бы одна ссылка из используемых переменных или объектов. Существуют также методы для инициации принудительной сборки мусора, но не гарантируется, что они будут вызваны исполняющей средой, и их не рекомендуется использовать для обычной работы.

3.1.2. Специальные классы и функции

Java не является процедурным языком: любая функция может существовать только внутри класса. Это подчёркивает терминология языка Java, где нет понятий «функция» или «функция-член» (англ. memberfunction), а только метод. В методы превратились и стандартные функции. Например, в Java нет функции sin(), а есть метод Math.sin() класса Math (содержащего, кроме sin(), методы cos(), exp(), sqrt(), abs() и многие другие). Конструкторы в Java не считаются методами. Деструкторов в Java не существует, а метод finalize() ни в коем случае нельзя считать аналогом деструктора.

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

Конструктор инициализирует объект непосредственно во время создания. Имя конструктора совпадает с именем класса, включая регистр, а по синтаксису конструктор похож на метод без возвращаемого значения.

private int Cat(); // так выглядит метод по имени Cat

Cat(); // так выглядит конструктор класса Cat

В отличие от метода, конструктор никогда ничего не возвращает. Конструктор определяет действия, выполняемые при создании объекта класса, и является важной частью класса. Как правило, программисты стараются явно указать конструктор. Если явного конструктора нет, то Java автоматически создаст его для использования по умолчанию.

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

class Box {

    int width; // ширинакоробки

    int height; // высотакоробки

    int depth; // глубинакоробки

     // Конструктор

    Box(int a, int b) {

        width = a;

        height = b;

        depth = 10;

    }

    // вычисляемобъёмкоробки

    int getVolume() {

        returnwidth * height * depth;

    }

}

Даже если конструктор специально не определён, виртуальная машина Java обязательно его создаст (пустым).

В Java (как и в C++) используются статические методы (англ. staticmethod — в теории программирования их также называют методами класса), которые задаются при помощи ключевого слова static. Статические поля (переменные класса) имеют тот же смысл, что и в C++: каждое такое поле является собственностью класса, поэтому для доступа к статическим полям не требуется создавать экземпляры соответствующего класса.

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

double x = Math.sin(1);

вместо

Math m = new Math();

double x = m.sin(1);

Поскольку статические методы существуют независимо от объектов (экземпляров класса), они не имеют доступа к обычным (нестатическим) полям и методам данного класса. В частности, при реализации статического метода недопустимо использовать идентификатор this.

Ключевое слово final (финальный) имеет разные значения при описании поля, метода или класса.

1.                Финальное поле класса инициализируется при описании или в конструкторе класса (а статическое поле — в статическом блоке инициализации). Впоследствии его значение не может быть изменено. Если статическое поле класса или переменная проинициализированы константным выражением, они рассматриваются компилятором как именованная константа; в таком случае их значение может быть использовано в операторах switch (для констант типа int), а также для условной компиляции (для констант типа boolean) при использовании с оператором if.

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

3.                Метод класса, отмеченный словом final, не может быть переопределён при наследовании.

4.                Финальный класс не может иметь наследников.

В Java методы, не объявленные явно как static, final или private, являются виртуальными в терминологии C++: при вызове метода, по-разному определённого в базовом и наследующем классах, всегда производится проверка времени выполнения.

Абстрактным методом (модификатор abstract) в Java называется метод, для которого заданы параметры и тип возвращаемого значения, но не задано тело. Абстрактный метод определяется в классах-наследниках. Аналог абстрактного метода в C++ — чисто виртуальная функция (pure virtual function). Для того чтобы в классе можно было описывать абстрактные методы, сам класс тоже должен быть описан как абстрактный. Объекты абстрактного класса создавать нельзя.

Высшей степенью абстрактности в Java является интерфейс (interface). Все методы интерфейса абстрактны: описатель abstract даже не требуется. Интерфейс в Java не считается классом, хотя, по сути, является полностью абстрактным классом. Класс может наследовать/расширять (extends) другой класс или реализовывать (implements) интерфейс. Кроме того, интерфейс может наследовать/расширять другой интерфейс.

В Java класс не может наследовать более одного класса, зато может реализовывать несколько интерфейсов. Множественное наследование интерфейсов не запрещено, то есть один интерфейс может наследоваться от нескольких.

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

В Java есть интерфейсы, которые не содержат методов для реализации, а специальным образом обрабатываются JVM:

·                    java.lang.Cloneable

·                    java.io.Serializable

·                    java.util.RandomAccess

·                    java.rmi.Remote

 

3.2. Классы и объекты

3.2.1. Описание класса

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

Шаблоном или описанием объекта является класс, а объект представляет экземпляр этого класса. Можно еще провести следующую аналогию. У нас у всех есть некоторое представление о человеке - наличие двух рук, двух ног, головы, туловища и т.д. Есть некоторый шаблон - этот шаблон можно назвать классом. Реально же существующий человек (фактически экземпляр данного класса) является объектом этого класса.

Класс определяется с помощью ключевого слова сlass:

classGraj{

 }

В примере класс называется Graj. После названия класса идут фигурные скобки, между которыми помещается тело класса - то есть его поля и методы.

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

Для хранения состояния объекта в классе применяются поля или переменные класса. Для определения поведения объекта в классе применяются методы. Например, класс Graj, который представляет человека, мог бы иметь следующее определение:

classGraj{

   String imya;        // имя

    intvozrast;            // возраст

    voiddisplayInfo(){

        System.out.printf("Imya: %s \tVozrast: %d\n", imya, vozrast);

    }

}

В классе Graj определены два поля: imya представляет имя человека, а vozrast - его возраст. И также определен метод displayInfo, который ничего не возвращает и просто выводит эти данные на консоль.

Теперь используем данный класс. Для этого определим следующую программу:

public class Program{

       public static void main(String[] args) {

Grajkarl;   }

}

class Graj{

    String imya;    // имя

    int vozrast;        // возраст

    void displayInfo(){

        System.out.printf("Imya: %s \tVozrast: %d\n", imya, vozrast);   }

}

Как правило, классы определяются в разных файлах. В примере для простоты мы определяем два класса в одном файле. Стоит отметить, что в этом случае только один класс может иметь модификатор public (в примере это класс Program), а сам файл кода должен называться по имени этого класса, то есть в примере файл должен называться Program.java.

Класс представляет новый тип, поэтому мы можно определять переменные, которые представляют данный тип. Так, здесь в методе main определена переменная karl, которая представляет класс Graj. Но пока эта переменная не указывает ни на какой объект и по умолчанию она имеет значение null. По большому счету мы ее пока не можно использовать, поэтому вначале необходимо создать объект класса Graj.

3.2.2. Конструкторы

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

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

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

publicclassProgram{

       publicstaticvoidmain(String[] args) {

        Grajkarl = newGraj(); // создание объекта

        karl.displayInfo();

          // изменяем имя и возраст

        karl.imya = "Said";

        karl.vozrast = 20;

        karl.displayInfo();    }

}

classGraj{

     String imya;    // имя

    intvozrast;        // возраст

    voiddisplayInfo(){

        System.out.printf("Imya: %s \tVozrast: %d\n", imya, vozrast);    }

}

Для создания объекта Graj используется выражение new Graj(). Оператор new выделяет память для объекта Graj. И затем вызывается конструктор по умолчанию, который не принимает никаких параметров. В итоге после выполнения данного выражения в памяти будет выделен участок, где будут храниться все данные объекта Graj. А переменная karl получит ссылку на созданный объект.

Если конструктор не инициализирует значения переменных объекта, то они получают значения по умолчанию. Для переменных числовых типов это число 0, а для типа string и классов - это значение null (то есть фактически отсутствие значения).

После создания объекта мы можно обратиться к переменным объекта Graj через переменную karl и установить или получить их значения, например, karl.imya = "Said".

В итоге мы увидим на консоли:

Imya: null             Vozrast: 0

Imya: Karl            Vozrast: 20

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

publicclassProgram{

      publicstaticvoidmain(String[] args) {

        Grajroman = newGraj();      // вызов первого конструктора без параметров

        roman.displayInfo();

        Grajkarl = newGraj("Said"); // вызов второго конструктора с одним параметром

        karl.displayInfo();

        Graj sam = newGraj("Suxrob", 22); // вызов третьего конструктора с двумя параметрами

        sam.displayInfo();    }

}

classGraj{

    String imya;    // имя

    intvozrast;        // возраст

    Graj()    {

        imya = "Undefined";

        vozrast = 18;

    }

    Graj(String n)    {

        imya = n;

        vozrast = 18;

    }

    Graj(String n, inta)    {

        imya = n;

        vozrast = a;

    }

    voiddisplayInfo(){

        System.out.printf("Imya: %s \tVozrast: %d\n", imya, vozrast);    }

}

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

Консольный вывод программы:

Imya: Undefined            Vozrast: 18

Imya: Said                     Vozrast: 20

Imya: Suxrob                 Vozrast: 22

 

3.2.3. Ключевое слово this

Ключевое слово this представляет ссылку на текущий экземпляр класса. Через это ключевое слово мы можно обращаться к переменным, методам объекта, а также вызывать его конструкторы. Например:

publicclassProgram{

    publicstaticvoidmain(String[] args) {

       Graj undef = newGraj();

        undef.displayInfo();

       Grajkarl = newGraj("Karl");

        karl.displayInfo();

        Graj sam = newGraj("Sam", 25);

        sam.displayInfo();    }

}

classGraj{

    String imya;    // имя

    intvozrast;        // возраст

    Graj()    {

        this("Undefined", 18);

    }

    Graj(String imya)    {

        this(imya, 18);

    }

    Graj(String imya, intvozrast)    {

        this.imya = imya;

        this.vozrast = vozrast;

    }

    voiddisplayInfo(){

        System.out.printf("Imya: %s \tVozrast: %d\n", imya, vozrast);    }

}

В третьем конструкторе параметры называются так же, как и поля класса. И чтобы разграничить поля и параметры, применяется ключевое слово this:

this.imya = imya;

Так, в примере указываем, что значение параметра imya присваивается полю imya.

Кроме того, у нас три конструктора, которые выполняют идентичные действия: устанавливают поля imya и vozrast. Чтобы избежать повторов, с помощью this можно вызвать один из конструкторов класса и передать для его параметров необходимые значения:

Graj(String imya){

    this(imya, 18);

}

В итоге результат программы будет тот же, что и в предыдущем примере.

3.2.4. Инициализаторы

Кроме конструктора начальную инициализацию объекта вполне можно было проводить с помощью инициализатора объекта. Инициализатор выполняется до любого конструктора. То есть в инициализатор мы можно поместить код, общий для всех конструкторов:

publicclassProgram{

          publicstaticvoidmain(String[] args) {

                 Graj undef = newGraj();

        undef.displayInfo();

                Grajkarl = newGraj("Karl");

        karl.displayInfo();    }

}

classGraj{

         String imya;    // имя

    intvozrast;        // возраст

         /*начало блока инициализатора*/

    {

        imya = "Undefined";

        vozrast = 18;

    }

    /*конец блока инициализатора*/

    Graj(){  }

    Graj(String imya){

                 this.imya = imya;    }

    Graj(String imya, intvozrast){

                 this.imya = imya;

        this.vozrast = vozrast;    }

    voiddisplayInfo(){

        System.out.printf("Imya: %s \tVozrast: %d\n", imya, vozrast);    }

}

 

Консольный вывод:

Imya: Undefined Vozrast: 18

Imya: Karl                     Vozrast: 18

 

 

3.3. Разработка мобильных приложений на языке программирования Java

3.3.1. Описание элементов

TextView

Для простого вывода текста на экран предназначен элемент TextView. Он просто отображает текст без возможности его редактирования. Некоторые его основные атрибуты:

•        android:text: устанавливает текст элемента

•        android:textSize: устанавливает высоту текста, в качестве единиц измерения для указания высоты используются sp

•        android:background: задает фоновый цвет элемента в виде цвета в шестнадцатиричной записи или в виде цветового ресурса

•        android:textColor: задает цвет текста

•        android:textAllCaps: при значении true делает все символы в тексте заглавными

•        android:textDirection: устанавливает направление текста. По умолчанию используется направление слева направо, но с помощью значения rtl можно установить направление справо налево

•        android:textAlignment: задает выравнивание текста. Может принимать следующие значения:

o       center: выравнивание по центру

o       textStart: по левому краю

o       textEnd: по правому краю

o       viewStart: по левому краю

o       viewEnd: по правому краю

•        android:fontFamily: устанавливает тип шрифта. Может принимать следующие значения:

o       monospace

o       serif

o       serif-monospace

o       sans-serif

o       sans-serif-condensed

o       sans-serif-smallcaps

o       sans-serif-light

o       casual

o       cursive

package com.example.layoutapp;

 import android.support.v7.app.AppCompatActivity;

import android.os.Bundle;

import android.view.ViewGroup;

import android.widget.LinearLayout;

import android.widget.TextView;

import android.graphics.Typeface;

public class MainActivity extends AppCompatActivity {

     @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

         LinearLayout linearLayout = new LinearLayout(this);

        TextView textView1 = new TextView(this);

        // установка фонового цвета

        textView1.setBackgroundColor(0xffe8eaf6);

        // установка цвета текста

        textView1.setTextColor(0xff5c6bc0);

        // делаем все буквы заглавными

        textView1.setAllCaps(true);

        // устанавливаем вравнивание текста по центру

        textView1.setTextAlignment(TextView.TEXT_ALIGNMENT_CENTER);

        // устанавливаем текста

        textView1.setText("Android Nougat 7");

        // установка шрифта

        textView1.setTypeface(Typeface.create("casual", Typeface.NORMAL));

        // устанавливаем высоту текста

        textView1.setTextSize(26);

         LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams

                (ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);

        // установка внешних отступов

        layoutParams.setMargins(20,20,20,20);

        // устанавливаем размеры

        textView1.setLayoutParams(layoutParams);

        linearLayout.addView(textView1);

        setContentView(linearLayout);    }

}

android:autoLink может принимать несколько значений:

•        none: отключает все ссылки

•        web: включает все веб-ссылки

•        email: включает ссылки на электронные адреса

•        phone: включает ссылки на номера телефонов

•        map: включает ссылки на карту

•        all: включает все вышеперечисленные ссылки

То есть при настройке android:autoLink="web" если в тексте есть упоминание адреса url, то этот адрес будет выделяться, а при нажатии на него будет осуществлен переход к веб-браузеру, который откроет страницу по этому адресу. С помощью прямой черты мы можно объединять условия, как в данном случае: android:autoLink="web|email"

EditText

Элемент EditText является подклассом класса TextView. Он также представляет текстовое поле, но теперь уже с возможностью ввода и редактирования текста. Таким образом, в EditText мы можно использовать все те же возможности, что и в TextView.

Из тех атрибутов, что не рассматривались в теме про TextView, следует отметить атрибут android:hint. Он позволяет задать текст, который будет отображаться в качестве подсказки, если элемент EditText пуст. Кроме того, мы можно использовать атрибут android:inputType, который позволяет задать клавиатуру для ввода. В частности, среди его значений можно выделить следующие:

•        text: обычная клавиатура для ввода однострочного текста

•        textMultiLine: многострочное текстовое поле

•        textEmailAddress: обычная клавиатура, на которой присутствует символ @, ориентирована на ввод email

•        textUri: обычная клавиатура, на которой присутствует символ /, ориентирована на ввод интернет-адресов

•        textPassword: клавиатура для ввода пароля

•        textCapWords: при вводе первый введенный символ слова представляет заглавную букву, остальные - строчные

•        number: числовая клавиатура

•        phone: клавиатура в стиле обычного телефона

•        date: клавиатура для ввода даты

•        time: клавиатура для ввода времени

datetime: клавиатура для ввода даты и времени

 

package com.example.layoutapp;

import android.support.v7.app.AppCompatActivity;

import android.os.Bundle;

import android.text.Editable;

import android.text.TextWatcher;

import android.widget.EditText;

import android.widget.TextView;

 

public class MainActivity extends AppCompatActivity {

    @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

         setContentView(R.layout.activity_main);

         EditText editText = (EditText) findViewById(R.id.editText);

        editText.addTextChangedListener(new TextWatcher() {

             public void afterTextChanged(Editable s) {}

  public void beforeTextChanged(CharSequence s, int start, int count, int after) {}

 public void onTextChanged(CharSequence s, int start,

                                      int before, int count) {

                TextView textView = (TextView) findViewById(R.id.textView);

                textView.setText(s);    }   });

    }}

С помощью метода addTextChangedListener() здесь к элементу EditText добавляется слушатель ввода текста - объект TextWatcher. Для его использования нам надо реализовать три метода, но в реалности нам хватит реализации метода onTextChanged, который вызывается при изменении текста. Введенный текст передается в этот метод в качестве параметра CharSequence. В самом методе просто передаем этот текст в элемент TextView.

Button

Одним из часто используемых элементов являются кнопки, которые представлены классом android.widget.Button. Ключевой особенностью кнопок является возможность взаимодействия с пользователем через нажатия.

Некоторые ключевые атрибуты, которые можно задать у кнопок:

              text: задает текст на кнопке

              textColor: задает цвет текста на кнопке

              background: задает фоновый цвет кнопки

              textAllCaps: при значении true устанавливает текст в верхнем регистре. По умолчанию как раз и применяется значение true

              onClick: задает обработчик нажатия кнопки

package com.example.layoutapp;

import android.support.v7.app.AppCompatActivity;

import android.os.Bundle;

import android.view.View;

import android.widget.EditText;

import android.widget.TextView;

 public class MainActivity extends AppCompatActivity {

     @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main);

    }

    // Обработка нажатия кнопки

    public void sendMessage(View view) {

        TextView textView = (TextView) findViewById(R.id.textView);

        EditText editText = (EditText) findViewById(R.id.editText);

        textView.setText("Добро пожаловать, " + editText.getText());

    }

}

При создании метода обработки нажатия следует учитывать следующие моменты:

•   Метод должен объявляться с модификатором public

•   Должен возвращать значение void

В качестве параметра принимать объект View. Этот объект View и представляет собой нажатую кнопку

package com.example.layoutapp;

import android.support.v7.app.AppCompatActivity;

import android.os.Bundle;

import android.view.View;

import android.widget.Button;

import android.widget.EditText;

import android.widget.LinearLayout;

import android.widget.TextView;

public class MainActivity extends AppCompatActivity {

     EditText editText;

    TextView textView;

    @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        LinearLayout linearLayout = new LinearLayout(this);

        linearLayout.setOrientation(LinearLayout.VERTICAL);

        textView = new TextView(this);

        textView.setLayoutParams(new LinearLayout.LayoutParams(

                LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT

        ));

        linearLayout.addView(textView);

 

        editText = new EditText(this);

        editText.setHint("Введите имя");

        editText.setLayoutParams(new LinearLayout.LayoutParams(

                LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT  ));

        linearLayout.addView(editText);

        Button button = new Button(this);

        button.setText("CLICK");

        button.setLayoutParams(new LinearLayout.LayoutParams(

                LinearLayout.LayoutParams.WRAP_CONTENT,

LinearLayout.LayoutParams.WRAP_CONTENT ));

        linearLayout.addView(button);

         button.setOnClickListener(new View.OnClickListener() {

            public void onClick(View v) {

                // Обработка нажатия

                textView.setText("Добро пожаловать, " + editText.getText());

            }});

        setContentView(linearLayout);

    }}

 

Checkbox

package com.example.layoutapp;

import android.support.v7.app.AppCompatActivity;

import android.os.Bundle;

import android.view.View;

mport android.widget.CheckBox;

import android.widget.TextView;

 

public class MainActivity extends AppCompatActivity {

     @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main);

    }

    public void onCheckboxClicked(View view) {

        // Получаем флажок

        CheckBox language = (CheckBox) view;

        // Получаем, отмечен ли данный флажок

        boolean checked = language.isChecked();

         TextView selection = (TextView) findViewById(R.id.selection);

        // Смотрим, какой именно из флажков отмечен

        switch(view.getId()) {

            case R.id.java:

                if (checked){

                    selection.setText("Java");   }

                break;

            case R.id.javascript:

                if (checked)

                    selection.setText("JavaScript");

                break;    }    }

}

В качестве параметра в обработчик нажатия onCheckboxClicked передается нажатый флажок. С помощью метода isChecked() можно узнать, выделен ли флажок - в этом случае метод возвращает true.

public void onCheckboxClicked(View view) {

    // Получаем флажок

    CheckBox language = (CheckBox) view;

    // Получаем, отмечен ли данный флажок

    TextView selection = (TextView) findViewById(R.id.selection);

    if(language.isChecked())

        selection.setText(language.getText());

}

package com.example.layoutapp;

import android.support.v7.app.AppCompatActivity;

import android.os.Bundle;

import android.view.View;

import android.widget.CheckBox;

import android.widget.TextView;

 

public class MainActivity extends AppCompatActivity {

     @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

         setContentView(R.layout.activity_main);

    }

     public void onCheckboxClicked(View view) {

         // Получаем флажки

        CheckBox java = (CheckBox) findViewById(R.id.java);

        CheckBox javascript = (CheckBox) findViewById(R.id.javascript);

        String selectedItems = "";

        if(java.isChecked())

            selectedItems +=java.getText() + ", ";

        if(javascript.isChecked())

            selectedItems +=javascript.getText();

         TextView selection = (TextView) findViewById(R.id.selection);

        selection.setText(selectedItems);   }

 }

 

Контрольныевопросы:

1.      Какие платформы используются для создания ОС мобильных приложений?

2.      Абстрактные классы и их использование

3.       Анонимные  классы и их использование

4.      Использование шаблонов и контейнеров

5.       Зачем нужны абстрактные классы?

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

4. РАБОТА С БАЗАМИ ДАННЫХ В МОБИЛЬНЫХ ПРИЛОЖЕНИЯХ, РАБОТА С ГЕОЛОКАЦИЕЙ

4.1. Работа с базой данных

4.1.1. Введение в базу данных в Android

Android использует для работы с базами данных известную библиотеку SQLite. SQLite зарекомендовала себя в качестве чрезвычайно надёжной системы баз данных, которая используется во многих бытовых электронных устройствах и программах, включая некоторые MP3-проигрыватели, iPhone, iPod Touch, Mozilla Firefox и др.

С помощью SQLite вы можете создавать для своего приложения независимые реляционные базы данных для хранения и управления сложными данными приложения. Android хранит базы данных в каталоге /data/data/<имявашегопакета>/databases на вашем устройстве (или эмуляторе). По умолчанию все базы данных приватные, доступ к ним могут получить только те приложения, которые их создали.

Те, кто имеет опыт работы со связкой PHP+MySQL, найдут много знакомых вещей и быстро разберутся с принципом работы.

Прежде всего нужно запомнить, что в Android уже есть готовый класс SQLiteOpenHelper. Помните, мы в своих проектах всегда наследовались от Activity (extends Activity). Точно так же надо поступить и при работе с базой данных.

4.1.2. Создание базы данных

Создадим новый проект как обычно. Теперь нужно создать новый класс, который будет работать с базой данных - добавлять, выбирать, удалять и прочие операции. Создаём новый класс и в диалоговом окне напротив текстового поля Superclass: нажимаем кнопку Browse.... Далее начинаем вводить первые символы класса, чтобы выбрать из списка нужный класс. Восьми символов оказалось вполне достаточно, чтобы в списке остался только один вариант. Нажимаем на кнопку OK для добавления класса в диалоговое окно и нажимаем на кнопку Finish, чтобы закончить с созданием класса.

New Class

Рис. 4.1. Создание нового класса

Появится следующая заготовка:

package ru.databasedemo;

import android.database.sqlite.SQLiteDatabase;

import android.database.sqlite.SQLiteOpenHelper;

public class CatsDataBase extends SQLiteOpenHelper {

@Override

         public void onCreate(SQLiteDatabase arg0) {

                   // TODO Auto-generated method stub

         }

         @Override

         public void onUpgrade(SQLiteDatabase arg0, int arg1, int arg2) {

                   // TODO Auto-generated method stub

         }

}

Укласса есть два обязательных метода onCreate() и onUpgrade().Название класса будет подчёркнуто красной волнистой чертой, требуя создать конструктор. Вручную напишем конструктор перед методами, а также добавим пару констант:

// константы для конструктора

private static final String DATABASE_NAME = "cat_database.db";

private static final int DATABASE_VERSION = 1;

public CatsDataBase(Context context) {

         // TODO Auto-generated constructor stub

         super(context, DATABASE_NAME, null, DATABASE_VERSION);

}

КонстантаDATABASE_NAME отвечает за имя файла, в котором будет храниться база данных приложения.

Вторая константа DATABASE_VERSION требует дополнительных объяснений. Она отвечает за номер версии базы. Принцип её работы схож с номером версий самого приложения. Когда мы видим, что вышла новая версия Chrome 23, то понимаем, что пора обновляться. Аналогично поступает и само приложение, когда замечает, что номер версии базы стал другим. Как только программа заметила обновление номера базы, она запускает метод onUpgrade(), который у нас сформировался автоматически. В этом методе необходимо разместить код, который должен сработать при обновлении базы.

Метод onCreate()  - создаётся база данных с необходимыми данными для работы.

Мы указали имя файла для базы данных и её номер. Также нужно указать имя таблицы и имена колонок для таблицы. Дляначала создадим простую таблицу, которая будет содержать только имена котов.

public static final String UID = "_id";

public static final String CATIMYA = "catimya";

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

Для создания таблицы в SQL используется команда CREATE TABLE. Для удобства вынесем команду в отдельную строку. Аналогично поступим с командой DROP TABLE. Сами команды помещаются в метод execSQL(). Полный листинг сейчас будет выглядеть следующим образом:

package ru.alexanderklimov.databasedemo;

import android.content.Context;

import android.database.sqlite.SQLiteDatabase;

import android.database.sqlite.SQLiteOpenHelper;

import android.util.Log;

public class CatsDataBase extends SQLiteOpenHelper {

         private static final String DATABASE_NAME = "cat_database.db";

         private static final int DATABASE_VERSION = 1;

         public static final String TABLE_NAME = "contact_table";

         public static final String UID = "_id";

         public static final String CATIMYA = "catimya";

         private static final String SQL_CREATE_ENTRIES = "CREATE TABLE " + TABLE_NAME + " (" + UID + " INTEGER PRIMARY KEY AUTOINCREMENT,"       + CATIMYA + " VARCHAR(255));";

         private static final String SQL_DELETE_ENTRIES = "DROP TABLE IF EXISTS "+ TABLE_NAME;

         public CatsDataBase(Context context) {

// TODO Auto-generated constructor stub

super(context, DATABASE_NAME, null, DATABASE_VERSION);

         }

         @Override

         public void onCreate(SQLiteDatabase db) {

// TODO Auto-generated method stub

                   db.execSQL(SQL_CREATE_ENTRIES);

         }

         @Override

public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

// TODO Auto-generated method stub

Log.w("LOG_TAG", "Обновление базы данных с версии " + oldVersion

+ " до версии " + newVersion + ", которое удалит все старые данные");

// Удаляем предыдущую таблицу при апгрейде

         db.execSQL(SQL_DELETE_ENTRIES);

// Создаём новый экземпляр таблицы

         onCreate(db);       }}

 

4.2. Создание запросов

Для работы с базой данных и для создания запросов рассмотрим некоторые методы. Напишемследующий кодс использованием методаonCreate():

@Override

protected void onCreate(Bundle savedInstanceState) {

    super.onCreate(savedInstanceState);

    setContentView(R.layout.activity_main);

        // Инициализируем наш класс-обёртку

    CatsDataBase sqh = new CatsDataBase(this);

        // База нам нужна для записи и чтения

    SQLiteDatabase sqdb = sqh.getWritableDatabase();

      // закрываем соединения с базой данных

    sqdb.close();

    sqh.close();

}

Запустите проект. На первый взгляд ничего не произошло, но на самом деле в каталоге /data/data/ru.alexanderklimov.ru.databasedemo/databases появился файл cat_database.db. Вы можете в этом сами убедиться, если в эмуляторе откроете перспективу DDMS на вкладке File Explorer и просмотрите структуру файлов.

Теперь нам нужно научиться. Существуют различные способы для добавления и извлечения  данные из базы данных.

Первый способ вставки данных заключается в использовании класса ContentValues. Вот пример для кнопки, когда данные из текстового поля попадают в базу данных:

case R.id.buttonCVInsert:

// Метод 1: INSERT через класс CONTENTVALUE

         ContentValues cv = new ContentValues();

         cv.put(CatsDataBase.CATIMYA, txtData.getText().toString());

// вызываем метод вставки

sqdb.insert(CatsDataBase.TABLE_NAME, CatsDataBase.CATIMYA, cv);

         txtData.setText("");

         break;

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

Если колонок несколько, то вызывайте метод put() несколько раз:

values.put(CatsDataBase.CATIMYA, txtData.getText().toString());

values.put(CatsDataBase.EMAIL, txtEmail.getText().toString());

values.put(CatsDataBase.PHONE, txtPhone.getText().toString());

У метода insert() три аргумента. В первом указывается имя таблицы, в которую будет вноситься записи. В третьем указывается объект ContentValues, созданный ранее. Второй аргумент используется для указания колонки. SQL не позволяет вставлять пустую запись, и если будет использоваться пустой ContentValue, то укажите во втором аргументе null во избежание ошибки.

Второй способ использует традиционный SQL-запрос INSERT INTO.... Вы формируете обычный запрос в виде строки, а затем передаёте её в метод execSQL(). Основное неудобство при этом способе - не запутаться в кавычках. Если что-то не вставляется, то смотрите логи сообщений.

case R.id.buttonSQLQuery:

         // Метод 2: INSERT через SQL-запрос

    String insertQuery = "INSERT INTO " +

    CatsDataBase.TABLE_NAME +

     " (" + CatsDataBase.CATIMYA + ") VALUES ('" + txtData.getText().toString() + "')";

    sqdb.execSQL(insertQuery);

    txtData.setText("");

         break;

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

У метода query() множество параметров. В первом параметре укажите имя таблицы, во втором - массив имён колонок, далее идут дополнительные условия. Пока везде оставим null. Далее через цикл while извлекаем данные и помещаем в логи. Можете снова запустить проект и проверить, какие данные вы уже занесли в базу:

case R.id.buttonQuery:

         Cursor cursor = sqdb.query(CatsDataBase.TABLE_NAME, new String[] {

                            CatsDataBase._ID, CatsDataBase.CATIMYA },

                            null, // The columns for the WHERE clause

                            null, // The values for the WHERE clause

                            null, // don't group the rows

                            null, // don't filter by row groups

                            null // The sort order

                            );

         while (cursor.moveToNext()) {

// GET COLUMN INDICES + VALUES OF THOSE COLUMNS

                   int id = cursor.getInt(cursor.getColumnIndex(CatsDataBase._ID));

String imya = cursor.getString(cursor

                                      .getColumnIndex(CatsDataBase.CATIMYA));

                   Log.i("LOG_TAG", "ROW " + id + " HAS IMYA " + imya);

         }

         cursor.close();

         break;

Второй способ использует сырой (raw) SQL-запрос. Сначала формируется строка запроса и скармливается методу rawQuery().

case R.id.buttonRawQuery:

         // Метод 2: Сырой SQL-запрос

         String query = "SELECT " + CatsDataBase._ID + ", "

                            + CatsDataBase.CATIMYA + " FROM " + CatsDataBase.TABLE_NAME;

         Cursor cursor2 = sqdb.rawQuery(query, null);

         while (cursor2.moveToNext()) {

                   int id = cursor2.getInt(cursor2

                                      .getColumnIndex(CatsDataBase._ID));

                   String imya = cursor2.getString(cursor2

                                      .getColumnIndex(CatsDataBase.CATIMYA));

                   Log.i("LOG_TAG", "ROW " + id + " HAS IMYA " + imya);

         }

         cursor2.close();

         break;

 

4.2.1. Контент  провайдеры и их использование

Контент-провайдер управляет доступом к хранилищу данных. Для реализации провайдера в Android приложении должен быть создан набор классов в соответствии с манифестом приложения. Один из этих классов должен быть наследником класса ContentProvider, который обеспечивает интерфейс между контент-провайдером и другими приложениями.

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

В мобильных приложениях контент-провайдеры необходимы в следующих случаях:

·                     приложение предоставляет сложные данные или файлы другим приложениям;

·                     приложение позволяет пользователям копировать сложные данные в другие приложения;

·                     приложение предоставляет специальные варианты поиска, используя поисковую платформу (framework).

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

Данные, с которыми работают контент-провайдеры, могут быть организованы двумя способами:

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

·                     Данные представлены некоторой структурой, например, таблица, массив. В этом случае необходимо хранить данные в табличной форме. Строка таблицы представляет собой некоторую сущность, например, сотрудник или товар. А столбец - некоторое свойство этой сущности, например, имя сотрудника или цена товара. В системе Android общий способ хранения подобных данных - база данных SQLite, но можно использовать любой способ постоянного хранения.

Для создание класса-наследника от классаContentProvider напрямую или через любого его потомка  необходимо переопределить обязательные методы.

Созданный контент-провайдер управляет доступом к структурированным данным, выполняя обработку запросов от других приложений. Все запросы, в конечном итоге, вызывают объект ContentResolver, который в свою очередь вызывает подходящий метод объекта ContentProvider для получения доступа.

query()

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

insert()

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

update()

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

delete()

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

getType()

- метод, возвращающий String в формате MIME, который описывает тип данных, соответствующий URI.

onCreate()

- метод, вызываемый системой, сразу после создания провайдера, включает инициализацию провайдера. Стоит отметить, что провайдер не создается до тех пор, пока объект ContentResolver не попытается получить к нему доступ.

 

Все вышеперечисленные методы, кроме onCreate(), вызываются приложением-клиентом. И все эти методы имеют такую же сигнатуру, как одноименные методы класса ContentResolver.

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

Наше приложение может сохранять разнообразую информацию о пользователе, какие-то связанные данные в файлах или настройках. Однако ОС Android уже хранит ряд важной информации, связанной с пользователем, к которой имеем доступ и которую мы можно использовать. Это и списки контактов, и файлы сохраненных изображений и видеоматериалов, и какие-то отметки о звонках и т.д., то есть некоторый контент. А для доступа к этому контенту в OC Android определены провайдеры контента (content provider)

В Android имеются следующие встроенные провайдеры, определенные в пакете android.content:

•        AlarmClock: управление будильником

•        Browser: история браузера и закладки

•        CalendarContract: каледарь и информаци о событиях

•        CallLog: информация о звонках

•        ContactsContract: контакты

•        MediaStore: медиа-файлы

•        SearchRecentSuggestions: подсказкипопоиску

•        Settings: системные настройки

•        UserDictionary: словарь слов, которые используются для быстрого набора

•        VoicemailContract: записи голосовой почты

Контакты в Android обладают встроенным API, позволяющим производить чтения и изменение списка контактов. Все контакты хранятся в базе данных SQLite, однако они не представляют единой таблицы. Для контактов отведено три таблицы, связанных отношением один-ко-многим: таблица для хранения информации о людях, таблица их телефонов и и таблица адресов их электоронных почт. Но благодаря имеющемуся в Android API мы можно абстрагироваться от связей между таблицами.

Итак, для доступа к контактам нам надо установить соответствующие разрешения в файле манифеста приложения:

<?xml version="1.0" encoding="utf-8"?>

<manifest xmlns:android="http://schemas.android.com/apk/res/android"

    package="com.example.eugene.sqliteapp" >

<uses-permission android:name="android.permission.READ_CONTACTS" android:maxSdkVersion="21" />

<application

        android:allowBackup="true"

        android:icon="@mipmap/ic_launcher"

        android:label="@string/app_name"

        android:theme="@style/AppTheme" >

<activity

            android:name=".MainActivity"

            android:label="@string/app_name" >

<intent-filter>

<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />

</intent-filter>

</activity>

</application>

</manifest>

 

Для вывода списка контактов определим следующую разметку интерфейса:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

    android:orientation="vertical"

    android:layout_width="match_parent"

    android:layout_height="match_parent">

<TextView

        android:layout_width="match_parent"

        android:layout_height="wrap_content"

        android:text="@string/contact_list">

</TextView>

<ListView

        android:id="@+id/contactList"

        android:layout_width="match_parent"

        android:layout_height="wrap_content">

</ListView>

</LinearLayout>

 

Для вывода списка контактов воспользуемся элементом ListView. И в классе activity получим контакты:

package com.example.eugene.sqliteapp;

 import android.support.v7.app.ActionBarActivity;

import android.os.Bundle;

import android.view.Menu;

import android.view.MenuItem;

 import android.provider.ContactsContract;

import android.database.Cursor;

import android.widget.ListView;

import android.widget.ArrayAdapter;

import java.util.ArrayList;

public class MainActivity extends ActionBarActivity {

     @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main);

         ListView contactsList = (ListView) findViewById(R.id.contactList);

        ArrayList<String> contacts = new ArrayList<String>();

         Cursor contactsCursor = getContentResolver()

                .query(ContactsContract.Contacts.CONTENT_URI, null, null, null, null);

        while (contactsCursor.moveToNext()) {

             // получаемкаждыйконтакт

            String contact = contactsCursor.getString(

contactsCursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME_PRIMARY));

// добавляем контакт в список

            contacts.add(contact);

}

         // создаемадаптер

        ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,

                android.R.layout.simple_list_item_1, contacts);

        // устанавливаемдляспискаадаптер

        contactsList.setAdapter(adapter);   }

}

 

Все контакты и сопутствующий функционал хранятся в специальных базах данных SQLite. Но нам не надо напрямую работать с ними. Мы можно воспользоваться объектом класса Cursor. Чтобы его получить, сначала вызывается метод getContentResolver(), который возвращает объект ContentResolver. Затем по цепочке вызывается метод query(). В этот метод передается ряд параметров, первый из которых представляет URI - ресурс, который мы хотим получить.

Для обращения к базе данных контактов используется константа ContactsContract.Contacts.CONTENT_URI.

Метод contactsCursor.moveToNext() позволяет последовательно перемещаться по записям контактов, считывая по одному контакту через вызов contactsCursor.getString().

Все полученные контакты добавляются в список, который потом выводится в ListView.

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

<uses-permission android:name="android.permission.WRITE_CONTACTS" android:maxSdkVersion="21" />

Чтобы вносить изменения в список контактов, следует установить разрешение android.permission.WRITE_CONTACTS

Для добавления контакта добавим новую activity. Назовем ее AddContactActivity. Определим в ее разметке текстовое поле и кнопку для добавления контакта:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

    android:layout_width="match_parent"

    android:layout_height="match_parent"

    android:padding="@dimen/activity_horizontal_margin"

    android:orientation="vertical">

<TextView android:text="Новыйконтакт:"

        android:layout_width="wrap_content"

        android:layout_height="wrap_content" />

<EditText

        android:id="@+id/contactText"

        android:layout_width="match_parent"

        android:layout_height="45dip" />

<Button

        android:layout_width="wrap_content"

        android:layout_height="wrap_content"

        android:text="Добавить"

        android:onClick="addContact"/>

</LinearLayout>

 

В коде activity пропишем обработчик addContact с добавлением контакта:

package com.example.eugene.sqliteapp;

import android.provider.ContactsContract;

import android.support.v7.app.ActionBarActivity;

import android.os.Bundle;

import android.view.Menu;

import android.view.MenuItem;

import android.view.View;

import android.content.ContentValues;

import android.content.ContentUris;

import android.provider.ContactsContract.RawContacts;

import android.provider.ContactsContract.Data;

import android.provider.ContactsContract.CommonDataKinds.StructuredName;

import android.widget.EditText;

import android.widget.Toast;

import android.net.Uri;

  public class AddContactActivity extends ActionBarActivity {

     @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_add_contact);

    }

     public void addContact(View view) {

       ContentValues contactValues = new ContentValues();

        EditText contactText = (EditText) findViewById(R.id.contactText);

        String newContact = contactText.getText().toString();

        contactValues.put(RawContacts.ACCOUNT_NAME, newContact);

        contactValues.put(RawContacts.ACCOUNT_TYPE, newContact);

        Uri newUri = getContentResolver().insert(RawContacts.CONTENT_URI, contactValues);

        long rawContactsId = ContentUris.parseId(newUri);

        contactValues.clear();

        contactValues.put(Data.RAW_CONTACT_ID, rawContactsId);

        contactValues.put(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE);

        contactValues.put(StructuredName.DISPLAY_NAME, newContact);

        getContentResolver().insert(Data.CONTENT_URI, contactValues);

        Toast.makeText(this, newContact + " добавленвсписокконтактов", Toast.LENGTH_LONG).show();   }

}

Весь код добавления находится в обработчике нажатия кнопки addContact. В Android контакты распределяются по трем таблицам: contacts, raw contacts и data. И нам надо добавить новый контакт в две последне таблицы. В таблицу contact в силу настроек мы добавить не можно, но это и не нужно.

Данные контакта представляют объект ContentValues, который состоит из ключей и их значений, то есть объект словаря. После его создания происходит добавление в него пары элементов:

contactValues.put(RawContacts.ACCOUNT_NAME, newContact);

contactValues.put(RawContacts.ACCOUNT_TYPE, newContact);

Здесь устанавливается название и тип контакта. В качестве ключей выставляются значения RawContacts.ACCOUNT_NAME и RawContacts.ACCOUNT_TYPE, а в качестве их значения - текст из текстового поля.

Далее этот объект добавляется в таблицу RawContacts с помощью метода insert():

Uri newUri = getContentResolver().insert(RawContacts.CONTENT_URI, contactValues);

Метод insert() возвращает URI - ссылку на добавленный объект в таблице, у которого мы можно получить id. Затем после очистки мы подготавливаем объект для доабвления в таблицу Data, вновь наполняя его данными:

contactValues.put(Data.RAW_CONTACT_ID, rawContactsId);

contactValues.put(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE);

contactValues.put(StructuredName.DISPLAY_NAME, newContact);

И опять добавление производит метод insert():

getContentResolver().insert(Data.CONTENT_URI, contactValues);

 

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

<menuxmlns:android="http://schemas.android.com/apk/res/android"

    xmlns:app="http://schemas.android.com/apk/res-auto"

xmlns:tools="http://schemas.android.com/tools" tools:context=".MainActivity">

<item

        android:id="@+id/action_settings"

        android:title="Добавить"

        android:orderInCategory="100"

        app:showAsAction="never" />

</menu>

Иобработкаэтогопунктавкодеглавной activity:

@Override

public boolean onCreateOptionsMenu(Menu menu) {

    getMenuInflater().inflate(R.menu.menu_main, menu);

    return true;

}

@Override

public boolean onOptionsItemSelected(MenuItem item) {

    int id = item.getItemId();

     if (id == R.id.action_settings) {

        Intent intent = new Intent(this, AddContactActivity.class);

        startActivity(intent);

        return true;     }

    return super.onOptionsItemSelected(item);

}

 

Для доступа к данным какого-либо контент-провайдера используется объект класса ContentResolver, который можно получить с помощью метода getContentResolver контекста приложения для связи с поставщиком в качестве клиента:

ContentResolver cr = getApplicationContext().getContentResolver();

 

Объект ContentResolver взаимодействует с объектом контент-провайдера, отправляя ему запросы клиента. Контент-провайдер обрабатывает запросы и возвращает результаты обработки.

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

Контент-провайдеры, обычно предоставляют минимум два URI для работы с данными: один для запросов, требующих все данные сразу, а другой – для обращения к конкретной «строке». В последнем случае в конце URI добавляется / (который совпадает с индексом «_id»).

Запросы на получение данных похожи на запросы к БД, при этом используется метод query объекта ContentResolver. Ответ также приходит в виде курсора, «нацеленного» на результирующий набор данных (выбранные строки таблицы):

ContentResolver cr = getContentResolver();

// получить данные всех контактов

Cursor c = cr.query(ContactsContract.Contacts.CONTENT_URI, null, null,

null, null);

// получить все строки, где третье поле имеет конкретное

// значение и отсортировать по пятому полю

String where = KEY_COL3 + "=" + requiredValue;

String order = KEY_COL5;

Cursor someRows = cr

.query(MyProvider.CONTENT_URI, null, where, null, order);

Изменение данных:

Uri myRowUri = cr.insert(SampleProvider.CONTENT_URI, newRow);

// Массовая вставка:

ContentValues[] valueArray = new ContentValues[5];

// здесь заполняем массив

// делаем вставку

int count = cr.bulkInsert(MyProvider.CONTENT_URI, valueArray);

 

При вставке одного элемента метод insert возвращает URI вставленного элемента, а при массовой вставке возвращается количество вставленных элементов.Пример удаления:

ContentResolver cr = getContentResolver();

// удаление конкретной строки

cr.delete(myRowUri, null, null);

// удаление нескольких строк

String where = "_id < 5";

cr.delete(MyProvider.CONTENT_URI, where, null);

 

Пример изменения:

ContentValues newValues = new ContentValues();

newValues.put(COLUMN_NAME, newValue);

String where = "_id < 5";

getContentResolver().update(MyProvider.CONTENT_URI, newValues,

where, null);

 

4.2.2. Обмен сообщениями. Messaging

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

1.                Он позволяет сторонним серверам приложений отправлять небольшие сообщения своим Android приложениям. Сервис обмена сообщениями не предназначен для отправки большого количества пользовательских данных через сообщения. Напротив, он должен использоваться для сообщения приложению, что есть новые данные на сервере, и что приложение может забрать их.

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

3.                Он использует существующее соединение для сервисов Google. Это требует от пользователей установки учетной записи Google на их мобильных устройствах.

SMS (Short Message Service)  означает «служба коротких сообщений».  Эту службу также часто называют обменом текстовыми сообщениями. В  Android SDK поддерживаются функции отправки и получения текстовых сообщений.  Для отправки смс используется константа Intent.ACTION_SEND. Создадим простейший интферфейс для отправки смс:

<LinearLayout

    xmlns:android="http://schemas.android.com/apk/res/android"

    xmlns:tools="http://schemas.android.com/tools"

    android:layout_width="match_parent"

    android:layout_height="match_parent"

    android:orientation="vertical">

     <EditText android:id="@+id/number"

        android:layout_width="match_parent"

        android:layout_height="wrap_content"

        android:cursorVisible="true"

        android:hint="Введите номер"

        android:editable="true"

        android:singleLine="true" />

    <EditText android:id="@+id/message"

        android:layout_width="match_parent"

        android:layout_height="wrap_content"

        android:cursorVisible="true"

        android:hint="Введите сообщение"

        android:editable="true"

        android:singleLine="false" />

    <Button android:id="@+id/sms"

        android:layout_width="match_parent"

        android:layout_height="wrap_content"

        android:text="Отправить"

        android:onClick="smsSend" />

</LinearLayout>

 

И определим обработчик кнопки в коде activity:

package com.example.eugene.telephoneapp;

import android.support.v7.app.ActionBarActivity;

import android.os.Bundle;

import android.view.Menu;

import android.view.MenuItem;

import android.content.Intent;

import android.view.View;

import android.widget.EditText;

import android.net.Uri;

 public class MainActivity extends ActionBarActivity {

     @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main);

    }

    public void smsSend(View v) {

        EditText number=(EditText)findViewById(R.id.number);

        EditText message=(EditText)findViewById(R.id.message);

        String toSms="smsto:"+number.getText().toString();

        String messageText= message.getText().toString();

        Intent sms=new Intent(Intent.ACTION_SENDTO, Uri.parse(toSms));

         sms.putExtra("sms_body", messageText);

        startActivity(sms);    }

}

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

Отправка смс в AndroidНабор смс в Android

Рис. 4.2. Отправка сообщения

После выбора программы можно будет подредактировать и уже потом окончательно отправить сообщение.

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

<?xml version="1.0" encoding="utf-8"?>

<manifest xmlns:android="http://schemas.android.com/apk/res/android"

    package="com.example.eugene.telephoneapp" >

    <uses-permission android:name="android.permission.SEND_SMS" />

Исправим код обработчика кнопки:

public void dial(View v) {

         EditText number=(EditText)findViewById(R.id.number);

    EditText message=(EditText)findViewById(R.id.message);

         String numberText = number.getText().toString();

    String messageText= message.getText().toString();

         SmsManager.getDefault()

           .sendTextMessage(numberText, null, messageText.toString(), null, null);

}

Для отправки используется класс SmsManager из пакета android.telephony. Его статический метод getDefault() возвращает объект данного класса. Для самой отправки применяется метод sendTextMessage(), который принимает пять параметров: номер телефона, адрес сервисного центра (в примере null), сообщение к отправке и два спецальных объкта PendingIntent, которые показывают статус отправки и доставки (в примере также null).

4.3.Определение местоположения пользователя

4.3.1. Сетевое программирование в мобильных приложениях

Приложение Android, как и любые другие программы для мобильных устройств, обычно не велики по размеру, но очень функциональны. Один из способов, которая достигается такая разнообразная функциональность на небольшом устройстве- получение программы информации из различных источников. Например, на T-mobileG1 при поставке содержится программа Maps, довольно совершенными картографическими функциями.

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

Обычно для интеграции нескольких программ используется протокол HTTP. Например, в Интернете может быть доступен сервлет Java, предоставляющий службы, которые нужны вам для более эффективной работы одной из программ Android .

Как применить этот сервлет в Android? Интересно отметить, что в состав AndroidSDKвходит клиент HttpClient для Аpache, являющийся универсальным средством для работы с J2EE. В AndroidSDKсодержится версия HttpClient, модифицирован­ная с учетом требований Android, но прикладные программные интерфейсы (API) очень похожи на соответствующие APIиз J2EE.

Клиент ApacheHttpClientявляется комплексным и многосторонним. Хотя этот клиент полностью поддерживает протокол HTTP, вы, скорее всего, будете пользо­ваться методами HTTPGETи POST.

Ниже показана общая схема использования HttpClient.

1.     Создание HttpClient(или получение имеющейся ссылки).

2.     Инстанцирование нового HTTP-метода, например PostMethodили GetMethod.

3.     Установка имен и значений параметров HTTP.

4.     Выполнение вызова HTTPпри помощи HttpClient.

5.     Обработка отклика HTTP.

В коде показано, как выполнить запрос HTTPGETпри помощи HttpClient. Поскольку в коде заложена попытка выхода в Интернет, при использовании HTTP-вызовов с примене­нием HttpClient необходимо добавить в файл описания право доступа android.permission.INTERNET. Использование HttpClient для получения запроса HTTP GET:

import java.io.BufferedReader;

import java.io.IOException;

import java.io.InputStreamReader;

import java.net.URI;

import org.apache.http.HttpResponse;

import org.apache.http.client.HttpClient;

import org.apache.http.client.methods.HttpGet;

import org.apache.http.impl.client.DefaultHttpClient;

public class TestHttpGet {

public void executeHttpGet() throws Exception {

BufferedReader in = null; try {

HttpClient client = new DefaultHttpClient();

HttpGet request = new HttpGet();

request.setURI(new URI("http://code.google.com/android/"));

HttpResponse response = client.execute(request);

in = new BufferedReader  (new InputStreamReader(response.getEntity().getContent()));

StringBuffer sb = new StringBuffer(“”);

String line =

String NL = System.getProperty("line.separator");

 while ((line = in.readLine()) != null) { sb.appendline + NL); }

in.closed; String page = sb.toString();

System.out.println(page);

} finally {  if (in != null) { try {  in.closed;

} catch (IOException e) { e.printStackTraced; }

}  }   }   }

В HttpClientпредусмотрены абстракции для описания HTTP-запросов различных типов, в частности HttpGet, HttpPostи т.д. Для выполнения самого HTTP-запроса вызывается метод client.execute(). После выполнения запроса код считывает весь полученный ответ и помещает его в объект-строку(string). Обратите внимание — BufferedReaderза­крывается в блоке кода finally. Вместе с этим завершается и базовое НТТР- соединение.

Класс из кода не является дополнением android.арр.Activity. Иными словами, для работы с HttpClientвам не обязательно находиться в контек­сте определенного явления, так как HttpClientвходит в пакет программ Android и этот клиент может использоваться и в контексте компонента Android, и как часть отдельного класса.

Код выполняет HTTP-запрос, не передавая на сервер никаких параметров HTTP. Параметры «имя/значение» можно передавать вместе с запро­сом, добавляя пары «имя/значение» к URL. Добавление параметров к запросу HTTP GET:

HttpGet method = new HttpGet("http://somehost/WS2/Upload.aspx?one=valueGoesHere"); client.execute(method);

При выполнении HTTP GETпараметры (имена и значения) запроса передаются в URL. При передаче параметров таким способом вы сталкиваетесь с рядом ограничений. Дело в том, что длина гиперссылки не должна превышать 2048 символов. Вместо HTTP GETможно использовать HTTP POST. Метод POSTявляется более гибким — при его применении параметры передаются в теле запроса.

Выполнение вызова HTTP POSTочень напоминает вызов HTTP GET. Выполнение запроса HTTPPOSTпри помощи HttpClient:

import java.util.ArrayList;

import java.util.List;

import org.apache.http.HttpResponse;

import org.apache.http.NameVa1uePair;

import org.apache.http.client.HttpClient;

import org.apache.http.client.entity.UrlEncodedFormEntity;

import org.apache.http.client.methods.HttpPost:

import org.apache.http.impl.client.DefaultHttpClient;

import org.apache.http.message.BasicNameValuePair;

public class TestHttpPost {

public String executeHttpPost() throws Exception {

BufferedReader in = null; try {

HttpClient client = new DefaultHttpClient();

HttpPost request = new HttpPost(

"http://somewebsite/WS2/Upload.aspx");

List<NameValuePair> postParameters = new ArrayList<NameValuePair>(); postParameters.add(new BasicNameValuePair("one","valueGoesHere"));

UrlEncodedFormEntity formEntity = new UrlEncodedFormEntity( postParameters);

request.setEntity(formEntity);

HttpResponse response = client.execute(request);

in = new BufferedReader(new

InputStreamReader(response.getEntity().getContent())):

StringBuffer sb = new StringBuffer(“”);

String line =String NL - System.getProperty("line.separator");

while ((line = in.readLine()) != null) { sb.appendline + NL);

in.closed;

String result = sb.toString(); return result;

} finally { if (in != null) { try {  in.closed;

} catch (IOException e) { e.printStackTraced;

}  }     }

При выполнении вызовов HTTP POSTпараметры форм вида «имя/значение» обычно кодируются как часть URL, передаваемой при запросе HTTP. Чтобы осуществить такую операциюнужно создать список экземпляров объектов NameValuePaiг а затем заключить этот список в объект UrlEncodedFormEntity.

Объект NameValuePaiг содержит в себе комбинацию «имя/значение», а класс UrlEncodedFormEntityумеет кодировать список объектов NameValuePaiг для последующего их применения в вы­зовах HTTP(обычно используются вызовы POST). После создания UrlEncodedFormEntityможно задать UrlEncodedFormEntityв качестве типа сущности (entity type) HttpPostа затем выполнить запрос.

Далее создается список объектов NameValuePaiг, который заполняется параметрами отдель­ных пар «имя/значение». Именем параметра будет one, а значением – valueGoesHere. Затем создается экземпляр UrlEncodedFormEntity. Его конструктору передается список NameValuePairobjects. Наконец, мы вызываем метод setEntity. А запрос POSTвыполняем при помощи экземпляра HttpClient.

HTTP POST позволяет передавать простые параметры «имя/значение», а также сложные параметры, например файлы. HTTP POSTтакже может работать с запро­сами другого формата, который называется multipartPOST. Используя такой тип, вы можете передавать с запросом не только пары «имя/значение», но и любые файлы. К сожалению, multipartPOSTнапрямую не поддерживается в той версии HttpClient, которая входит в состав Android.

 

4.3.2.  Работа с сервером

Многие мобильные приложения используют архитектуру клиент-сервер. Общая схема следующая:

 

·    сервер — представляет собой программу, работающую на удаленном компьютере, и реализующую функционал «общения» с приложениями-клиентами (слушает запросы, распознает переданные параметры и значения, корректно отвечает на них);

·    клиент —программа на мобильном устройстве, которая умеет формировать понятный серверу запрос и читать полученный ответ;

·    интерфейс взаимодействия - формат и способ передачи/получения запросов/ответов обеими сторонами.

Реализуем сервер и Android клиент, работающий с ним. Как пример, будем использовать мобильный интернет-мессенджер (Viber, ICQ), а приложение назовем «интернет-чат».

Клиент, установленный на устройстве А, посылает сообщение для клиента, установленного на устройстве Б и наоборот. Сервер играет роль связующего звена между устройством А и Б… С, Д… и т.д. Также он играет роль «накопителя» сообщений, для их восстановления, на случай удаления на одном из клиентских устройств.

Для хранения сообщений используем SQL БД как на сервере, так и на устройствах-клиентах. Дополнительно, интернет-чат будет уметь стартовать вместе с запуском устройства и работать в фоне. Взаимодействие будет происходить путем HTTP запросов и JSON ответов.

Для реализации «сервера», нам нужно зарегистрироваться на любом хостинге, который дает возможность работы с SQL и PHP.

Создаем пустую SQL БД, в ней создаем таблицу.

CREATETABLE`chat` (

`_id`int(11) NOTNULL AUTO_INCREMENT,

`author`textCHARACTERSET utf8 COLLATE utf8_unicode_ci NOTNULL,

`client`textCHARACTERSET utf8 COLLATE utf8_unicode_ci NOTNULL,

`data`bigint(20) NOTNULL,

`text`textCHARACTERSET utf8 COLLATE utf8_unicode_ci NOTNULL,

PRIMARYKEY (`_id`) )

Структура следующая:

1.                       author — автор сообщения;

2.                       client — получатель сообщения;

3.                       data — время и дата получения сообщения на сервере;

4.                       text — сообщение.

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

$mysql_host = "localhost"; // sql сервер, может быть локальным или внешним.

   // например mysql5.000webhost.com

$mysql_user = "l29340eb_chat"; // пользователь

$mysql_password = "123456789"; // пароль

$mysql_database = "l29340eb_chat"; // имя базы данных на сервере SQL

Файл chat.php это наш api, реализующий структуру понятных серверу запросов.

<?php // сохранить в utf-8 !

//  эти значения задавались при создании БД на сервере

$mysql_host = "localhost"; // sql сервер

$mysql_user = "l29340eb_chat"; // пользователь

$mysql_password = "123456789"; // пароль

$mysql_database = "l29340eb_chat"; // имя базы данных chat

// проверяем переданные в строке запроса параметры

// например ...chat.php?action=select

// переменная action может быть:

// select - формируем содержимое таблицы chat в JSON и отправляем назад

// insert - встваляем новую строку в таблицу chat, так же нужны 4 параметра : автор/получатель/время создания/сообщение

// ВАЖНО время создания мы не передаем в параметрах, его берем текущее на сервере

// delete - удаляет ВСЕ записи из таблицы chat - пусть будет для быстрой очистки

//  получимпереданный action

if (isset($_GET["action"])) {  $action = $_GET['action']; }

//  если action=insert тогдаполучимеще author|client|text

if (isset($_GET["author"])) {     $author = $_GET['author'];}

if (isset($_GET["client"])) {     $client = $_GET['client'];}

if (isset($_GET["text"])) {     $text = $_GET['text'];  }

// если action=select тогда получим еще data - от после какого времени передавать ответ

if (isset($_GET["data"])) {  $data = $_GET['data'];}

mysql_connect($mysql_host, $mysql_user, $mysql_password); // коннектксерверу SQL

mysql_select_db($mysql_database); // коннекткБДнасервере

mysql_set_charset('utf8'); // кодировка

//  обрабатываем запрос если он был

if($action == select){ // еслидействие SELECT

if($data == null){

// выберем из таблицы chat ВСЕ данные что есть и вернем их в JSON

$q=mysql_query("SELECT * FROM chat");

}else{

// выберем из таблицы chat ВСЕ данные ПОЗНЕЕ ОПРЕДЕЛЕННОГО ВРЕМЕНИ и вернем их в JSON

$q=mysql_query("SELECT * FROM chat WHERE data > $data");  }

while($e=mysql_fetch_assoc($q))

        $output[]=$e;

print(json_encode($output));  }

if($action == insert && $author != null && $client != null && $text != null){ // еслидействие INSERT иестьвсечтонужно

// время = время сервера а не клиента !

$current_time = round(microtime(1) * 1000);

// примерпередачискриптуданных:

// chat.php?action=insert&author=author&client=client&text=text

// вставимстрокуспереданнымипараметрами

mysql_query("INSERT INTO `chat`(`author`,`client`,`data`,`text`) VALUES ('$author','$client','$current_time','$text')");}

if($action == delete){ // еслидействие DELETE

// полностьюобнулимтаблицузаписей

mysql_query("TRUNCATE TABLE `chat`");  }

mysql_close();

?>

Структура запросов к api:

·    обязательный атрибут action — может быть равен select (сервер ответит списком записей из своей БД), insert (сервер добавить новую запись в свою БД), delete (сервер очистит свою БД)

·    если action=insert, нам нужно будет передать дополнительные параметры: author (кто написал сообщение), client (кому адресовано сообщение), text (сообщение)

·    action=select может содержать дополнительный параметр data, в этом случае ответ сервера содержит не все сообщения из БД, а только те, у которых время создания позднее переданного

Примеры:

·    chat.php?action=delete – удалит все записи на сервере

·    chat.php?action=insert&author=Jon&client=Smith&text=Hello — добавит на сервере новую запись: автор Jon, получатель Smith, содержание Hello

·    chat.php?action=select&data=151351333 — вернет все записи, полученные после переданного времени в long формате

 

Клиентская часть

В фоне работает FoneService.java, который, в отдельном потоке, каждые 15 секунд делает запрос на сервер. Если ответ сервера содержит новые сообщения, FoneService.java записывает их в локальную БД и отправляет сообщение ChatActivity.java о необходимости обновить ListView, с сообщениями. ChatActivity.java (если она в этот момент открыта) получает сообщение и обновляет содержимое ListView из локальной БД.

Отправка нового сообщения из ChatActivity.java происходит сразу на сервер, минуя FoneService.java. При этом наше сообщение НЕ записывается в локальную БД! Там оно появится только после получения его назад в виде ответа сервера. Такую реализацию я использовал в связи с важным нюансом работы любого интернет-чата — обязательной группировкой сообщений по времени. Если не использовать группировку по времени, будет нарушена последовательность сообщений. Учитывая, что клиентские приложения просто физически не могут быть синхронизированы с точностью до миллисекунд, а возможно будут работать даже в разных часовых поясах, логичнее всего будет использовать время сервера.

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

В MainActivity.java, для наглядности, я добавил возможность удаления сообщений из локальной БД — это эквивалентно чистой установке приложения (в этом случае FoneService отправит на сервер запрос на получение всех сообщений выбранного чата). Так же есть возможность послать запрос на удаление всех сообщений из БД, расположенной на сервере.

Платформа Android основана на ядре Linux® и содержит богатый набор сетевых средств.

В таблице 1 приведены некоторые пакеты, относящиеся к сетевым возможностям, которые присутствуют в SDK Android.

Таблица 1. Сетевые пакеты SDK Android

Пакет

Описание

java.net

Содержит классы, связанные с сетевыми функциями, в том числе сокеты потоков и датаграмм, протокол IP, а также общие средства для работы с HTTP. Это многоцелевой ресурс для работы с сетями. Пользуясь этим знакомым пакетом, опытные Java-разработчики смогут сразу же приступить к созданию приложений.

java.io

Этот пакет чрезвычайно важен, хотя и не относится непосредственно к сетям. Его классы используются сокетами и соединениями, содержащимися в других пакетах Java. Они используются также для обмена с локальными файлами (что часто происходит при взаимодействии с сетью).

java.nio

Содержит классы, которые служат буфером для определенных типов данных. Удобен для организации сетевой связи между двумя конечными точками средствами Java.

org.apache.*

Набор пакетов, которые обеспечивают точный контроль и функции для HTTP-коммуникаций. Это Apache - знакомый и популярный Web-сервер с открытым исходным кодом.

android.net

Содержит дополнительные сокеты доступа к сети в дополнение к основным классам java.net.*. Этот пакет включает в себя класс URI, который часто используется в разработке приложений Android, выходящих за рамки традиционных сетевых функций.

android.net.http

Содержит классы для работы с сертификатами SSL.

android.net.wifi

Содержит классы для реализации всех аспектов WiFi (802.11 Wireless Ethernet) на платформе Android. Не все устройства оснащены возможностями WiFi, особенно учитывая, что Android пробивает себе дорогу в сегмент "раскладушек" от таких производителей сотовых телефонов, как Motorola и LG.

android.telephony.gsm

Содержит классы, необходимые для управления (текстовыми) сообщениями SMS и для их передачи. Со временем, вероятно, появятся дополнительные пакеты, предоставляющие аналогичные функции в не-GSM сетях, таких как CDMA, что-то вроде android.telephony.cdma.

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

Чтобы продемонстрировать, как легко подключить Android к сети, приведем пример извлечения текста из Web-страницы. 

Извлечение текста из Web-страницы

Рис.4.5. Извлечение текста из Web-страницы

Сначала рассмотрим аспекты пользовательского интерфейса, а затем разберем код, относящийся к сети.

Здесь имеется три элемента пользовательского интерфейса:

·                     EditText позволяет указать Web-страницу (на рисунке 1).

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

·                     полученные данные отображаются в поле TextView.

В листинге 1 представлен файл main.xml, который представляет собой полный макет интерфейса пользователя для этого приложения.

Листинг 1. main.xml

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

    android:orientation="vertical"

    android:layout_width="fill_parent"

    android:layout_height="fill_parent"    >

<EditText

android:layout_height="wrap_content"

android:id="@+id/address"

android:layout_width="fill_parent"

android:text="http://google.com">

</EditText>

<Button

 android:id="@+id/ButtonGo"

 android:layout_width="wrap_content"

 android:layout_height="wrap_content"

 android:text="go!" >

</Button>

<TextView 

    android:layout_width="fill_parent"

    android:layout_height="fill_parent"

    android:background="#ffffff"

    android:textColor="#000000"

    android:id="@+id/pagetext"    />

</LinearLayout>

В листинге 2 показан код Java для данного примера.

Листинг 2. GetWebPage.java

package com.msi.getwebpage;

 import android.app.Activity;

import android.os.Bundle;

// used for interacting with user interface

import android.widget.Button;

import android.widget.TextView;

import android.widget.EditText;

import android.view.View;

// used for passing data

import android.os.Handler;

import android.os.Message;

// used for connectivity

import java.io.BufferedReader;

import java.io.InputStreamReader;

import java.net.URL;

import java.net.URLConnection;

public class GetWebPage extends Activity {

    /** Called when the activity is first created. */

    Handler h;

         @Override

    public void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.main);

         final EditText eText = (EditText) findViewById(R.id.address);

        final TextView tView = (TextView) findViewById(R.id.pagetext);

         this.h = new Handler() {

             @Override

            public void handleMessage(Message msg) {

                // process incoming messages here

                switch (msg.what) {

                    case 0:

                        tView.append((String) msg.obj);

                        break;                }

                super.handleMessage(msg);            }        };

        final Button button = (Button) findViewById(R.id.ButtonGo);

        button.setOnClickListener(new Button.OnClickListener() {

            public void onClick(View v) {

                try {

                    tView.setText("");

                // Perform action on click

                    URL url = new URL(eText.getText().toString());

                    URLConnection conn = url.openConnection();

                    // Get the response

                    BufferedReader rd = new BufferedReader(new

InputStreamReader(conn.getInputStream()));

                    String line = "";

                    while ((line = rd.readLine()) != null) {

                        Message lmsg;

                        lmsg = new Message();

                        lmsg.obj = line;

                        lmsg.what = 0;

                        GetWebPage.this.h.sendMessage(lmsg);    }          }

                catch (Exception e) {       }       }      });      }}

Этот код можно разделить на несколько общих областей. Несколько важных (обязательных) операторов импорта обеспечивают правильные ссылки на используемые в приложении классы пользовательского интерфейса, передаваемых данных и сетевых функций. Весь код, относящийся к работе с сетями, находится в методе OnClick класса OnClickListener. Он извлекается при нажатии кнопки, помеченной на рисунке 1.

Классы URL и URLConnection обеспечивают фактическую связь с выбранным пользователем Web-сайтом. Пример BufferedReader выполняет чтение данных, поступающих через соединение с Web-сайтом. После чтения каждой строки ее текст добавляется к TextView. Эти данные не просто присваиваются TextView (хотя в данном примере это можно было бы сделать). Мы предложили механизм создания объекта сообщения и передачи этого объекта экземпляру обработчика. Этот предпочтительный способ обновления пользовательского интерфейса, особенно в реальных приложениях, где несколько потоков могут выполняться одновременно.

В данном примере приложение Android поддерживает связь с Web-сервером HTTP, таким как Apache или Internet Information Server (IIS на сервере Microsoft®). Если бы приложение непосредственно обращалось к сокету TCP, а не HTTP, его пришлось бы строить иначе. В листинге 3 представлен фрагмент кода, демонстрирующий другие средства взаимодействия с удаленным сервером. Этот код реализован как отдельный поток.

Листинг 3. Клиент времени.

    public class Requester extends Thread {

        Socket requestSocket;

        String message;

        StringBuilder returnStringBuffer = new StringBuilder();

        Message lmsg;

        int ch;

        @Override

        public void run() {

            try {

                this.requestSocket = new Socket("remote.servername.com", 13);

                InputStreamReader isr = new InputStreamReader(this.requestSocket.

getInputStream(), "ISO-8859-1");

                while ((this.ch = isr.read()) != -1) {

                    this.returnStringBuffer.append((char) this.ch);  }

                this.message = this.returnStringBuffer.toString();

                this.lmsg = new Message();

                this.lmsg.obj = this.message;

                this.lmsg.what = 0;

                h.sendMessage(this.lmsg);

                this.requestSocket.close();

            } catch (Exception ee) {

                Log.d("sample application", "failed to read data" + ee.getMessage());

            }       }    }

Как и в предыдущем примере, приведенный выше код использует подход сообщения и обработчика для возврата данных отправителю с обновлением пользовательского интерфейса и последующей обработкой. В отличие от кода из листинга 1, этот пример не связывается с сервером HTTP, поэтому класс URLConnection не используется. Вместо этого, с помощью класса более низкого уровня Socket через порт 13 открывается потоковое сокет-соединение с удаленным сервером. Порт 13 представляет собой классическое приложение "сервера времени".

Сервер времени принимает входящее сокет-соединение и возвращает в вызывающий сокет дату и время в текстовом формате. Отправив данные, сервер закрывает сокет. Этот пример демонстрирует также использование метода InputStreamReader и конкретной кодировки.

Еще одна задача, которую можно решать с помощью Android — отправка текстовых сообщений. Пример приведен в листинге 4.

Листинг 4. Отправка текстового сообщения

void sendMessage(String recipient,String myMessage) {

 SmsManager sm = SmsManager.getDefault();

 sm.sendTextMessage("destination number",null,"hello there",null,null);

}

Процесс отправки текстового сообщения предельно прост. Сначала получим ссылку на SmsManager с помощью статического метода getDefault(). Затем применяем метод sendTextMessage. Аргументы:

Номер сотового телефона получателя

Включает код города.

Телефон сервисного центра

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

Ваше сообщение

Длина сообщения должна быть меньше 160 байт, иначе данные будут разбиты на несколько сообщений.

Интент отправки

Факультативное действие (intent), инициируемое после отправки сообщения или в случае ошибки. Если такое уведомление не требуется, этому параметру можно передать значение null.

Интент доставки

Факультативное действие (intent), инициируемое после получения подтверждения доставки. Если такое уведомление не требуется, этому параметру можно передать значение null.

ПлатформаAndroid, подключенная к Web-странице или специальному приложению TCP показано в листинге 4, передача текстовых сообщений осуществляется очень просто. С помощью факультативных параметров интентов можно также предпринимать какие-то действия, когда сообщение отправлено, а затем - когда оно доставлено. Это мощная функция доступна не на всех мобильных платформах.

4.3.2. Определение местоположения пользователя

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

Для этого используется провайдер и  через провайдер  можно получить  данные.

На данный момент есть два провайдера: GPS и Network.

GPS –это данные с GPS-спутников.

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

Напишем простое приложение, которое будет запрашивать и отображать координаты. СоздаёмпроектВ strings.xml добавимстроки:

<stringname="provider_gps">GPS</string>
<stringname="provider_network">Network</string>
<stringname="location_settings">Location settings</string>

Для отображения приложения на экране  main.xml:

<?xmlversion="1.0"encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="5dp">
<TextView
android:id="@+id/tvTitleGPS"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/provider_gps"
android:textSize="30sp">
</TextView>
<TextView
android:id="@+id/tvEnabledGPS"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="24sp">
</TextView>
<TextView
android:id="@+id/tvStatusGPS"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="24sp">
</TextView>
<TextView
android:id="@+id/tvLocationGPS"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="24sp">
</TextView>
<TextView
android:id="@+id/tvTitleNet"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:text="@string/provider_network"
android:textSize="30sp">
</TextView>
<TextView
android:id="@+id/tvEnabledNet"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="24sp">
</TextView>
<TextView
android:id="@+id/tvStatusNet"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="24sp">
</TextView>
<TextView
android:id="@+id/tvLocationNet"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="24sp">
</TextView>
<Button
android:id="@+id/btnLocationSettings"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:onClick="onClickLocationSettings"
android:text="@string/location_settings">
</Button>
</LinearLayout>

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

MainActivity.java:

package ru.develop.p1381location;
import java.util.Date;
import android.app.Activity;
import android.content.Intent;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;
public class MainActivity extends Activity {
  TextView tvEnabledGPS;
  TextView tvStatusGPS;
  TextView tvLocationGPS;
  TextView tvEnabledNet;
  TextView tvStatusNet;
  TextView tvLocationNet;
  private LocationManager locationManager;
  StringBuilder sbGPS = new StringBuilder();
  StringBuilder sbNet = new StringBuilder();
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
    tvEnabledGPS = (TextView) findViewById(R.id.tvEnabledGPS);
    tvStatusGPS = (TextView) findViewById(R.id.tvStatusGPS);
    tvLocationGPS = (TextView) findViewById(R.id.tvLocationGPS);
    tvEnabledNet = (TextView) findViewById(R.id.tvEnabledNet);
    tvStatusNet = (TextView) findViewById(R.id.tvStatusNet);
    tvLocationNet = (TextView) findViewById(R.id.tvLocationNet);
    locationManager = (LocationManager) getSystemService(LOCATION_SERVICE)}
  @Override
  protected void onResume() {
    super.onResume();
    locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER,
        1000 * 10, 10, locationListener);
    locationManager.requestLocationUpdates(
        LocationManager.NETWORK_PROVIDER, 1000 * 10, 10,
        locationListener);
    checkEnabled()}
  @Override
  protected void onPause() {
    super.onPause();
    locationManager.removeUpdates(locationListener)}
  private LocationListener locationListener = new LocationListener() {
    @Override
    public void onLocationChanged(Location location) {
      showLocation(location);    }
    @Override
    public void onProviderDisabled(String provider) {
      checkEnabled();    }
    @Override
    public void onProviderEnabled(String provider) {
      checkEnabled();
      showLocation(locationManager.getLastKnownLocation(provider));    }
    @Override
    public void onStatusChanged(String provider, int status, Bundle extras) {
      if (provider.equals(LocationManager.GPS_PROVIDER)) {
        tvStatusGPS.setText("Status: " + String.valueOf(status));
      } else if (provider.equals(LocationManager.NETWORK_PROVIDER)) {
        tvStatusNet.setText("Status: " + String.valueOf(status))} }
  };
  private void showLocation(Location location) {
    if (location == null)
      return;
    if (location.getProvider().equals(LocationManager.GPS_PROVIDER)) {
      tvLocationGPS.setText(formatLocation(location));
    } else if (location.getProvider().equals(
        LocationManager.NETWORK_PROVIDER)) {
      tvLocationNet.setText(formatLocation(location))}
  }
  private String formatLocation(Location location) {
    if (location == null)
      return "";
    return String.format(
      "Coordinates: lat = %1$.4f, lon = %2$.4f, time = %3$tF %3$tT",
        location.getLatitude(), location.getLongitude(), new Date(
            location.getTime()));
  }
  private void checkEnabled() {
    tvEnabledGPS.setText("Enabled: "
        + locationManager
            .isProviderEnabled(LocationManager.GPS_PROVIDER));
    tvEnabledNet.setText("Enabled: "
        + locationManager
            .isProviderEnabled(LocationManager.NETWORK_PROVIDER))}
  public void onClickLocationSettings(View view) {
    startActivity(new Intent(
        android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS))};
}

В onCreate определяемTextView-компонентыиполучаем LocationManager, черезкоторыйибудемработать.

В onResume исползуем метод requestLocationUpdates. Навходпередаем:

- типпровайдера: GPS_PROVIDER или NETWORK_PROVIDER
-
минимальноевремя (вмиллисекундах) междуполучениемданных. В коде указан 10 секунд. Для получениякоординатыбеззадержек можно вводить 0 секунд. Ноэтотолькоминимальноевремя. Реальноеожиданиеполучения данныхможетбытьпобольше.

- минимальноерасстояние (вметрах). Т.е. есливашеместоположениеизменилосьнауказанноекол-вометров, товампридутновыекоординаты. 
-
слушатель, объектlocationListener.

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

В onPause отключаемметодremoveUpdates.

locationListener реализуетинтерфейс LocationListener сметодами:

onLocationChanged новыеданныеоместоположенииобъектаLocation. Здесьвызываетсяметод showLocation, который на экране отобразит данные о местоположении.

onProviderDisabled – отключение  указанного провайдера. В этом методе вызываем свой метод checkEnabled, который на экране обновит текущие статусы провайдеров.

onProviderEnabled – включение указанного провайдера. Здесь вызывается checkEnabled. Далее метод  getLastKnownLocation (он может вернуть null) запрашивает последнее доступное местоположение от включенного провайдера и отображает его. Оно может быть вполне актуальным, если вы до этого использовали какое-либо приложение с определением местоположения.

onStatusChanged – изменение статуса указанного провайдера. В поле status могут быть значения OUT_OF_SERVICE (данные будут недоступны долгое время), TEMPORARILY_UNAVAILABLE (данные временно недоступны),  AVAILABLE (данные доступны). В этом методе мы просто выводим новый статус на экран.

Провайдеры включаются и отключаются в настройках системы. Тем самым, просто определяется доступен ли провайдер для получения от него координат. Программное включение/выключение провайдеров через стандартные методы недоступно.

Метод showLocation на вход берет Location, определяет его провайдера методом getProvider и отображает координаты в соответствующем текстовом поле.

Метод formatLocation на вход берет Location, читает из него данные и форматирует из них строку: getLatitude – широта,  getLongitude – долгота, getTime – время определения.

Метод checkEnabled определяет включены или выключены провайдерыметодом isProviderEnabled и отображает эту информацию на экране.

Метод onClickLocationSettings срабатывает по нажатию кнопки Location settings и открывает настройки, чтобы пользователь мог включить или выключить провайдер. Дляэтогоиспользуется Intent с action = ACTION_LOCATION_SOURCE_SETTINGS.

В  манифесте приложения нужно прописать разрешение на определение координат - ACCESS_FINE_LOCATION, которое позволит использовать и Network и GPS. Также существует разрешение ACCESS_COARSE_LOCATION, которое дает доступ только к Network-провайдеру.

Сохраняем и запускаем приложение. Если на мобильном аппарате выключен GPS, выключен WiFi  и выключен мобильный интернет, то при запуске приложения и отображается такой вид:

http://www.startandroid.ru/images/stories/lessons/L0138/L0138_010.png

Рис. 4.6. Запуск приложения

 

Здесь GPS выключен, Network включен. Но так как Интернет не включен  Network не отображает информацию. Необходимо включить мобильный интернет или WiFi.

После  включения WiFi  вид изменяется и через 15-20 секунд  Network  отображает информацию о текущем местоположении. Это ширина, долгота и время.

http://www.startandroid.ru/images/stories/lessons/L0138/L0138_020.png

Рис. 4.7. Отображение информацию о текущем местоположении

 

В коде  минимальная скорость обновления информации  – 10 сек. Но у провайдер Network можеть быть и больше.

Теперь включим GPS. Для этого мы специально поставили кнопку Location settings, которую надо будет нажать пользователю, чтобы перейти в настройки

http://www.startandroid.ru/images/stories/lessons/L0138/L0138_030.png

Рис. 4.8. Окно настройки

 

В приложении  видно, что GPS выключен, а Network включен.  Разумеется, GPS можно включать и выключать через быстрые настройки системы (справа сверху). Но не все пользователи об этом знают. Включаем GPS и жмем Назад, чтобы вернуться в приложение.

http://www.startandroid.ru/images/stories/lessons/L0138/L0138_040.png

Рис. 4.9. Включение GPS

 

GPS теперь показывает что он включен, ждем координаты.

http://www.startandroid.ru/images/stories/lessons/L0138/L0138_050.png

Рис. 4.10. Включенный GPS

 

У GPS через какое-то время включился статус 2 (AVAILABLE).

http://www.startandroid.ru/images/stories/lessons/L0138/L0138_060.png

Рис. 4.11. Изменение статуса

А у Network статус не обображается. Если с GPS сигналом все хорошо, то каждые 10 сек вы будете получать информацию о вашем местоположении. Если плохой сигнал то данные могут приходить реже и статус иногда меняется на 1 (TEMPORARILY_UNAVAILABLE).

Третийтиппровайдера - PASSIVE_PROVIDER. Сам по себе этот провайдер никакие данные не вернет. Но  вы сможете получать данные о местоположении, когда  кто-то еще в системе пытается определить местоположение через обычные провайдеры. Система будет дублировать результаты и вам.

Метод getAllProviders вернет вам список всех доступных провайдеров. Метод getProviders(boolean enabledOnly) вернет либо все, либо только включенные. 

Объект Location кроме координат, времени и провайдера имеет еще несколько атрибутов, которые могут прийти и пустыми:

getAccuracy – точность показания в метрах

getAltitude – высота над уровнем моря в метрах

getSpeed – скорость движения в м/с

getBearing –угол, на который текущая траектория движения отклоняется от траектории на север.

Местоположение можно протестировать и через AVD эмулятор. Для этого надо в Eclipse открыть DDMS (Window>OpenPerspective>DDMS) и выбрать вкладку EmulatorControl. Внизу будет вкладка Manual, на которой есть поля для ввода координат и кнопка отправки.

 

Контрольные вопросы:

1.     Опишите базы данных SQLite в мобильных приложениях.

2.     Как создается база данных ?

3.     Как можно работать с базой данных ?

4.     Как можно создавать запросы ?

5.     Какие методы используются для работы с базой данных ?

6.     Опишите понятие контент-провайдера.

7.     Опишите создание контент провайдера.

8.     Как используется созданные  контент провайдеры ?

9.     Какие методы используются для работы с контент провайдерами ?

10.                       Каким образом функционируют сервисы для управления сообщениями ?

11.                       Как осуществляется прием и передача сообщений ?

12.                       Как осуществляется обмен сообщениями через пользовательский интерфейс?

13.  Опишите работу с сервером.

14.  Как осуществляется подключение к серверу через HTTP GET?

15.  Как осуществляется подключение к серверу через HTTP POST?

5. РАБОТА С МОБИЛЬНЫМИ ДАТЧИКАМИ

5.1. Сенсорные возможности Android

5.1.1. Отличительные особенности смартфонов

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

В смартфонах экран занимает практически всю площадь передней панели устройства, имеет высокое разрешение и является чувствительным к прикосновениям. Благодаря такой чувствительности, для взаимодействия с устройством и его приложениями можно использовать виртуальные элементы управления, например кнопки, отображаемые на экране. В смартфонах реализуется, touch-интерфейс - интерфейс, основанный на виртуальных элементах управления, выбор которых выполняется простым касанием, а также на использовании жестов (gestures). Если точек касания несколько (т. е. используется несколько пальцев), такой интерфейс, уже называется multi-touch.

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

Платформа Android позволяет разрабатывать такие приложения, которые предоставляют пользователям возможности делать фотоснимки или записывать видео, каким-то образом обрабатывать полученные материалы и использовать их далее.

5.1.2. Сенсорное управление

Сенсорное управление подразумевает использование сенсорных жестов для взаимодействия с приложением. Ниже представлен набор жестов, поддерживаемый системой Android:

      Касание (touch).

Использование: Запуск действия по умолчанию для выбранного элемента.

Выполнение: нажать, отпустить.

      Длинное касание (long touch).

Использование: Выбор элемента. Не стоит использовать этот жест для вызова контекстного меню.

Выполнение: нажать, ждать, отпустить.

      Скольжение или перетаскивание (swipe or drag).

Использование: Прокрутка содержимого или навигация между элементами интерфейса одного уровня иерархии.

Выполнение: нажать, переместить, отпустить.

      Скольжение после длинного касания (long press drag).

Использование: Перегруппировка данных или перемещение в контейнер.

Выполнение: длительное касание, переместить, отпустить.

      Двойное касание (double touch).

Использование: Увеличение масштаба, выделение текста.

Выполнение: быстрая последовательность двух касаний.

      Перетаскивание с двойным касанием (double touch drag).

Использование: Изменение размеров: расширение или сужение по отношению к центру жеста.

Выполнение: касание, следующее за двойным касанием со смещением вверх или вниз при этом:

o   смещение вверх уменьшает размер содержимого;

o   смещение вниз увеличивает размер содержимого.

      Сведение пальцев (pinch close).

Использование: уменьшение содержимого, сворачивание.

Выполнение: касание экрана двумя пальцами, свести, отпустить.

      Разведение пальцев (pinch open).

Использование: увеличение содержимого, разворачивание.

Выполнение: касание экрана двумя пальцами, развести, отпустить.

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

Основные действия, которые может произвести пользователь при взаимодействии с сенсорным экраном: коснуться экрана пальцем, переместить палец по экрану и отпустить. Эти действия распознаются системой Android, как сенсорные события (touch-события).

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

Жест начинается, при первом касании экрана, продолжается пока система отслеживает положение пальцев пользователя и заканчивается получением финального события, состоящего в том, что ни один палец не касается экрана. Объект MotionEvent, передаваемый в метод onTouchEvent(), предоставляет детали каждого взаимодействия. Рассмотрим основные константы класса MotionEvent, определяющие сенсорные события:

·                 MotionEvent.ACTION_DOWN - касание экрана пальцем, является начальной точкой для любого сенсорного события или жеста;

·                 MotionEvent.ACTION_MOVE - перемещение пальца по экрану;

·                 MotionEvent.ACTION_UP - поднятие пальца от экрана.

Приложение может использовать предоставленные данные для распознавания жеста.

Можно реализовать свою собственную обработку событий для распознавания жеста, таким образом можно работать с произвольными жестами в приложении. Если же в приложении необходимо использовать стандартные жесты, можно воспользоваться классом GestureDetector. Этот класс позволяет распознать стандартные жесты без обработки отдельных сенсорных событий.

5.1.3. Распознавание жестов

Android предоставляет класс GestureDetector для распознавания стандартных жестов. Некоторые жесты, которые он поддерживает включают: onDown()onLongPress()onFling() и т. д. Можно использовать класс GestureDetector в связке с методом onTouchEvent().

Начиная с версии 1.6, Android предоставляет API для работы с жестами, который располагается в пакете android.gesture и позволяет сохранять, загружать, создавать и распознавать жесты. Виртуальное устройство Android (AVD), начиная все с той же версии 1.6, содержит предустановленное приложение, которое называется Gesture Builder и позволяет создавать жесты. После создания жесты сохраняются на SD карте виртуального устройства и могут быть добавлены в приложение в виде бинарного ресурса.

Для распознавания жестов необходимо добавить компонент GestureOverlayView, в XML файл активности. Этот компонент может быть добавлен как обычный элемент графического интерфейса пользователя и встроен в компоновку, например RelativeLayout. C другой стороны он может быть использован, как прозрачный слой поверх других компонентов, в этом случае в XML файле активности он должен быть записан, как корневой элемент.

Кроме всего вышеперечисленного, для использования собственных жестов в приложении необходимо реализовать интерфейс OnGesturePerformedListener и его метод onGesturePerformed().

Для работы со стандартными жестами Android предоставляет классGestureDetector. Этот класс содержит два вложенных интерфейса-слушателя: OnGestureListener и OnDoubleTapListener, эти интерфейсы задают методы, отслеживающие стандартные жесты. А также GestureDetector содержит вложенный классSimpleOnGestureListener, который содержит пустые реализации, возвращающие значение false, где это необходимо, всех методов интерфейсов: OnGestureListener и OnDoubleTapListener.

Разработаем приложение, в котором продемонстрируем распознавание всех поддерживаемых жестов. Приложение содержит одну активность, одно информационное поле для вывода информации о распознанном жесте. Приложение работает следующим образом: пользователь выполняет один из поддерживаемых сенсорных жестов, в информационном поле отображается информация о распознанном жесте.

1.  Создадим простое приложение и добавим на форму TextView для вывода информации.

2.  Настроим логику приложения. В java класс, соответствующий активности внесем следующие дополнения.

o   Класс активности должен реализовывать интерфейсы: GestureDetector.OnGestureListener и GestureDetector.OnDoubleTapListener, для этого в объявление класса добавим конструкцию:

implements GestureDetector.OnGestureListener,
GestureDetector.OnDoubleTapListener

o   Нам понадобится экземпляр класса GestureDetectorCompat поэтому в качестве поля класса активности объявим следующую переменную:

GestureDetectorCompat mDetector;

В методе onCreate() класса активности, создадим экземпляр класса GestureDetectorCompat и присвоим его переменной mDetector:

mDetector = new GestureDetectorCompat(this,this);

одним из параметров конструктора является класс, который реализует интерфейсGestureDetector.OnGestureListener, в нашем случае использовано словоthis, т. е. параметром является сам класс активности. Этот интерфейс уведомляет пользователей когда появляется определенное сенсорное событие.

В методе OnCreate() класса активности, следующая строка:

mDetector.setOnDoubleTapListener(this);

устанавливает слушатель событий, связанных с двойным касанием, это должен быть класс, реализующий интерфейсGestureDetector.OnDoubleTapListener. В нашем случае использовано словоthis, т.е. слушателем будет опять сам класс активности.

Чтобы позволить вашему объекту GestureDetector получать события, необходимо переопределить метод onTouchEvent() для активности или элемента GUI. И передавать в экземпляр детектора все обнаруженные события.

publicbooleanonTouchEvent(MotionEventevent){ 
this.mDetector.onTouchEvent(event);
    // Be sure to call the superclass implementation
return super.onTouchEvent(event);
}

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

Методы интерфейса GestureDetector.OnGestureListener:

onDown()

- отслеживает появление касания, т. е. палец прижат к экрану;

onFling()

- отслеживает появление жеста смахивания;

onLongPress()

- отслеживает удерживание пальца прижатым к экрану длительное время;

onScroll()

- отслеживает появление жеста прокрутки (пролистывания);

onShowPress()

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

onSingleTapUp()

- отслеживает появление жеста одиночного нажатия (клик).

Методы интерфейса GestureDetector.OnDoubleTapListener:

onDoubleTap()

- отслеживает появление жеста двойного нажатия ("двойной клик");

onDoubleTapEvent()

- отслеживает появление события во время выполнения жеста двойного нажатия, включая касание, перемещение, подъем пальца.

onSingleTapConfirmed()

- отслеживает появление жеста одиночного нажатия (клик).

В листинге 1 представлен код приложения, в котором распознаются все поддерживаемые жесты, информация о появившемся и распознанном жесте выдается в информационное поле (TextView).

В качестве практики предлагается воспроизвести данное приложение и проверить, как система распознает тот или иной жест. Очень полезно для понимания, как выполняются основные жесты.

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

1.                Создадим простое приложение и добавим на форму TextView для вывода информации.

2.                Настроим логику приложения. В java класс, соответствующий активности внесем следующие дополнения.

Нам понадобится экземпляр класса GestureDetectorCompat поэтому в качестве поля класса активности объявим следующую переменную:

GestureDetectorCompat mDetector;

В методе onCreate() класса активности, создадим экземпляр класса GestureDetectorCompat и присвоим его переменной mDetector:

mDetector=new GestureDetectorCompat(this, new MyGestListener());

в конструкторе аргументом, отвечающим за отслеживание сенсорных событий, служит экземпляр класса MyGestListener() - внутренний класс, который является наследником класса GestureDetector.SimpleOnGestureListener.

Имеет смысл немного рассмотреть класс GestureDetector.SimpleOnGestureListener. Этот класс реализует интерфейсы GestureDetector.OnGestureListener и GestureDetector.OnDoubleTapListener, все методы заявленные в интерфейсах в этом классе имеют пустую реализацию и те, которые должны возвращать значение, возвращают false. Поэтому для распознавания какого-то события или некоторого подмножества событий достаточно написать реализацию соответствующих методов, в классе наследнике.

В листинге 2 представлен код приложения, в котором распознается только жест смахивания, т. е. реализован метод onFling(), информация о появившемся и распознанном жесте выдается в информационное поле (TextView). В качестве слушателя используется экземпляр класса MyGestListener(), являющийся наследником класса GestureDetector.SimpleOnGestureListener.

package com.example.lagestall;
import android.os.Bundle;
import android.app.Activity;
import android.support.v4.view.GestureDetectorCompat;
import android.view.*;
import android.widget.*;
public class MainActivity extends Activity 
       implements GestureDetector.OnGestureListener,
                  GestureDetector.OnDoubleTapListener{
  TextView tvOutput;
  GestureDetectorCompat mDetector;
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    tvOutput = (TextView)findViewById(R.id.textView1);
    mDetector = new GestureDetectorCompat(this,this);
    mDetector.setOnDoubleTapListener(this);
  }
  public boolean onTouchEvent(MotionEvent event){ 
    this.mDetector.onTouchEvent(event);
    // Be sure to call the superclass implementation
    return super.onTouchEvent(event);
      }
  @Override
  public boolean onDown(MotionEvent event) { 
      tvOutput.setText("onDown: " + event.toString()); 
      return false;
  }
  @Override
  public boolean onFling(MotionEvent event1, MotionEvent event2, 
                 float velocityX, float velocityY) {
    tvOutput.setText("onFling: " + event1.toString()+event2.toString());
      return true;
  }
  @Override
  public void onLongPress(MotionEvent event) {
     tvOutput.setText("onLongPress: " + event.toString()); 
  }
  @Override
  public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,
                    float distanceY) {
     tvOutput.setText("onScroll: " + e1.toString()+e2.toString());
       return true;
  }
  @Override
  public void onShowPress(MotionEvent event) {
     tvOutput.setText("onShowPress: " + event.toString());
  }
  @Override
  public boolean onSingleTapUp(MotionEvent event) {
    tvOutput.setText("onSingleTapUp: " + event.toString());
      return true;
  }
  @Override
  public boolean onDoubleTap(MotionEvent event) {
    tvOutput.setText("onDoubleTap: " + event.toString());
      return true;
  }
  @Override
  public boolean onDoubleTapEvent(MotionEvent event) {
    tvOutput.setText("onDoubleTapEvent: " + event.toString());
      return true;
  }
  @Override
  public boolean onSingleTapConfirmed(MotionEvent event) {
     tvOutput.setText("onSingleTapConfirmed: " + event.toString());
return true;  }
}

Листинг 1. Распознавание поддерживаемых жестов с помощью реализации интерфейсов

package com.example.labgestsubset;
import android.os.Bundle;
import android.app.Activity;
import android.support.v4.view.*;
import android.view.*;
import android.widget.*;
public class SubsetGestActivity extends Activity {
private GestureDetectorCompat mDetector; 
private TextView tvOut;
@Override
protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_subset_gest);
  mDetector = new GestureDetectorCompat(this, new MyGestListener());
  tvOut = (TextView)findViewById(R.id.textView1);
}
@Override 
public boolean onTouchEvent(MotionEvent event){ 
this.mDetector.onTouchEvent(event);
return super.onTouchEvent(event);
}
class MyGestListener extends GestureDetector.SimpleOnGestureListener {
@Override
public boolean onFling(MotionEvent event1, MotionEvent event2, 
                  float velocityX, float velocityY) {
tvOut.setText("onFling: " + event1.toString()+event2.toString());
   return true;  }}
}

Листинг 2. Распознаваниежестовсиспользованиемкласса GestureDetector.SimpleOnGestureListener.

5.1.4. Создание набора жестов

Для начала создадим новое приложение.Запустимэмулятор и используем приложение Gesture Builder для создания жестов "1", "2" и "S". Жест всегда связан с именем, но имя не обязательно должно быть уникальным. Дляповышения точности в распознавании жеста рекомендуется сохранять несколько жестов с одним и тем же именем.

На рис. 5.1 можно увидеть приложение Gesture Builder в работе, чтобы добавить жест, необходимо нажать на кнопку Add gesture, в свободном пространстве изобразить жест, в поле ввода задать имя жеста. Результат последовательного добавления жестов можно увидеть на рис. 5.2.

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

Создание жестов с помощью приложения Gesture Builder

Рис. 5.1. Создание жестов с помощью приложения Gesture Builder

Набор жестов, созданных в приложении Gesture Builder

Рис. 5.2. Набор жестов, созданных в приложении Gesture Builder

Gesture Builder может сообщить, что ему некуда сохранять жесты, в этом случае необходимо запустить эмулятор с образом SD карты.

Сначала образ нужно создать с помощью утилиты mksdcard (расположена в папке <AndrSDK>/sdk/tools, где <AndrSDK> - путь, куда установлен Android SDK). В командной строке напишем:

mksdcard -lmySdCard 64Mgesture.img

    Следующим шагом будет создать и запустить эмулятор с образом gesture.img, для этого в папку <AndrSDK>/sdk/tools и выполним команду:

emulator -avd nameEmulator -sdcard gestures.img

nameEmulator - имя, которое присвоено эмулятору при создании.

Когдасоздаем или редактируем жесты с помощью Gesture Builder, создается файл gestures на SD карте эмулятора. Необходимо импортировать этот файл в директорию res/raw/, созданного проекта, в котором планируем использовать жесты.

Расположение файла gestures

Рис. 5.3. Расположение файла gestures

Самый простой способ импортировать жесты в проект заключается использовании вкладки File Explorer в компоновке (perspective) DDMS. (Window->Open Perspective->Other...->DDMS. Есливкладки File Explorer нет, можнодобавить: Window-> Show View-> File Explorer). На вкладке File Explorer найти директорию sdcard/ (директория storage/sdcard/, имеет смысл при создании жестов обратить внимание в какую директорию Gesture Builder их сохраняет). На рис. 5.3 показана вкладка File Explorer в компоновке DDMS.

Чтобы скопировать файл жестов с эмулятора в проект, необходимо выбрать его и нажать кнопку "Pull a file from the device", выделенную на рис. 5.3 красным подобием окружности. Откроется диалог с предложением выбрать папку, в которую необходимо скопировать жесты, найти папку проекта, в ней папку res/raw/ (если папки raw/ нет, ее необходимо создать) и нажать кнопку Сохранить. Теперь жесты есть в проекте и их можно использовать.

Для распознавания жестов необходимо добавить элемент GestureOverlayView в XML файл активности. Этотфайл может выглядеть, например, как показано на рис. 5.4:
XML файл активности приложения,  элемент  GestureOverlayView обычный компонент интерфейса пользователя
Рис. 5.4. XML файл активности приложения, элемент GestureOverlayView обычный компонент интерфейса пользователя
Можно добавить элемент GestureOverlayView поверх всех компонентов, как прозрачный слой, в этом случае XML файл активности может выглядеть так, как показано на рис. 5.5.
XML файл активности приложения, элемент  GestureOverlayView поверх всех компонентов интерфейса пользователя
Рис. 5.5. XML файл активности приложения, элемент GestureOverlayView поверх всех компонентов интерфейса пользователя
Далее нужно обработать ввод жеста пользователя, сравнить с загруженными жестами илиопределить жест и сообщить пользователю, что такого жеста нет. Всяработа будет выполняться в java файле, описывающем главную активность приложения. Внесем в этот класс следующие дополнения:
Класс активности должен реализовывать интерфейс OnGesturePerformedListener, для этого в объявление класса добавим конструкцию: 
implements OnGesturePerformedListener;
Потом понадобятся экземпляры классов GestureLibrary и GestureOverlayView, поэтому в качестве полей класса активности объявим следующие переменные: 
GestureLibrary gLib;
GestureOverlayView gestures;
    В методе onCreate() выполним следующие действия: 
gLib = GestureLibraries.fromRawResource(this, R.raw.gestures); 
if (!gLib.load()) {
finish();
}
 
В первой строке выполнена инициализация переменной gLib жестами, загруженными из файла gestures папки res/raw/. 
Оператор if выполняет проверку загружены ли жесты, если нет, выполняется выход из приложения.
Добавим в метод onCreate(): 
gestures = (GestureOverlayView)  findViewById(R.id.gestureOverlayView1);
gestures.addOnGesturePerformedListener(this);
 
Для инициализации переменной gesture и подключения к ней слушателя событий появления жеста.
Реализация метода OnGesturePerformed(), который и будет вызываться при появлении события, соответствующего какому-либо жесту:
public void onGesturePerformed(GestureOverlayView overlay, Gesture gesture) {
//Создаёт ArrayList c загруженными из gestures жестами
ArrayList<Prediction> predictions = gLib.recognize(gesture);
        if (predictions.size() > 0) {
          //еслизагруженхотябыодинжестиз gestures