Разработка приложения для автоматического обеспечения передачи оповещения с мобильного телефона на компьютер

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

Рубрика Программирование, компьютеры и кибернетика
Вид дипломная работа
Язык русский
Дата добавления 17.07.2016
Размер файла 706,7 K

Отправить свою хорошую работу в базу знаний просто. Используйте форму, расположенную ниже

Студенты, аспиранты, молодые ученые, использующие базу знаний в своей учебе и работе, будут вам очень благодарны.

Размещено на http://www.allbest.ru/

МИНИСТЕРСТВО ОБРАЗОВАНИЯ И НАУКИ РФ

Федеральное государственное автономное образовательное учреждение

высшего образования

"БАЛТИЙСКИЙ ФЕДЕРАЛЬНЫЙ УНИВЕРСИТЕТ

ИМЕНИ ИММАНУИЛА КАНТА"

Институт транспорта и технического сервиса

Дипломная работа

Разработка приложения для автоматического обеспечения передачи оповещения с мобильного телефона на компьютер

Разработал студент

Шелыгин Д.С.

дневной формы обучения

Специальность "Программирование в компьютерных системах"

Научный руководитель

Румянцева Е.В.

Калининград 2016

Содержание

  • Введение
  • 1. Назначение и область применения
  • 2. Описание и обоснование
  • 2.1 Обзор мобильной операционной системы Android
  • 2.1.1 Архитектура ОС Android
  • 2.1.2 Уровень приложений
  • 2.1.3 Уровень каркаса приложений
  • 2.1.4 Уровень библиотек
  • 2.1.5 Уровень среды исполнения
  • 2.1.6 Уровень ядра Linux
  • 2.2 Описание разработанной системы
  • 2.2.1 Структура системы
  • 2.2.3 Назначение и принцип работы Android-приложения
  • 2.2.3 Назначение и принцип работы ПК-клиента
  • 2.2.4 Назначение и принцип работы сервера
  • 2.3 Безопасность разработанной системы
  • 3. Результаты тестирования
  • 4. Экономическое обоснование дипломного проекта
  • 4.1 Расчет себестоимости создания системы
  • 4.2 Расчет срока окупаемости системы
  • 4.3 Анализ действующих цен на рынке
  • 5. Охрана окружающей среды и техника безопасности
  • Заключение
  • Список используемых источников

Введение

С каждым днем персональный компьютер все больше и больше вытесняется смартфонами. Уже с уверенностью можно сказать, что настала эра мобильных телефонов. Большинство приложении ежедневно портируются на Android и iOS. Мобильные технологии развиваются с невероятной скоростью. Изо дня в день телефоны становятся все мощнее и функциональнее. Скоро их возможности совсем не будут уступать возможностям персонального компьютера. Человек нуждается в мобильных приложениях в частности из-за того, что свой смартфон он может использовать где угодно и когда угодно, в отличии от персонального компьютера. К тому же мобильный интернет есть уже абсолютно в любом населенном пункте.

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

Задачей для данной дипломной работы является разработка системы синхронизации уведомлений. Приложение под Android, клиент для Windows и сервер. Используемые языки программирование: Java и C#.

Цель дипломной работы:

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

2. Разработать клиентское приложение для персональных компьютеров с операционной системой Windows с.net Framework версии 4 и выше. Клиент должен получать от сервера данные, отправленные ему с Android-устройства и выводить сообщение и этими данными на экран компьютера.

3. Разработать сервер, работающий также под управлением Windows. Сервер должен получать данные с Android-приложения и отправлять их нужному ПК-клиенту.

Решаемые задачи:

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

2. Ознакомиться с многопоточными приложениями и особенностями платформы Android.

3. Закрепить знания разработки клиент-серверных приложений на языке C#.

4. Научиться разрабатывать асинхронные серверные приложения.

1. Назначение и область применения

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

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

синхронизация уведомление приложение сервер

2. Описание и обоснование

2.1 Обзор мобильной операционной системы Android

ОС Android - операционная система для мобильных телефонов, планшетных компьютеров и нетбуков, которая основывается на ядре Linux. Изначально ОС Android разрабатывала компания Android Inc., но затем ее купила компания Google. Впоследствии, компания Google инициировала создание альянса компаний Open Handset Alliance (OHA), занимающегося поддержкой и дальнейшим развитием платформы. Первая версия ОС Google Android вышла в сентябре 2008 года. В конце 2010 года ОС Android стала самой продаваемой ОС для смартфонов.

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

Для разработки приложений под платформу Android используется набор инструментов и библиотек API - Android SDK, предназначенный для компьютеров с архитектурой процессора x86 под операционными системами Windows, Mac OS X и Linux. Для разработки требуется среда исполнения Java Runtime Environment (JRE), комплект разработчика Java Development Kit (JDK), среда разработки Android Studio и Android SDK. Разработку приложений для ОС Android можно вести на языке Java. Кроме того, существуют плагины, облегчающие разработку Android-приложений в средах разработки IntelliJ IDEA и NetBeans IDE. MonoDroid SDK позволяет писать для ОС Android на C# и других языках.

2.1.1 Архитектура ОС Android

На Рисунке 1 представлена диаграмма основных компонентов операционной системы Android.

Размещено на http://www.allbest.ru/

Рисунок 1. Основные компоненты ОС Android

2.1.2 Уровень приложений

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

2.1.3 Уровень каркаса приложений

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

Системы и службы являются основой всех приложений:

1. Менеджер действий (Activity Manager) позволяет управлять жизненным циклом приложения, предоставлять ему систему навигации по истории работы с действиями.

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

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

4. Менеджер ресурсов (Resource Manager) предназначается для получения доступа к графическим, строковым и прочим видам ресурсов.

5. Менеджер извещений (Notification Manager) - это менеджер, позволяющий каждому приложению отображать пользовательские уведомления в строке статуса.

2.1.4 Уровень библиотек

Платформа Android имеет несколько C/C++ библиотек, которые используются разными компонентами операционной системы. Application Framework предоставляет разработчикам доступ к функциям этих библиотек. Примеры библиотек:

1. System C library - реализация базовой системной библиотеки C (libc) для встраиваемых устройств, на ядре Linux.

2. Media Libraries - библиотеки, предназначающиеся для поддержки воспроизведения и записи большинства популярных аудио-форматов и видео-форматов, таких как MP3, MPEG4, AAC, JPG, PNG и прочих. Они основаны на технологии PacketVideo`s OpenCORE.

3. LibWebCore - ядро встроенного web-браузера.

4. Surface Manager (менеджер поверхностей) - библиотека, предоставляющая доступ к подсистеме отображения 2D - и 3D - графических слоев.

5. SGL (Scalable Graphics Library) - библиотека для работы с 2D-графикой, основанная на библиотеке SDL (Simple DirectMedia Layer).

6.3D libraries - библиотеки для работы с 3D-графикой, основанные на OpenGL ES 1.0 API.

7. FreeType - библиотека, необходимая для работы со шрифтами.

8. SQLite - легковесная реляционная система управления базами данных.

2.1.5 Уровень среды исполнения

В состав ОС Android входит набор библиотек ядра, которые предоставляют большую часть функциональности библиотек ядра языка Java.

Платформа использует виртуальную машину Dalvik. Это оптимизированная, регистр-ориентированная виртуальная машина. В отличии от нее, стандартная виртуальная машина Java является стек-ориентированной. Любое приложение работает в рамках своего собственного процесса и со своим собственным экземпляром виртуальной машины. Виртуальная машина Dalvik использует формат Dalvik Executable (*. dex). Этот формат оптимизирован для использования приложением минимального объема памяти. Это происходит благодаря такими базовыми функциями ядра Linux, как организация поточной обработки и низкоуровневое управление памятью. Специальная утилита dx, входящая в состав SDK компилирует байт-код Java, на котором написаны приложения, в dex-формат.

2.1.6 Уровень ядра Linux

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

2.2 Описание разработанной системы

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

2.2.1 Структура системы

Система состоит из приложения для Android, клиентского приложения для Windows и сервера.

Размещено на http://www.allbest.ru/

Рисунок 2. Структура системы синхронизации уведомлений

2.2.3 Назначение и принцип работы Android-приложения

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

Сервисы (службы) в Android работают как фоновые процессы и представлены классом android. app. Service. Они не имеют пользовательского интерфейса и нужны в тех случаях, когда не требуется вмешательства пользователя. Сервисы работают в фоновом режиме, выполняя сетевые запросы к веб-серверу, обрабатывая информацию, запуская уведомления и т.д. Сервис может быть запущен и будет продолжать работать до тех пор, пока кто-нибудь не остановит его или пока он не остановит себя сам. Сервисы предназначены для длительного существования, в отличие от активностей (activity). Они могут работать, постоянно перезапускаясь, выполняя постоянные задачи или выполняя задачи, требующие много времени.

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

Рисунок 3. Стартовый экран Android-приложения

После того, как оба поля заполнены, кнопка становится активной, и пользователь может на нее нажать. После нажатия на кнопку, происходит подключение к серверу с заданным ip-адресом и портом 31111, и на устройстве создается новый сервис, который называется Notification Catch Service. Сервис необходимо вручную включить в меню настроек телефона во вкладке "Специальные возможности" ("Accessibility”).

Рисунок 4. Notification catch service во вкладке специальных возможностей.

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

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

Для того, что соединение не обрывалось при отключении сервиса, класс, создающий соединение, ServerConnection, расширяет класс AsyncTask.

AsyncTask - это абстрактный класс, позволяющий правильно и легко использовать главный поток приложения, обслуживающий интерфейс пользователя (UI thread). Этот класс позволяет выполнять длительные операции в отдельном потоке и отображать их результаты в UI thread, без всякой необходимости вручную манипулировать потоками thread или обработчиками (handler).

AsyncTask разработан, чтобы быть классом-оберткой (helper class) вокруг классов Thread и Handler, и он не составляет универсальную платформу для поточной обработки. AsyncTasks идеально подходит не только для коротких операций (занимающих по времени около нескольких секунд), но и для операций, выполняемых в бесконечном цикле.

Как понятно уже из названия, класс AsyncTask предназначен для выполнения асинхронных по отношению к UI thread задач. Асинхронная задача - это некое вычисление, которое работает в фоновом потоке, и свои результаты публикует в UI thread. Асинхронная задача определена 3 стандартными типами, которые называются Params, Progress и Result, и 4 методами, называемыми onPreExecute, doInBackground, onProgressUpdate и onPostExecute. Для начала работы объекта этого класса, используется метод Execute.

Метод onPreExecute выполняется один раз - сразу после того, как выполнится метод Execute. Это, своего рода, конструктор класса. Обычно его используют для инициализации объектов класса.

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

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

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

В данном приложении использовались лишь три метода - onPreExecute, doInBackground и onPostExecute, потому как никаких промежуточных операций выполнять не требуется. В методе onPreExecute инициализируются объекты классов Socket и PrintWriter. В методе doInBackground серверу отправляется сообщение следующего типа: "@@onConnection# + имя пользователя". Затем соединение простаивает для того, чтобы в дальнейшем с его помощью отправлять серверу сообщения. В методе onPreExecute это соединение уничтожается.

@Override

protected Void doInBackground (Void. params) {

try {

sendMessage ("@@onConnection#" + MainActivity. getName ());

}

catch (IOException ex) {

ex. printStackTrace ();

}

return null;

}

@Override

protected Void onPreExecute (Void. params) {

try{

socket = new Socket (SERVER_IP, SERVER_PORT);

pw = new PrintWriter (socket. getOutputStream (), true);

}

catch (IOException ex) {

ex. printStackTrace ();

}

return null;

}

@Override

protected Void onPostExecute (Void. params) {

try{

socket. close ();

pw. flush ();

pw. close ();

}

catch (Exception ex) {

ex. printStackTrace ();

}

}

Рисунок 4. Исходный код класса ServerConnection

В данном случае класс AsyncTask не принимает никаких параметров, то есть все три параметра равны Void.

public class ServerConnection extends AsyncTask<Void, Void, Void>

Рисунок 5. Объявление класса ServerConnection

Также, класс ServerConnection имеет метод sendMessage, принимающий аргумент типа String и не возвращающий ничего. Этот метод, при помощи ранее инициализированного объекта класса PrintWriter, отправляет серверу сообщение, которым и является передаваемый аргумент.

public void sendMessage (String msg) {

try {

pw. print (msg);

pw. flush ();

} catch (Exception ioException) {

ioException. printStackTrace ();

}

}

Рисунок 6. Метод sendMessage

Самым главным классом в любом Android-приложении является класс MainActivity. С его метода onCreate запускается приложение. В данном приложении в методе onCreate объявляется слушатель (Listener) нажатия на кнопку "GO!".

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

Обработка большинства событий, таких как, например, нажатие на кнопку, клик мышью и прочих, происходит посредством связывания этого события с каким-либо методом, который будет его обрабатывать. Начиная с Java версии 2, обработка событий основывается на модели делегирования событий. Эта модель состоит из блока прослушивания события (EventListener), ожидающего поступления определенного события от источника, за которым следует его обработка и возвращается управление. Источником называют объект, генерирующий событие, при изменении его внутреннего состояния. Например, если изменился размер какого-либо элемента, изменилось значение некоего поля, был клик кнопки мыши на элементе формы или выбрано какое-либо значение из выпадающего списка. После генерации объект-событие передается для обработки зарегистрированному в источнике блоку прослушивания как параметр его методов - обработчиков событий. Объекты классов, которые реализуют интерфейсы прослушивания событий, определенных в пакете java. awt. event, представляют собой блоки прослушивания слушателя. При создании собственных классов прослушивания, соответствующие методы, которые были объявлены в используемых интерфейсах, требуется явно реализовать. Эти методы и будут являться обработчиками события. Объект-событие, который передается источником блоку прослушивания является аргументом обработчика события. Объект класса - блока прослушивания события необходимо зарегистрировать в источнике методом addListener. После чего объект-слушатель начнет реагировать именно на данное событие и вызывать указанный метод-обработчик события. Такая логика обработки событий предоставляет легкое отделение интерфейсной части приложения от его функциональной части, что считается необходимым при проектировании современных приложений.

@Override

protected void onCreate (Bundle savedInstanceState) {

super. onCreate (savedInstanceState);

setContentView (R. layout. activity_main);

final Button button = (Button) findViewById (R. id. okButton);

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

final EditText ipEditText = (EditText) findViewById (R. id. ipEditText);

button. setOnClickListener (new View. OnClickListener () {

@Override

public void onClick (View view) {

name = editText. getText (). toString ();

ip = ipEditText. getText (). toString ();

editText. setVisibility (View. INVISIBLE);

button. setVisibility (View. INVISIBLE);

goService ();

}

});

editText. setOnTouchListener (new View. OnTouchListener () {

@Override

public boolean onTouch (View v, MotionEvent event) {

button. setEnabled (true);

return false;

}

});

FloatingActionButton fab = (FloatingActionButton) findViewById (R. id. fab);

fab. setOnClickListener (new View. OnClickListener () {

@Override

public void onClick (View view) {

Context context = getApplicationContext ();

Intent notificationIntent = new Intent (context, MainActivity. class);

PendingIntent contentIntent = PendingIntent. getActivity (context,

0, notificationIntent,

PendingIntent. FLAG_CANCEL_CURRENT);

Notification. Builder builder = new Notification. Builder (context);

builder. setContentIntent (contentIntent)

. setSmallIcon (R. mipmap. ic_launcher)

. setTicker ("Test notification")

. setWhen (System. currentTimeMillis ())

. setAutoCancel (true)

. setContentTitle ("Напоминание")

. setContentText ("notification");

Notification notification = builder. build ();

NotificationManager nm = (NotificationManager) context

. getSystemService (Context. NOTIFICATION_SERVICE);

nm. notify (NOTIFY_ID, notification);

}

});

}

Рисунок 7. Метод onCreate

Помимо метода onCreate, в классе MainActivity описаны еще несколько классов. Автоматически созданные средой разработки методы onCreateOptionMenu и OnOptionItemSelected. Эти методы необходимы для реализации меню в приложении. А также методы getName, getIP и goService.

public static String getName () { return name; }

public static String getIP () { return ip; }

private void goService () {

Intent intent = new Intent (MainActivity. this, NotificationWorker. class);

startService (intent);

}

@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) {

return true;

}

return super. onOptionsItemSelected (item);

}

Рисунок 8. Методы класса MainActivity

Метод getName является, так называемым, геттером, статическим методом, позволяющим безопасно получить значение какого-либо объекта или переменной. Данный метод возвращает значение, введенное пользователем в поле "Имя". Метод getIP - еще один геттер, возвращающий введенный пользователем IP-адрес. goService является методом, который создает вышеупомянутый сервис на устройстве. В этом методе инициализируется объект класса Intent, в конструктор которого передается ссылка на класс NotificationWorker. Затем вызывается метод startService, аргументом которого передается вышеупомянутый объект класса Intent. Intent (от англ. Намерение) - это механизм, описывающий какую-то операцию. Например, выбор фотографии, отправка письма, совершение звонка, запуск браузера или открытие страницы по указанному вэб-адресу. В Android-приложениях через намерения работает большое количество операций.

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

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

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

Класс NotificationWorker - это класс, наследуемый от класса AccessibilityService, и предназначенный для описания работы сервиса.

В конструкторе класса NotificationWorker инициализируется объект класса serverConnection и выполняется его метод execute.

public NotificationWorker () {

serverConnection = new ServerConnection ();

serverConnection. execute ();

}

Рисунок 9. Конструктор класса NotificationWorker

Далее, переопределены три метода класса AccessibilityService:

onAccessibilityEvent (AccessibilityEvent event) - это метод, который будет выполняться, когда на устройстве обнаружится новое уведомление. Объект event хранит в себе большое количество различной информации о произошедшем событии. В данном методе создается локальная переменная типа String (строка) и присваивается значение следующего вида - "name#packageName: text", где name - это имя пользователя, указанное при первом запуске приложения, знак # - это separator (разделитель), он предназначен для того, чтобы сервер отделил имя клиента от сообщения, packageName - это название пакета приложения, создавшего уведомление, text - это текст сообщения в уведомлении. Далее, метод encode зашифровывает текст для безопасности. Затем вызывается метод класса serverConnection sendMessage, в который передается вся вышеуказанная, уже зашифрованная, строка.

@Override

public void onAccessibilityEvent (AccessibilityEvent event) {

if (event. getEventType () == AccessibilityEvent. TYPE_NOTIFICATION_STATE_CHANGED) {

String packageName = String. valueOf (event. getPackageName ());

String text = event. getText (). toString ();

String toSend = name + "#" + packageName + ": " + text;

Log. i ("notifyService", toSend);

toSend = encode (toSend);

serverConnection. sendMessage (toSend);

}

}

Рисунок 10. Метод onAccessibilityEvent

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

@Override

public void onInterrupt () {

}

Рисунок 11. Метод onIterrupt

onServiceConnected - это метод, который выполняется при запуске сервиса. Здесь описывается на какой вид события должен реагировать сервис. Сделано это при помощи класса AccessibilityServiceInfo, свойству eventType которого присвоено константное значение enum-класса (перечисление) AccessibilityEvent TYPE_NOTIFICATION_STATE_CHANGED.

@Override

protected void onServiceConnected () {

Log. i ("notifyService", "onServiceConnected ");

AccessibilityServiceInfo info = new AccessibilityServiceInfo ();

info. eventTypes = AccessibilityEvent. TYPE_NOTIFICATION_STATE_CHANGED;

info. notificationTimeout = 100;

info. feedbackType = AccessibilityEvent. TYPES_ALL_MASK;

setServiceInfo (info);

}

Рисунок 12. Метод onServiceConnected

2.2.3 Назначение и принцип работы ПК-клиента

Клиент для ПК работает под управлением операционной системы Windows. Он соединяется с тем же сервером, с которым соединено Android-устройство пользователя. Клиент в отдельном потоке начинает прослушивать сокет и, получив данные от сервера, отображает их на экране. Клиент разрабатывался с помощью технологии WPF (Windows Presentation Foundation). WPF является системой построения клиентских приложений для операционной системы Windows с визуально привлекательными возможностями взаимодействия с пользователем. Она входит в состав.net Framework с версии 3.0. Для построения графического интерфейса пользователя в WPF используется язык XAML.

XAML (eXtensible Application Markup Language) является декларативным языком разметки, упрощающим создание графического интерфейса пользователя для приложения на.net Framework. XAML позволяет сначала создавать видимые элементы графического интерфейса в декларативной разметке XAML, а после чего отделять его от логики времени выполнения, при помощи использования файлов кода программной части, присоединенных к разметке XAML при помощи определений разделяемых классов. Этот язык разметки представляет создание экземпляров объектов напрямую, в определенном наборе резервных типов, которые определенны в сборках. В этом и есть его отличие от остальных языков разметки, являющихся интерпретируемыми языками, которые не имеют прямой связи с системой резервных типов. Язык XAML позволяет обеспечивать рабочий процесс нескольким участникам, разрабатывающим пользовательский интерфейс и логику приложения отдельно, используя потенциально различные средства.

При запуске приложение, пользователю необходимо ввести имя и IP-адрес сервера, и нажать кнопку "ОК". После чего создается экземпляр класса TcpClient и выполняется его метод Connect, в который передается введенный IP-адрес и порт 31111. Для того, чтобы работа GUI (graphical user interface) не мешал соединению, т.е. не прерывал его при отображении полученного сообщения от сервера, соединение создается в отдельном потоке и продолжает свою работу до тех пор, пока приложение полностью не закроется.

Рисунок 13. Стартовый экран ПК-клиента.

Данное приложение состоит из двух классов - MainWindow и Connection.

Класс Connection - это класс, который создается соединение с сервером и позволяет в отдельном потоке прослушивать это соединение. Он содержит в себе вложенный класс (inner class) State, который предназначен для хранения информации о соединении. Класс State содержит в себе три поля (field) - buffer, socket и msg. Поле buffer - это массив типа byte []. Он необходим для временного хранения информации, полученной с сервера, так как информация передается в виде массива байт. Поле socket - это объект класса Socket, предназначенное для хранения соединения. Поле msg - это переменная типа string. Оно требуется для хранения в себе сообщения, полученного из массива байт buffer.

private class State

{

public State (byte [] buffer, Socket socket, string msg)

{

this. buffer = buffer;

this. socket = socket;

this. msg = msg;

}

public byte [] buffer;

public Socket socket;

public string msg;

}

Рисунок 14. Внутренний класс State

Конструктор класса Connection принимает три аргумента:

- string name - строковое значение имени пользователя

- string ip - строковое значение ip-адреса сервера, к которому необходимо подключиться.

- int port - значение типа Integer, обозначающее порт.

В конструкторе класса Connection создается объект класса TcpClient. TcpClient - это класс, предоставляющий простейшие методы для создания соединения, отправления и приема различной информации через протокол tcp. Затем создается объект класса IPEndPoint, в конструктор которого передаются ip-адрес и порт. После чего выполняется метод Connect, в который передается объект класса IPEndPoint. Далее объявляется поле ns типа NetworkStream, которому присваивается поток соединения объекта client при помощи метода GetStream. Затем серверу отправляется сообщение следующего типа: "@@onConnection#name-pc", где @@onConnection - это флаг, обозначающий, что это сообщение предназначено только для занесения имени клиента в пул подключений сервера, а не переотправки его другому клиенту, знак # - это сепаратор, отделяющий флаг от имени, name является именем пользователя, которое он ввел в соответствующее поле на форме, постфикс "-pc" необходим для того, что сервер в дальнейшем отличал какое соединение от смартфона, а какое от персонального компьютера, так как имена обязательно должны вводиться одинаковые. После отправки сообщения, вызывается метод StartReceiving, в который передается объект client.

public Connection (string name, string ip, int port)

{

client = new TcpClient ();

IPEndPoint point = new IPEndPoint (IPAddress. Parse (ip), port);

client. Connect (point);

NetworkStream ns = client. GetStream ();

byte [] buffer = Encoding. ASCII. GetBytes ("@@onConnection#" + name + "-pc");

ns. Write (buffer, 0, buffer. Length);

// ns. Close ();

StartReceiving (client);

}

Рисунок 15. Конструктор класса Connection

Метод StartReceiving - это метод, в котором клиент начинает прослушивать сервер через указанные ip-адрес и порт. В этом методе создаются три локальные переменные - buffer размером 1024 байта, пустое строковое поле message и socket, которому приравнивается свойство Client объекта client. Затем создается экземпляр внутреннего класса State, который принимает buffer, socket и message. После этого у объекта socket вызывается метод BeginReceive, который начинает асинхронный прием данных с подключенного объекта Socket. Метод BeginReceive имеет множество перегрузок, одна из которых принимает следующие аргументы:

- Buffer - массив типа Byte, который является местоположением памяти для полученных данных.

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

- Size - число принимаемых байтов.

- socketFlags - поразрядное сочетание значений SocketFlags.

- Callback - делегат AsyncCallback, ссылающийся на метод, вызываемый по завершении данной операции.

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

socket. BeginReceive (state. buffer, 0, state. buffer. Length, SocketFlags. None, new AsyncCallback (ReceiveCallback), state);

Рисунок 16. Вызов метода BeginReceive

Затем полю message приравнивается значение, полученное из массива байт buffer.

public void StartReceiving (TcpClient client)

{

byte [] buffer = new byte [1024];

string message = String. Empty;

Socket socket = client. Client;

// while (true)

// {

State state = new State (buffer, socket, message);

socket. BeginReceive (state. buffer, 0, state. buffer. Length, SocketFlags. None, new AsyncCallback (ReceiveCallback), state);

message = Encoding. ASCII. GetString (buffer);

// }

}

Рисунок 17. Метод StartReceiving

ReceiveCallback - это метод, на который ссылается делегат AsyncCallback в методе BeginReceive. ReceiveCallback вызывается после получения от сервера каких-либо данных. В этом методе создается локальный экземпляр класса State, которому присваивается значение свойства AsyncState объекта IAsyncResult, который передается аргументом в ReceiveCallback. Так как AsyncState является свойством типа object, а не State, то необходимо использовать кастинг (casting). Кастинг - это явное преобразование объекта одного типа к другому типу.

Затем создается локальный объект класса Socket, которому присваивается поле socket объекта state, тем самым позволяя в этом методе работать все с тем же подключением.

После этого вызывается метод объекта socket EndRecieve, возвращающий значение типа int. Это значение присваивается локальной переменной bytesRead. Если от сервера получены хоть какие-то данные, то bytesRead будет иметь значение больше нуля.

За счет этого происходит проверка, получены ли какие-нибудь данные. Если получены, тогда массив преобразовывается в строку и создается MessageBox, отображающий эти данные. MessageBox - это класс, создающий диалоговое окно, отображающее текст с информацией. MessageBox может содердать текст, кнопки, символы, с помощью которых информируется и инструктируется пользователь. Класс MessageBox входит пространство имен System. Windows. Forms.

private void ReceiveCallback (IAsyncResult ar)

{

try

{

State state = (State) ar. AsyncState;

Socket socket = state. socket;

int bytesRead = socket. EndReceive (ar);

if (bytesRead > 0)

{

string msg = Encoding. ASCII. GetString (state. buffer, 0, bytesRead);

MessageBox. Show (msg);

}

}

catch (Exception e)

{

MessageBox. Show (e. ToString ());

}

}

Рисунок 18. Метод ReceiveCallback

Класс MainWindow - это класс для работы с главной формой. Он предназначен для описания только логики работы интерфейса пользователя. Сам интерфейс описывается в отдельном файле MainWindow. xaml.

<Window x: Class="Notiful_Client. MainWindow"

xmlns="http://schemas. microsoft.com/winfx/2006/xaml/presentation"

xmlns: x="http://schemas. microsoft.com/winfx/2006/xaml"

xmlns: d="http://schemas. microsoft.com/expression/blend/2008"

xmlns: mc="http://schemas. openxmlformats.org/markup-compatibility/2006"

xmlns: local="clr-namespace: Notiful_Client"

mc: Ignorable="d"

Title="MainWindow" Height="350" Width="350"

ResizeMode="NoResize">

<Grid>

<Label Content="Введите имя" Margin="10,54,10,232"/>

<TextBox Name="nameTextBox" Margin="10,94,10, 196"/>

<Button Margin="27,257,237,34" Click="Button_Click" Content="OK"/>

<Label x: Name="label" Content="Введите IP" HorizontalAlignment="Left" Margin="10,130,0,0" VerticalAlignment="Top"/>

<TextBox x: Name="ipTextBox" Margin="10,156,10,137"/>

</Grid>

</Window>

Рисунок 19. XAML-разметка ПК приложения

Класс MainWindow описан в файле MainWindow. xaml. cs. Этот класс содержит только один метод и конструктор.

В конструкторе класса вызывается статический метод InitializeComponent, который необходим для отрисовки интерфейса пользователя (GUI), описанного в MainWindow. xaml.

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

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

<Button Margin="27,257,237,34" Click="Button_Click" Content="OK"/>

Рисунок 20. XAML-разметка кнопки

В этом методе строковым переменным name и ip приравниваются значения, введенные пользователем в соответствующий поля на форме. Затем инициализируется объект класса Connection, в конструктор которого передаются переменные name и ip, а также целочисленное значение 31111, означающее порт.

2.2.4 Назначение и принцип работы сервера

Сервер, также, как и ПК-клиент, запускается под управлением операционной системы Windows. На сервере реализованы асинхронные TCP-соединения. TCP (transmission control protocol) - один из основных протоколов передачи данных через интернет. Предназначается для управления передачей данных. Сети и подсети, в которых совместно используются протоколы TCP и IP называются сетями TCP/IP. В стеке протоколов TCP/IP протокол TCP работает на таком же уровне, что и протокол UDP, на транспортном. Он позволяет, методом установки логического соединения, обеспечивать надежную транспортировку данных между несколькими прикладными процессами. В протоколе TCP для связи с необходимыми прикладными процессами используются порты. Номера портов присваиваются произвольно. Однако имеются зарезервированные номера портов (например, номер 21-сервис FTP, 80 - http). В протоколе TCP порты используются немного другим способом, нежели в UDP. Для того, чтобы организовать надежную передачу данных, устанавливается логическое соединение между двумя прикладными процессами. В пределах этого соединения происходит подтверждение корректности приема для всех переданных сообщений, и при необходимости выполняется повторная передача. С TCP-соединением можно вести одновременную передачу данных в обе стороны. Это называется полнодуплексной передачей.

Идентифицируется соединения в протоколе TCP осуществляется парой полных адресов обоих взаимодействующих процессов, также называемых оконечными точками. Адрес каждой оконечной точки включает в себя: IP-адрес и номер порта. Одна оконечная точка может участвовать сразу в нескольких соединениях.

Сервер не имеет GUI (графического интерфейса пользователя). Он представляет собой обыкновенную консоль от Microsoft. Это сделано потому, что нет необходимости иметь быстрый доступ к каким-либо компонентам, соответственно не нужны никакие кнопки, меню и прочее. К тому же на систему приходится меньшая нагрузка.

Рисунок 21. Внешний вид серверного приложения

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

Многопоточная архитектура

Данная архитектура работает по принципу, при котором приложение создает пул потоков и каждому из них передает задачу с данными для обработки. Все эти задачи выполняются параллельно друг другу. Накладных расходов на синхронизацию не происходит при условии, что потоки не имеют общих данных. Это делает работу достаточно быстрой. Завершив работу, поток остается в пуле и ожидает следующей задачи, а не уничтожается, что позволяет убрать накладные расходы на создание и удаление потоков. Один поток обрабатывает только одну задачу. Пул может иметь огромное количество потоков. Медленные задачи занимают поток надолго, быстрые - обрабатываются почти мгновенно и освобождают поток для другой работы. Это позволяет медленным задачам не забирать всё процессорное время и заставлять подвисать быстрые задачи. Однако у такой системы есть определенные ограничения. Если программе потребуется обработать большое количество медленных задач, например, работающих с базой данных или с файловой системой, эти задачи займут все потоки и сделают невозможным выполнение других задач. Даже если задаче требуется 1мс чтобы выполниться, вовремя выполнена она все равно не будет. Есть вариант решения этой проблемы путем увеличения числа потоков, с целью обработки ими большего количество медленных задач. Однако, выделяет процессорное время и обрабатывает потоки операционная система. Именно поэтому с увеличением потоков увеличиваются накладные расходы на их обработку и уменьшается процессорное временя, которое выделяется каждому потоку. Блокирующие операции работы с базами данных, с файловой системой, вводом/выводом данных тоже тратят огромное количество процессорного времени, при этом не выполняя никакой полезной работы.

Асинхронная архитектура

Асинхронная архитектура основывается на механизме очереди событий (event-loop). Когда возникает некоторое событие, оно помещается в конец очереди. Эту очередь обрабатывает отдельный поток. Он берет событие с начала очереди и выполняет связанный с этим событием код. Пока очередь не пуста, процессор занят работой. Например, имеется единственный поток, обрабатывающий очередь событий. Почти все операции неблокирующие. Блокирующие тоже имеются, но их использование крайне не рекомендуется. Из очереди сообщений берется событие, связанное с приходом запроса. Обработка запроса тратит 1мс. Далее делается асинхронный неблокирующий запрос к базе данных и управление сразу же передается дальше. Можно взять из очереди следующее событие и выполнить его. Например, еще один запрос, проводится обработка, посылается запрос к БД, возвращается управление и проделывается то же самое еще один раз. И тут приходит ответ БД на самый первый запрос. Событие, связанное с ним, помещается в очередь. Если в очереди ничего не было - он сразу же выполнится, данные обрабатываются и отправляются клиенту. Если в очереди что-то есть - придется подождать обработку других событий. Обычно скорость обработки одного запроса будет сравнима со скоростью обработки многопоточной системой и блокирующими операциями. В худшем случае - на ожидание обработки других событий потратится время, и запрос обработается медленнее. Но зато в тот момент, пока система с блокирующими операциями просто ждала бы 2 мс ответа, система с неблокирующими операциями успела выполнить еще 2 части двух других запросов. Каждая задача может выполняться чуточку медленнее в целом, но в единицу времени мы можем обработать гораздо больше задач. Общая производительность будет выше. Процессор всегда будет занят полезной работой. При этом на обработку очереди и переходе от события к событию тратится гораздо меньше времени, чем на переключение между потоками в многопоточной системе. Многопоточная система с блокирующими операциями имеет большое время простоя. Чрезмерное количество потоков может создать много накладных расходов, недостаточное же количество может привести к замедлению работы при большом количестве медленных запросов. Асинхронное приложение с неблокирующими операциями использует процессорное время эффективнее, но более сложно при проектировании.

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

Серверное приложение состоит лишь из одного класса, но содержит много методов и два вложенных класса.

StateObject - вложенный класс для хранения информации. Он состоит из следующих элементов:

- workSocket - это экземпляр класса Socket

- BufferSize - переменная типа int, обозначающая размер массива buffer

- buffer - массив типа byte [], необходимый для хранения данных в виде байт

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

- name - строковое значение, обозначающее имя соединения

public class StateObject

{

public Socket workSocket = null;

public const int BufferSize = 1024;

public byte [] buffer = new byte [BufferSize];

public StringBuilder sb = new StringBuilder ();

public string name = "";

}

Рисунок 22. Внутренний класс StateObject

AsynchronousSocketListener - это второй вложенный класс, представляющий собой всю логику работы сервера. Этот класс содержит несколько методов, а также метод Main, с которого начинается любая программа.

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

В этом методе создается экземпляр класса IPHostEntry, которому присваивается имя компьютера, на котором запущен сервер, при помощи метода Dns. GetHostName. Затем экземпляру класса IPAddress приравнивается значение первого элемента массива AddressList в ipHostInfo. После чего создается IPEndPoint, аргументами которого передаются ipAddress и порт 31111.

IPHostEntry ipHostInfo = Dns. GetHostEntry (Dns. GetHostName ());

IPAddress ipAddress = ipHostInfo. AddressList [0];

IPEndPoint localEndPoint = new IPEndPoint (ipAddress, 31111);

Рисунок 23. Инициализация объектов для создания соединения

Далее создается объект класса Socket, который называется listener. В конструктор класса передаются следующие три параметра:

- AddressFamily. InterNetwork. Это означает, что сокет будет использовать сеть интернет для обмена данными.

- SocketType. Stream. Указывает, что тип сокета является поточным.

- ProtocolType. Tcp. Выбор протокола передачи данных tcp.

Socket listener = new Socket (AddressFamily. InterNetwork, SocketType. Stream, ProtocolType. Tcp);

Рисунок 24. Инициализация объекта класса Socket

Затем переменной listener методом Bind привязывается localEndPoint (т.е. локальный адрес компьютера) и методом Listen открывается сокет, готовый принимать новые подключения.

В консоль пишется фраза "Waiting for a connection…", означающая, что к серверу можно подключиться, и что он уже ожидает подключения.

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

- Callback - делегат AsyncCallback

- State - объект, содержащий сведения о состоянии входящего запроса

Делегат AsyncCallback ссылается на метод AcceptCallback.

public static void StartListening ()

{

byte [] bytes = new Byte [1024];

IPHostEntry ipHostInfo = Dns. GetHostEntry (Dns. GetHostName ());

IPAddress ipAddress = ipHostInfo. AddressList [0];

IPEndPoint localEndPoint = new IPEndPoint (ipAddress, 31111);

Socket listener = new Socket (AddressFamily. InterNetwork, SocketType. Stream, ProtocolType. Tcp);

try

{

listener. Bind (localEndPoint);

listener. Listen (100);

while (true)

{

allDone. Reset ();

Console. WriteLine ("Waiting for a connection. ");

listener. BeginAccept (

new AsyncCallback (AcceptCallback),

listener);

allDone. WaitOne ();

}

}

catch (Exception e)

{

Console. WriteLine (e. ToString ());

}

Console. WriteLine ("Press a key to continue");

Console. Read ();

}

Рисунок 25. Метод StartListening

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

Затем создается еще один локальный экземпляр класса Socket, называемый handler. Ему приравнивается возвращаемое значение метода EndAccept объекта listener.

После этого объявляется и инициализируется новый объект внутреннего класса StateObject. Его полю workSocket присваивается handler. В пул подключений (List<StateObject> sockets) добавляется этот объект.

У объекта handler вызывается метод BeginReceive, который начинает асинхронный прием данных с объекта handler. Метод BeginReceive имеет множество перегрузок, одна из которых принимает следующие аргументы:


Подобные документы

Работы в архивах красиво оформлены согласно требованиям ВУЗов и содержат рисунки, диаграммы, формулы и т.д.
PPT, PPTX и PDF-файлы представлены только в архивах.
Рекомендуем скачать работу.