Результати пошуку
ITVDN: курси програмування
Відеокурси з
програмування
Підписка

300+ курсів за популярними IT-напрямками

Вибери свою IT спеціальність

Підписка
Підписка

300+ курсів за популярними IT-напрямками

Результати пошуку за запитом: начальный курс c
Введення в розробку програм під iOS. Частина 7

Автор: Volodymyr Bozhek

Здравствуйте, дорогие читатели. В этом уроке мы: выполним рефакторинг проекта “Warehouse” под “iOS”. Создадим удобную структуру расположения модулей в проекте. научимся пользоваться библиотекой “Alamofire”. научимся сохранять объекты в настройки телефона и извлекать их. реализуем функциональность для работы со всеми сервисами, созданными в данном уроке. Откройте проект “Warehouse” под iOS, который мы делали на протяжении всех уроков. Запросы на сервер можно отправлять синхронно и асинхронно. Основное требование Apple, чтобы приложение не зависало и большие операции выполнялись в фоновом режиме. Представим ситуацию: у нас мобильный интернет “GPRS EDGE”, скорость соединения около 54 кб/сек. Если отправлять запрос на получение данных на сервер синхронно, мы блокируем работу приложения и пользователь вынужден ждать. Наша задача - дать пользователю выбор, ждать или нет. Поэтому лучше всего отправлять такой запрос асинхронно. На сегодняшний день существует такая библиотека “Alamofire”, где собраны лучшие практики асинхронного программирования и все оптимизировано за нас. Откройте браузер и перейдите по адресу: “https://github.com/Alamofire/Alamofire”. На сайте нажмите кнопку “Clone of download” и в появившемся окне нажмите “Download Zip”. Когда скачается архив, распакуйте его. Выделите файл “Alamofire.xcodeproj” и папку “Source” и скопируйте их в папку проекта “Warehouse”. Перейдите в проект “Warehouse”. В панели навигации выполните контекстное меню по папке “Warehouse” и в контекстном меню выберите “Add files to 'Warehouse'...”. В открывшемся диалоговом окне выбора файлов  выберите файл “Alamofire.xcodeproj”. Выполните в меню “Product -> Build”, чтобы собрать проект. Затем выделите в панели навигации папку “Warehouse”, в области содержимого откроются свойства проекта. Откройте вкладку “General”, пролистайте область содержимого в самый низ. Установите свойство “Embedded binaries”, нажмите плюсик. В появившемся диалоговом окне, выберите “Alamofire.Framework.iOS” и нажмите кнопку “Add”. Результат должен получиться таким. Выполните в меню “Product -> Build”, чтобы собрать проект. Итак, что мы только что сделали. Мы подключили исходники библиотеки “Alamofire” в проект “Warehouse”. Можно было это сделать и отдельным проектом, но так проще для восприятия. Затем мы подключили сборку “Alamofire.framework.iOS” в приложение “Warehouse”  для возможности обращения к классам данной библиотеки из нашего проекта. Теперь нам надо создать модели данных, в которые мы будем сохранять JSON объекты, полученные с сервера. Также необходимо предусмотреть удобство сохранения и извлечения данных, поскольку запросы будут асинхронными и просто вернуть нужный экземпляр из метода будет проблематично. Именно поэтому мы поступим следующим образом. Отправляем запрос на сервер асинхронно. В метод отправки  добавляем callback метод, который вызовется  после того, как модель была успешно сохранена в настройки телефона. Ждем ответа. Когда ответ пришел, десериализуем объект JSON в нужную модель данных, объявленную в приложении. Сохраняем заполненную модель в настройки телефона. В контроллере, из которого был отправлен запрос, в callback методе  извлекаем модель из настроек телефона и инициализируем данными элементы управления. Создайте следующую структуру папок в панели навигации. В папку “Warehouse”  были добавлены папки “Controllers”, “Protocols”, “Models”, “Services”. В папку “Controllers”  были добавлены папки “Users”, “Registration”, “Authorization”, “Supplies”. В папке “Controllers”  будут храниться модули контроллеров. В папке “Protocols”  будут храниться модули протоколов. В папке “Models”  будут храниться модули моделей данных. В папке “Services”  будут храниться модули обращения к REST сервисам. Сейчас нам необходимо провести некоторую подготовку, перед тем как начать реализовывать приложение. Пока, давайте,  добавим нужные модули. Сам по себе добавленный модуль ничего не делает. Перетащите в папку “Controllers / Supplies”  модули “SuppliesViewController.swift”, “ProductViewController.swift”. Перетащите в папку “Controllers / Authorization”  модуль “ViewController.swift”. Добавьте в папку “Controllers / Registration”  модуль “RegistrationViewController.swift”. Добавьте в папку “Controllers / Supplies”  модуль “ImageCollectionViewController.swift”. Добавьте в папку “Controllers / Users”  модули “UsersViewController.swift”, “UserItemViewController.swift”. Добавьте в папку “Protocols”  модуль “SelectedImageProtocol.swift”. Добавьте в папку “Models”  модули “ProductModel.swift”, “UserModel.swift”. Добавьте в папку “Services”  модули “UserData.swift”, “ProductData.swift”, “Services.swift”, “AuthorizationService.swift”, “ProductService.swift”, “UserService.swift”. У вас должна получиться следующая структура. Как видите, проект стал больше. Он станет еще больше, как только мы заполним добавленные модули. Откройте модуль “Models / UserModel.swift”. Заполните его, как показано ниже. В этом модуле мы создаем модель пользователя, в которую будем десериализовывать JSON объект пользователя,  полученный от сервера. В классе “UserModel”  мы добавляем поля модели “_id”, “userName”, “userPwd”, “userDesc”, которые декларировали при объявлении схемы базы данных “Mongo DB” на сервере. На 9 строке  мы подключаем пространство имен “Foundation”. На 11 строке  мы объявляем класс с именем “UserModel”, который наследует класс “NSObject”, являющийся базовым классом для всех классов в iOS платформе. Наследуемся от протокола “NSCoding”, данный протокол необходимо реализовать, чтобы объект можно было сохранять в настройках телефона и извлекать оттуда. Рассмотрим протокол “NSCoding”. Протокол содержит метод “encode” и инициализатор. Экземпляр класса “NSCoder”  позволяет кодировать и раскодировать свойства класса, значения которых могут сохраняться в хранилище настроек телефона. В методе “encode”  данные извлекаются из свойств и кодируются в формат, нужный для сохранения в настройки телефона. В инициализаторе  данные извлекаются и раскодируются из настроек телефона, затем сохраняются в нужные свойства класса. Вот так это работает. Теперь вернемся и рассмотрим дальше модуль “UserModel.swift”. На 12 строке  мы объявляем поле “_id” типа “String” и инициализируем его значением по умолчанию, пустая строка. На 13 строке  мы объявляем поле “userName” типа “String” и инициализируем его значением по умолчанию, пустая строка. На 14 строке  мы объявляем поле “userPwd” типа “String” и инициализируем его значением по умолчанию, пустая строка. На 15 строке  мы объявляем поле “userDesc” типа “String” и инициализируем его значением по умолчанию, пустая строка. На 17 строке  мы переопределяем инициализатор по умолчанию для класса “NSObject”, тем самым мы говорим, что создание объекта без передачи аргументов в инициализатор будет возможно. На 18 строке  мы вызываем базовый инициализатор из класса “NSObject”. На 21 строке  мы объявляем параметризированный инициализатор. Данный инициализатор  инициализирует поля класса “_id”, “userName”, “userPwd”, “userDesc” значениями, переданными в аргументы инициализатора. На 31 строке  мы реализуем конструктор протокола “NSCoding”. Ключевое слово “required”  обозначает, что вызов этого инициализатора обязателен в первую очередь.   Ключевое слово “convenience”  гарантирует то, что после вызова этого инициализатора  будут вызваны остальные инициализаторы классов, связанных с этим классом по цепочке вверх. На 32 строке  мы раскодируем из настроек телефона  значение свойства “_id”  путем вызова на экземпляре “aDecoder” типа “NSCoder”  метода “decodeObject”, аргумент “forKey”  которого принимает название поля/свойства, которое было сохранено в настройки телефона. Этот метод возвращает значение “Any?”, т.е. неопределенный тип (аналог в C#  - это Object, в Visual Basic - это Variant). Нам необходимо это значение вручную привести к нужному типу поля. В настройках телефона содержится большой файл, который имеет древовидную  структуру словаря ключ и значение. Чтобы привести к нужному значению, мы используем конструкцию приведения к типу “as”. Причем, мы можем управлять этой конструкцией. Например, нам надо привести тип “Any?” к типу “String”. Для этого мы используем конструкцию “as!”. Если же нам надо привести к типу “String?” (nullable тип), тогда мы используем конструкцию “as?”. Зачем мы это делаем. Настройки телефона  разбиваются на разные файлы, для каждого приложения используется свой файл с настройками. Когда приложения устанавливаются на macOS или iOS платформу, они устанавливаются в виде папки и имеют тип “Bundle”. Apple довольно кардинально подошла к этому вопросу, она сделала так, что все настройки для каждого приложения хранятся внутри “Bundle” данного приложения. И когда приложение удаляется, удаляется “Bundle” со всеми настройками с диска операционной системы и не остается никаких следов после удаления. Чего не скажешь о таких платформах, как Windows и Android, которые засоряют все пространство на диске операционной системы вокруг папки, в которую было установлено приложение, и выше. И когда приложение удаляешь, есть риск, что после этого будет необходимо еще и переустановить операционную систему. Отчасти именно из-за этого подхода техника Apple  пользуется такой популярностью и их устройства выигрывают по отказоустойчивости программного обеспечения по сравнению с конкурентами. Если приложение обновляется, то файл настроек остается старый. Чтобы обновить его, надо выполнить запись в него, тогда старые конструкции удалятся, а новые добавятся.   Представим себе ситуацию, сейчас в нашей модели “UserModel”  всего 4 поля. Мы сохранили эту модель в файл настроек телефона. Отправили в магазин, пользователи скачали, установили приложение. Вышла новая версия приложения, в которой мы добавили новое поле в эту модель с именем “userRole” типа “String”. Разумеется, первая операция, которая будет выполнена как обычно - это запрос данных из настроек телефона, но для поля “userRole”  установлено значение “nil” (аналог в C# null) и произойдет ошибка и креш приложения, так как мы не обработали эту ситуацию, а пытаемся через метод “decodeObject” получить значение этого поля и еще и явно привести его к типу “String”  через, например, оператор “as!”. Но в поле “userRole” - значение “nil” и оно не приведется к типу “String” через конструкцию “as!”, но может привестись без ошибок к типу “String?”  через “as?”. Надеюсь, я прояснил понятно, зачем нужна данная проверка. Продолжим. На 32 строке мы проверяем, доступно ли нам значение поля “_id”. Чтобы проверить, равно ли возвращаемое значение “nil”, можно было бы использовать такую конструкцию: “aDecoder.decodeObject(..) == nil”, но такая конструкция не слишком информативна. Поэтому мы используем конструкцию “(aDecoder.decodeObject(..) as? String) == nil”.       Далее, если данная конструкция возвращает значение “true”, тогда мы через тернарный оператор  присваиваем  константе “_id” значение по умолчанию пустая строка, иначе присваиваем реальное значение  поля “_id” из настроек телефона. С 33 по 35 строку  мы выполняем те же операции проверок для полей “userName”, “userPwd”, “userDesc”. На 36 строке  мы вызываем параметризированный конструктор и инициализируем его аргументы объявленными ранее константами. На 47 строке  объявлен вспомогательный метод “Populate”, который переносит данные из экземпляра “dictionary” типа “NSDictionary”  в поля класса “UserModel”. Этот метод мы будем использовать, когда будем создавать запросы к серверу и получать ответы.   В ответе содержится словарь типа “NSDictionary”, в котором находятся свойства с теми же именами, что и поля нашего класса. С 48 по 51 строку мы инициализируем поля класса “UserModel”  значениями из экземпляра “dictionary” c теми же проверками, что мы рассматривали выше. На 53 строке объявлен вспомогательный метод “PopulateArray”, который переносит данные из экземпляра “array” типа “NSArray” в массив объектов типа “UserModel”. Этот метод мы будем использовать, когда в ответе от сервера  придет массив JSON объектов типа “User”, чтобы сконвертировать массив типа “[NSDictionary]” в массив типа “[UserModel]”. На 55 строке  мы объявляем пустой массив типа “[UserModel]” c именем “result”. На 56 строке  мы выполняем цикл “foreach” по элементам массива “array”. На 58 строке  мы создаем новый экземпляр типа “UserModel” с именем “newItem”. На 59 строке  мы проверяем, содержит ли элемент массива  значение “nil”. На 60 строке  мы вызываем на экземпляре “newItem”  метод “Populate”, который перенесет данные из словаря типа “NSDictionary”  в поля экземпляра “newItem”. На 62 строке  мы обращаемся к экземпляру “result” и вызываем у него метод “append”, чтобы добавить новый элемент в массив, и добавляем проинициализированный выше экземпляр “newItem”. На 64 строке  мы возвращаем сконвертированный массив типа “[UserModel]” из метода “PopulateArray”. Откройте модуль “Models / ProductModel.swift”, заполните его в соответствии с содержимым ниже. В этом модуле мы создаем модель пользователя, в которую будем десериализовывать JSON объект пользователя,  полученный от сервера. В классе “UserModel”  мы добавляем поля модели “_id”, “userName”, “userPwd”, “userDesc”, которые декларировали при объявлении схемы базы данных “Mongo DB” на сервере. На 9 строке  мы подключаем пространство имен “Foundation”. На 11 строке  мы объявляем класс с именем “UserModel”, который наследует класс “NSObject”, являющийся базовым классом для всех классов в iOS платформе. Наследуемся от протокола “NSCoding”, данный протокол необходимо реализовать, чтобы объект можно было сохранять в настройках телефона и извлекать оттуда. Рассмотрим протокол “NSCoding”. Данный модуль не нуждается в объяснении, поскольку он по образу и подобию такой же, как и модуль “UserModel.swift”, с разницей лишь в том, что он построен на модели “Product” и содержит соответствующие поля.       Итак, модели данных мы создали, теперь мы сможем с уверенностью сохранять результаты ответа от сервера в нормальном виде и потом использовать их при инициализации элементов управления в соответствующих представлениях :) Теперь приступаем к реализации классов для отправки запросов на сервер и сохранения/загрузки полученных данных в настройки телефона. Откройте модуль “Services / Services.swift”, заполните его в соответствии с содержимым ниже. На 11 строке  объявлен класс с именем “Services”. Данный класс будет содержать вспомогательные свойства и методы, которые мы будем часто использовать при работе с ответом от сервера. На 12 строке  объявлено статическое свойство “host” типа “String” и мы ограничили его функциональность только одним аксессором доступа “get”. Т.е. значение свойства получить можно, но изменить нельзя. В данном свойстве содержится адрес, по которому доступен сервер. В прошлом уроке, когда мы делали сервер, мы использовали адрес “http://localhost:3000” , внутри приложения клиента имеется свой “localhost”, именно поэтому мы используем явный IP адрес “127.0.0.1”, чтобы запрос шел именно на сервер, а не внутрь приложения. На 18 строке  объявлен статический метод “ejectJsonArray”, который принимает аргумент “data” типа “NSData?” и возвращает массив типа “NSArray?”. Данный метод конвертирует экземпляр класса “NSData” в экземпляр класса “NSArray”. В экземпляре “data” будет содержаться массив JSON объектов, полученных от сервера. На 19 строке  объявлена переменная “json” типа “NSArray?” и инициализировано значение по умолчанию “nil”. На 20 строке  мы используем конструкцию “do .. try .. catch” (аналог в C# - “try .. catch”). Символ “_” в блоке “catch” на месте аргумента используется потому, что мы не будем использовать этот аргумент в блоке “catch”. В случае возникновения исключения мы не будем завершать аварийно приложение, а просто вернем значение “nil” по умолчанию, которое будет говорить о том, что конвертация не удалась, поскольку в NSData  не содержится JSON объект. Аналог JSON объекта в Swift - это класс “NSDictionary”. На 21 строке  мы правым операндом обращаемся к классу “JSONSerialization” и вызываем от его экземпляра статический метод “jsonObject”, который первым аргументом принимает данные типа “Data” для конвертации, вторым аргументом принимает опции конвертации. Метод “jsonObject” возвращает экземпляр типа “Any”. Далее мы приводим это значение к нужному нам типу “NSArray?”, если приведение было проведено не успешно, будет возвращено значение “nil”. Результат конвертации присваивается объявленной выше переменной “json”. Обратите внимание на ключевое слово “try” перед конструкцией “JSONSerialization.jsonObject(..)”, использование этого ключевого слова является обязательным, так как метод “jsonObject” имеет в своей сигнатуре ключевое слово “throws”, это означает, что данный метод может генерировать исключения и их необходимо обрабатывать. На 23 строке в случае исключения вызванного методом “jsonObject”  возвращается значение “nil”. На 25 строке в случае успеха операции  возвращается экземпляр типа “NSArray?”. На 28 строке  объвлен метод “ejectJsonDictionary” с той же сигнатурой, что и метод “ejectJsonArray”, но с разницей в возвращаемом значении. В этом методе возвращается экземпляр типа “NSDictionary?”. Метод подразумевает, что в экземпляре “data” типа “NSData?” содержится единичный JSON объект и пытается его  сконвертировать в тип “NSDictionary” , являющийся аналогом JSON объектов в Swift.       Реализацию метода смысла описывать не вижу, она такая же, как и в методе “ejectJsonArray”. Реализуем сохранение моделей данных “UserModel” и “ProductModel” в настройки телефона. Откройте модуль “Services / UserData.swift”, заполните его в соответствии с содержимым ниже. Модуль большой, поэтому будем разбирать его, разбив на две части по функциональности. В первой его части  будут описаны методы сохранения / извлечения единичного экземпляра типа “UserModel” и экземпляра типа массив “[UserModel]”. Во второй его части  будут описаны вспомогательные методы  для обработчиков ошибок ответа от сервера и сохранения данных в настройки телефона. Данные методы были созданы для того, чтобы не копипастить один и тот же код много раз, а стандартизировать и сделать его повторно используемым. На 9 строке  подключено пространство имен “UIKit”, необходимое для классов “UserDefaults”, “NSKeyedArchiver”, “NSKeyedUnarchiver”. В пространстве имен уже содержится подключенное пространство имен “Foundation”, поэтому нет смысла его тоже  подключать для классов “NSArray”, “NSDictionary”, “NSData”. На 10 строке  подключено пространство имен “Alamofire”, необходимое для класса “DataResponse”. На 12 строке  объявлен класс с именем “UserData”. На 13 строке  объявлен метод “saveUserModel”, который принимает аргумент “userModel” типа “UserModel”. Метод будет сохранять поля модели “UserModel” в настройки телефона. На 14 строке  правым операндом мы обращаемся от экземпляра “UserDefaults” к его статическому свойству “standard”, это свойство является источником данных по отношению к файлу настроек телефона и содержит необходимые методы для сохранения / извлечения данных. На 15 строке  правым операндом мы обращаемся к экземпляру “NSKeyedArchiver” и вызываем статический метод “archivedData”. Этот метод принимает один аргумент “withRootObject” типа “Any” и возвращает экземпляр типа “Data”. Метод упаковывает экземпляр типа “UserModel” в экземпляр типа “Data” (аналог NSData) и возвращает его. На 16 строке  мы от экземпляра “userDefaults” вызываем метод “set”, который принимает два аргумента.  Первый аргумент “_” типа “Any?” принимает экземпляр, который необходимо сохранить в настройках телефона. Второй аргумент “forKey” типа “String” необходим, чтобы задать название ключа в словаре настроек  телефона. По этому ключу будет произведено сохранение экземпляра “UserModel”. На 17 строке  мы обращаемся к экземпляру “userDefaults”, вызываем от него метод “synchronize”. Данный метод выполняет сохранение экземпляра модели “UserModel” в настройки телефона (аналог в C#, например,  'using(StreamWriter sw = File.CreateText(“test.txt”)) { sw.Write(“some text”); sw.Flush(); }', из этого примера метод “Write” аналогичен методу “set”, а метод “Flush” аналогичен методу “synchronize”). На 20 строке  мы объявили статический метод “getUserModel”, который не принимает аргументов и возвращает экземпляр типа “UserModel”. В этом методе мы излекаем модель “UserModel” из файла настроек телефона по ключу, указанному в предыдущем методе при сохранении этой модели. На 21 строке  мы получаем экземпляр источника данных настроек телефона. На 22 строке  мы проверяем, если модель “UserModel” ранее не сохранялась в настройки телефона, тогда необходимо создать пустую модель по умолчанию. Вызываем на экземпляре “userDefaults” метод “object”, который принимает аргумент “forKey” типа “String” и возвращает значение типа “Any?”. В качестве аргумента мы передаем ключ, под которым сохраняется данная модель в настройках телефона. Если значение было найдено в словаре, будет возвращен экземпля , который надо будет привести у нужному типу. Если значение не было найдено, будет возвращено значение “nil”. На 23 строке  мы создаем пустой экземпляр типа “UserModel”. На 24 строке  мы возвращаем этот экземпляр из метода. На 26 строке  код будет выполнен при наличии записи по ключу в словаре. Правым операндом мы обращаемся к экземпляру “userDefaults”, вызываем метод “object” и передаем в аргумент “forKey”  ключ, по которому надо извлечь значение. Полученное значение приводим к типу “NSData”. На 27 строке  мы обращаемся к экземпляру “NSKeyedUnarchiver” и вызываем от него статический метод “unarchiveObject”, который принимает аргумент “with” типа “Data” и возвращает значение типа “Any”. Метод конвертирует экземпляр типа “Data” в экземпляр типа “Any”. Возращаемое значение метода мы явно приводим к типу “UserModel”. На 28 строке  мы возвращаем из метода полученный из словаря экземпляр типа “UserModel”. На 31 строке  объявлен метод “saveListOfUsersModel”, который принимает аргумент “listOfUsersModel” типа “[UserModel]” и ничего не возвращает. Метод сохраняет массив “[UserModel]” в файл настроек телефона. Описывать данный метод не имеет смысла, его работа аналогична методу “saveUserModel”. На 38 строке  объявлен метод “getListOfUsersModel”, который не принимает аргументов и возвращает экземпляр типа массив “[UserModel]”. Метод извлекает из настроек телефона массив типа “[UserModel]” по ключу, указанному в методе “saveListOfUsersModel”. Описывать данный метод не имеет смысла, его работа аналогична методу “getUserModel”. Откройте модуль “ProductData.swift”, заполните его в соответствии с содержимым ниже. В первой части описано сохранение / извлечение моделей типа “ProductModel”, “[ProductModel]” в настройки телефона. Описывать код не имеет смысла, его работа точно такая же, как и в модуле “UserData.swift”. Во второй части описаны вспомогательные методы для обработки запросов от сервера и сохранения данных. Описывать код не имеет смысла, его работа точно такая же, как и в модуле “UserData.swift”. Теперь, наконец то, мы можем реализовать первый класс для работы с сервисом авторизации пользователя и разобрать работу с библиотекой “Alamofire”. Откройте модуль “Services / AuthorizationService.swift”, заполните его в соответствии с содержимым ниже. На 12 строке  объявлен класс с именем “AuthorizationService”.             На 13 строке  объявлен метод “login”, который принимает три аргумента и ничего не возвращает.             Первый аргумент “userName” типа “String” -  это имя пользователя (логин).             Второй аргумент “userPwd” типа “String” -  это пароль пользователя.             Третий аргумент - это “callback” метод, который принимает аргумент типа “Bool” и ничего не возвращает.             Метод с такой сигнатурой, переданный в третий аргумент, будет вызван по завершению обработки ответа от сервера. В случае наличия ошибок  в аргумент этого метода будет передано значение “false”. В случае успешно проведенной операции - значение “true”.             Да, можно было сделать этот аргумент другим типом, например, отдельным классом ошибок, в который можно было сохранять подробно, что за ошибка произошла.             Но это учебный пример, он итак очень большой. Я решил не усложнять этим жизнь  ни себе, ни вам :)             На 14 строке  правым операндом мы вызываем метод “request” из библиотеки “Alamofire”.             Метод принимает 5 аргументов и возвращает значение типа “DataRequest”.             Данный метод используется для отправки HTTP/HTTPS запросов на сервер.             Первый аргумент “_” типа “URLConvertible” принимает адрес, по которому необходимо отправить запрос. Мы его инициализируем значением “\(Services.host)/auth”, которое после подстановки свойства “host”  будет выглядеть вот так: “http://127.0.0.1:3000/auth”.             Второй аргумент “method” типа “HTTPMethod” принимает HTTP глагол. Тип “HTTPMethod”  является перечислением и может принимать следующие значения. Мы инициализируем данный аргумент значением “.post” (это сокращенная запись, полная запись “HTTPMethod.post”)             Третий аргумент “parameters” типа “Parameters?”. Принимает словарь, ключ - значение, мы его заполняем теми же данными, что отправляли в прошлом уроке через утилиту “Postman”. Конкретно, мы передаем значение имени пользователя и его пароль.             Четвертый аргумент “encoding” типа “ParameterEncoding”. Этим параметром задается заголовок запроса “Content-Type”, т.е. указывается тип данных, который будет отправлен на сервер, сами данные задаются в третьем аргументе “parameters”. Мы инициализируем аргумент значением “JSONEncoding.default”, что аналогично записи “application/json”. Тип “ParameterEncoding” является протоколом и имеет следующие реализации себя в классах “URLEncoding”, “JSONEncoding”, “PropertyListEncoding”. Про эти классы вы можете почитать в официальной документации на сайте “Alamofire”.             Пятый аргумент “headers” типа “HTTPHeaders?” имеет значение по умолчанию “nil”, в нашем вызове метода он не используется. Данный аргумент задается, если вы желаете отправить в заголовках запроса на сервер какую- либо информацию, например, “cookie”. Если у вас сервер написан на Web API и вы используете для авторизации OWin , с настройкой надо использовать “cookie” как ключ авторизации пользователя. Тогда при отправке запросов вам будет необходимо заполнять этот аргумент. Пример его заполнения может выглядеть вот так: Этот пример взят из моего приложения “Smart Payment”,  написанного на Swift 2.2 c минимальной версией iOS 8.3  библиотекой “Alamofire” версии 2. Чтобы этот пример работал на Swift 3, надо заменить значение параметра “encoding” на “JSONEncoding.default”. Этот пример не относится к нашему приложению и его переписывать не нужно.             На 16 строке  мы вызываем от экземпляра “r” типа “DataRequest”  метод “responseJSON”. Этот метод выполняет запрос на сервер и содержит три аргумента.             Первый аргумент “queue” типа “DispatchQueue?”  по умолчанию содержит значение “nil”. Используется при переопределении потока выполнения запроса.             Второй аргумент “options” типа “JSONSerialization.ReadingOptions” по умолчанию содержит значение “.allowFragments”. Используется для задания опция отправки запроса.    Тип “ReadingOptions” является структурой и имеет такое объявление. Третий аргумент “completionHandler” типа делегат “@escaping (DataResponse) -> Void”. Данная сигнатура принимает один аргумент типа “DataResponse” и ничего не возвращает. В этом аргументе содержится ответ от сервера. Сам метод вызывается после запроса, когда сервер отправил ответ на клиент.             Обратите внимание, на 16 строке, когда мы вызываем метод “responseJSON”, у нас нет круглых скобок после метода, а идут сразу фигурные скобки, это называется “closure” подход. В фигурных скобках мы объявляем лямбда метод с сигнатурой, соответствующей третьему аргументу метода “responseJSON”.             Запись “response in” означает следующее: “response” - это аргумент лямбда метода, а само тело метода начинается после ключевого слова “in”. Аргумент “response” имеет тип “DataResponse”.             На 17 строке  мы проверяем, если ответ пришел с ошибкой, то это означает, что данных нет и пришло пустое значение типа “nil”. Мы обращаемся к экземпляру “response” и вызываем свойство “result” типа “Result” и от свойства “result” вызываем свойство “value” типа “Value?”. Если в свойстве “value” пришло пустое значение, значит запрос не был выполнен, возможно, по причине недоступности сервера или отсутствия подключения к интернету.             На 18 строке  мы вызываем “callback” метод “completion” со значением “false”.             На 21 строке, в случае если проблем с подключением к серверу не возникло, мы правым операндом вызываем от экземпляра “Services” метод “ejectJsonDictionary”, который ранее уже подробно рассматривали. В аргумент “data” мы передаем значение свойства “data”, вызванного от экземпляра “response”. Свойство “data” имеет тип “Data?”. Левому операнду “json” присваивается экземпляр “NSDictionary”, в котором содержится JSON объект пользователя, прошедшего авторизацию, который был отправлен сервером на клиент.             На 22 строке  мы проверяем, если пришел JSON объект с ошибкой, тогда возвращаем нужный флаг в “callback” методе “completion”. Помним, что в предыдущем уроке мы создавали JSON объект с ошибкой на сервере, в случае если пользователь ввел неверные данные авторизации,  JSON выглядел вот так '{ “Error”: “Authorization error”}'.             У словаря “json” есть возможность получить все ключи, для этого надо вызвать свойство “allKeys”, которое имеет тип “[Any]”. Но,  поскольку выполнить поиск строки в массиве объектов нельзя, мы оборачиваем массив объектов в массив типа “Array” и явно приводим его к нужному нам типу массива “[String]”. Мы это делаем, поскольку знаем наверняка, что ключи JSON объекта имеют тип “String”. Затем от полученного экземпляра типа “[String]”  мы вызываем метод “contains”, в аргумент которого передает название свойства JSON объекта ошибки, значение “Error”. Если такой JSON объект пришел в ответе, значит пользователь ввел неправильные логин или пароль, или ввел несуществующего пользователя.             На 23 строке  мы вызываем “callback” метод “completion” с аргументом “false”.             На 25 строке  мы создаем пустой экземпляр “userModel” типа “UserModel”.             На 26 строке  мы вызываем на экземпляре “userModel”  метод “Populate”  в аргумент “dictionary”, передаем экземпляр “json”. Метод “Populate”  перенесет данные из экземпляра “json” типа “NSDictionary” в поля экземпляра “userModel”.             На 27 строке  мы обращаемся к экземпляру “UserData” и вызываем статический метод “saveUserModel”, чтобы сохранить данные экземпляра “userModel” в настройки телефона. В аргумент “userModel”  мы передаем экземпляр “userModel”.             На 28 строке  мы вызываем “callback” метод “completion” и передаем в него результат операции “true”.             Позже в контроллере, где мы будем вызывать метод “login”, будет передан “callback” метод “completion”, объявленный в этом контроллере. Тем самым решается проблема ожидания ответа от сервера в контроллере, так как есть “callback” метод. Внутри этого метода мы проверим результат операции и затем обратимся к экземпляру “UserData” и вызовем метод “getUserModel”, который вернет нам экземпляр “UserModel”, с которым мы сможем работать на стороне контроллера.             Откройте модуль “Services / UserService.swift”, заполните его, как показано  ниже. На 12 строке  объявлен класс “UserService”.             На 13 строке  объявлен метод “get”, который принимает два аргумента и ничего не возвращает.             Первый аргумент “id” типа “String?”  принимает идентификатор пользователя “_id”. Если передать в аргумент значение “nil”, будет возвращен весь список пользователей, если передать конкретный идентификатор, будет возвращен конкретный пользователь.             Второй аргумент “completion” типа делегат “(Bool) -> Void”  используется для получения сигнала, что ответ был получен и основные операции были выполнены.             На 29 строке  объявлен метод “create”, который принимает 4 аргумента и ничего не возвращает. Метод отправляет запрос на создание пользователя на сервер.             Первые три аргумента “userName”, “userPwd”, “userDesc” имеют тип “String” и содержат данные пользователя. Идентификатор пользователя в операции создания не нужен. Так как идентификатор пользователю присваивается на стороне сервера и данный метод выполняет как раз эту операцию.             Четвертый аргумент “completion” типа делегат “(Bool) -> Void”  используется для получения сигнала, что ответ был получен и основные операции были выполнены.             На 37 строке  объявлен метод “update”, который принимает 5 аргументов и ничего не возвращает. Данный метод отправляет запрос на обновление данных пользователя на сервер.             Первые 4 аргумента “id”, “userName”, “userPwd”, “userDesc” имеют тип “String” и содержат данные пользователя.             Пятый аргумент “completion” типа делегат “(Bool) -> Void”  используется для получения сигнала, что ответ был получен и основные операции были выполнены.             На 45 строке  мы объявляем метод “delete”, который принимает два аргумента. Этот метод отправляет запрос на удаление пользователя на сервер.             Первый аргумент “id” типа “String?”  принимает идентификатор пользователя “_id”.             Второй аргумент “completion” типа делегат “(Bool) -> Void”, используется для получения сигнала, что ответ был получен и основные операции были выполнены.               Откройте модуль “Services / ProductService.swift”, заполните его, как показано ниже На 12 строке  объявлен класс “ProductService”.             На 13 строке  объявлен метод “get”, который принимает два аргумента и ничего не возвращает.             Первый аргумент “id” типа “String?”  принимает идентификатор товара “_id”. Если передать в аргумент значение “nil”, будет возвращен весь список товаров, если передать конкретный идентификатор, будет возвращен конкретный товар.             Второй аргумент “completion” типа делегат “(Bool) -> Void”  используется для получения сигнала, что ответ был получен и основные операции были выполнены.             На 29 строке  объявлен метод “create”, который принимает 4 аргумента и ничего не возвращает. Метод отправляет запрос на создание товара на сервер.             Первые три аргумента “productImage”, “productName”, “productDescription” имеют тип “String” и содержат данные товара. Идентификатор товара в операции создания не нужен. Так как идентификатор товару присваивается на стороне сервера и данный метод выполняет как раз эту операцию.             Четвертый аргумент “completion” типа делегат “(Bool) -> Void”  используется для получения сигнала, что ответ был получен и основные операции были выполнены.             На 37 строке  объявлен метод “update”, который принимает 5 аргументов и ничего не возвращает. Данный метод отправляет запрос на обновление данных товара на сервер.             Первые 4 аргумента “id”, “productImage”, “productName”, “productDescription” имеют тип “String” и содержат данные пользователя.             Пятый аргумент “completion” типа делегат “(Bool) -> Void”  используется для получения сигнала, что ответ был получен и основные операции были выполнены.             На 45 строке  мы объявляем метод “delete”, который принимает два аргумента. Этот метод отправляет запрос на удаление товара на сервер.             Первый аргумент “id” типа “String?”  принимает идентификатор товара “_id”.             Второй аргумент “completion” типа делегат “(Bool) -> Void”  используется для получения сигнала, что ответ был получен и основные операции были выполнены.             Заметьте, адреса запросов, используемых нами при отправке на сервер, совпадают с теми, что мы использовали в прошлом уроке в утилите “Postman”.             На этом урок завершен.             На следующем уроке мы продолжим разработку клиента, создадим контроллеры и представления для редактирования пользователей, подключим работу сервисов к странице входа в систему, создадим форму регистрации пользователя.
Упаковка та розпакування в .NET

Автор: Редакція ITVDN

Мы уже знаем особенности работы с памятью и доступные структуры данных в .NET приложениях, в этом посте мы разберем упаковку и распаковку, а также рассмотрим, как эти две операции влияют на производительность приложения.   Что такое упаковка и распаковка? Зачем нам задумываться об упаковке и распаковке? Разве это не обязанность .NET-среды, которая следит за управлением данных и, соответственно, сама "выбирает" наиболее оптимальный способ их хранения? На самом деле - нет. Что очень важно знать и понимать -  так это механизм перемещения данных из области стека в кучу - и наоборот. Помните: Когда любой значимый тип присваивается к ссылочному типу данных, значение перемещается из области стека в кучу. Эта операция называется упаковкой. Когда любой ссылочный тип присваивается к значимому типу данных, значение перемещается из области кучи в стек. Это называется распаковкой. К примеру, здесь мы имеем следующий пример упаковки: А вот состояние памяти в момент произведения операции: Чтобы сохранить значение "123" в виде объекта, в куче создается "упаковка", куда впоследствии и перемещаются данные. Когда же производится распаковка: Вот что происходит с памятью: Значение "123" было изъято из упаковки и помещено назад в область стека. Заметьте, что когда тип данных i упаковывается внутри объекта o, в стеке хранится лишь ссылка, в то время как само значение хранится в куче. Как только производиться распаковка, данные в куче обязаны быть скопированы в стек (переменная j). В обоих случаях наша цель - это работать с тем самым значением (123). Как вы можете себе представить, сии операции могут быть достаточно ресурсоемкими.   Давайте рассмотрим IL Когда мы производим подобный анализ производительности, часто бывает полезно заглянуть непосредственно в Intermediate Language (IL). Мы еще не рассматривали эту концепцию, но, как вы наверняка знаете, когда мы производим компиляцию в DLL или EXE, выходной файл на самом деле содержит IL - промежуточный код, который в последствии исполняется JIT и впоследствии - виртуальной машиной. Среда выполнения .NET обязана как-то знать, нужно ли упаковывать или распаковывать определенные переменные. Поэтому для обозначения этих операций также требуются дополнительные затраты памяти. Давайте создадим несложное .NET консольное приложение: Теперь скомпилируем приложение и при помощи утилитки ILSpy посмотрим его код внутри EXE. Как только EXE-файл будет открыт в ILSpy, пронавигируемся к методу Main, выбрав "IL with C#". Заметьте, что операция box выполняется только после присвоение ссылочному типу значения значимого. И наоборот: unbox.any - только после попытки присвоить ссылочному типу данных значимой переменной. Это де-факто способ, которым операции упаковки и распаковки представлены в IL.   Когда стоит производить упаковку и распаковку? Код в примере выше скорее всего вам покажется наивным, и вы можете подумать: "Эй, что за вздор! Я никогда не буду такого делать". Что же, в большинстве случаев это действительно так. Но данные в нашем приложении часто упаковываются и распаковываются, когда мы об этом даже не догадываемся.   Гетерогенные коллекции К примеру, старая школа до сих пор может похвастаться ArrayList. Метод добавления элемента здесь, как можно отметить, принимает object-параметр. Таким образом, и здесь производится наша излюбленная упаковка. Впрочем, подобное кануло в лету с приходом обобщений и обобщенных коллекций.   Конкатенация строк Другой интересный пример в виде конкатенации строк. Эта операция требует наличия метода String.Concat, который принимает два object-параметра. Дабы избежать подобных ситуаций, нам достаточно просто немного изменить код, используя на переменной типа int метод ToString (и здесь стоит проигнорировать сообщение ReSharper о том, что операция бессмысленна:) ). И все! Никакой упаковки больше нет. Вообще, это далеко не единичные примеры для демонстрации. Но цель нашей статьи - донести четкое представление о том, что такое упаковка и распаковка и когда они применяются.   Производительность Как мы уже говорили, упаковка и распаковка требуют определенных затрат производительности. В случае с конкатенацией строк, выигрыш от применения ToString весьма незначителен. Именно потому, как я упомянул выше, даже ReSharper не советовал нам делать подобное: В этом случае гораздо лучше сохранить читабельность кода без ToString. Целесообразность оптимизации появляется, как правило, тогда, когда операции упаковки и распаковки предстоит производить в цикле сотни и тысячи раз. В этом случае время выполнения кода с упаковкой может составлять порядка 150 процентов от времени исполнения кода без нее (вы можете сами создать тестовое приложение и сравнить требуемый промежуток времени). Упакованные значения могут также требовать больше памяти, чем значения в стеке. Копирование значений в/из стека также требует своих затрат. Согласно MSDN, упаковка может занимать порядка 20 раз больше времени, нежели простое присвоение. В то время как распаковка примерно в 4 раза медленней простого присвоения.   Итак... зачем же тогда вообще нужно использовать упаковку и распаковку? Несмотря на все недостатки в плане падения производительности .NET -приложения, концепции упаковки и распаковки были внедрены в .NET не просто так. И вот причины: .NET-стандарт обладает общей системой типов, что позволяет представлять и ссылочные. и значимые типы схожим образом - и все это благодаря упаковке. Коллекции можно было использовать для хранения значимых типов до появления обобщений. Упрощения кода, вроде конкатенации строк и так далее. Упаковка и распаковка настолько распространены, что мы не может избежать их полностью. Мы должны знать принцип их работы, чтобы минимизировать их использование, но к этому нужно подходить разумно. Не тратьте свое время на постоянную оптимизацию кода, частую проверку через IL, чтобы убедиться, дабы ни одна лишняя операция упаковки не была использована. Помните, что чистота и простота чтения кода иногда значительно более важна, нежели незаметное, мельчайшее ускорение работы программы.   Подведем итоги В сегодняшнем уроке мы рассмотрели, что такое упаковка и распаковка, как она представлена в IL-коде, и какое влияние на производительность они имеют. Искренне надеюсь, моя статья сумела прояснить некоторые общие концепции, хотя бы чуть-чуть. :) В грядущих статьях мы рассмотрим механизм сборки мусора. Если у вас есть идеи или пожелания касательно материала новых статьей - милости просим в комментарии!   Автор перевода: Евгений Лукашук Источник
Паралакс для 2D гри без нервів та милиць

Автор: Дар'я Коновалова

Недавно в моей жизни начинающего разработчика игр появилась задача - сделать фон в игре, но не просто уныленький статичный бэкграунд, а параллакс. Да, эта чудо-красота применима не только в разработке сайтов, но и при создании игр. Попытка вдохновиться в гугле практически ничем не закончилась. Пришлось справляться с задачей собственными силами. У меня получилось. Хочу поделиться с вами опытом. Справедливо замечу, что параллакс — это не достояние веба. Еще в дремучие времена существования 8-битных игр параллакс успешно применялся для создания иллюзии объема в двухмерной игре. Коротко говоря, параллакс — это наслоение изображений, каждый слой движется со своей скоростью. Ближайший к игроку имеет самую высокую скорость, соответственно дальний (последний) — самую низкую. Ну что, вроде, минимально в теории разобрались и даже нашли, откуда ноги растут, значит, мы готовы перейти непосредственно к практике и сотворить это чудо своими руками. Что вам потребуется: базовые знания Unity3D 5 (на уровне создания скриптов, добавления компонентов); понимание С#; 3 или больше картинок в формате .png; внимательность и желание. Ладно, последнее не очень обязательно =) По ссылке вы можете скачать необходимые изображения, а также уже готовый проект. Подготовка Запускаем Unity3D, создаем новый проект, называем его, например, Parallax2D. Закидываем в папку Assets наши бэкграунды. Рекомендую сложить их в отдельную папку. В моем случае они лежат в Assets – StarSky. Каждое изображение называем удобно и понятно. Я назвала их по порядку размещения (Background – задний фон, MiddleBackground – средний, TopBackground – верхний слой). Для того, чтобы картинка перемещалась гладко, нам необходимо настроить ее в Inspector. Обратите внимание, этот этап очень важен, иначе все размажет, как звезды за иллюминатором Энтерпрайза на 3-й космической скорости. В поле Texture Type выбираем тип Texture, во Wrap Mode отмечаем Repeat. И радостно тыкаем Apply. Без этого действия изменения не сохранятся, а потом можно долго недоумевать, почему же оно не работает. Совершаем эти телодвижения и для 2-х остальных текстур. Подготовив картинки, переходим к этапу размещения их на сцене. Часто в этих ваших интерентах можно встретить совет - размещать их с помощью GameObject – Plane. Вы, конечно, можете потрудиться и заставить 3D объект нормально функционировать в 2D игре. Но, на самом деле, это все будет уныло, как последний эпизод «Звездных войн», а работать это чудовище будет чуть более быстро, чем аж никак. Поэтому я рекомендую долго не мучиться и использовать элемент UI – Canvas. Canvas меняет размер фона автоматически, подстраивает его под размеры экрана гаджета, на котором запускают игру. Это избавляет нас от потребности писать скрипт, который будет отвечать за отслеживание размеров экрана и изменения размера фона. В Hierarchy выбираем UI – Canvas. Собственно, если работать с Юнькой для вас не впервой, то вы явно знаете еще много других способов, как добавить в Hierarchy объект. Так что делайте это любым удобным способом. Создав Canvas, добавляем чайлдами («внутрь» канвы) три Panel для 3-х наших фонов. После добавления наша Hierarchy выглядит так: Переименовываем Canvas и Panel, чтобы у всех были свои пароли и явки. А теперь засучиваем рукава и беремся препарировать — подготавливать каждый компонент в Inspector. Начнем с ParallaxBackground. Изменяем Layer на Ignore Raycast, чтобы наш фон не реагировал на касания пальцами. Unity обязательно спросит, применить ли эти изменения ко всем «детям» — соглашаемся.  Далее переходим к компоненту Canvas. Находим Render Mode, выбираем Screen Space – Camera. В Render Camera добавляем нашу текущую дефолтную камеру (Main Camera(Camera)). Plane Distance ставим пока 110, но этот показатель проще отстроить во время теста. По факту — это расстояние от камеры до нашей канвы. То есть, изменяя его, мы будем получать разную глубину изображения. Остальное не трогаем и переходим к Back. В Rect Transform привязываем позицию к левому краю. Теперь наш фон будет всегда отстраиваться по одному краю, и мы избежим проблем с правильной позицией на разных устройствах. Удаляем компонент Image (Script), вместо него добавляем Raw Image (Script) (напомню, Add Component – UI – Raw Image (Scriot)). В Texture добавляем картинку нашего самого последнего слоя. Те же операции проделываем и для остальных слоев. Можно сделать немного проще, наколдовать изменения в первой Panel, дублировать (Ctrl + D), поставить в каждую свою текстуру, переименовать. Тут уже зависит от того, как вам будет удобнее. Запускаем сцену — любуемся. Три картинки прекрасно легли друг на друга. Немного черной магии Теперь весь смак. Мы с вами напишем скрипт, который заставит наши картинки двигаться. Создаем новый скрипт, напомню, пишем на C#, назовем его BackgroundHelper. Он у нас будет один, поэтому нет смысла делать отдельную папку, кидаем его прямо в основную Assets. Открываем созданный скрипт и понеслась тяжкая работа на 5 строчек: using UnityEngine; using UnityEngine.UI; // обязательно добавляем библиотеку пользовательского интерфейса, без нее кино не будет using System.Collections; public class BackgroundHelper : MonoBehaviour {     public float speed = 0; //эта публичная переменная отобразится в инспекторе, там же мы ее можем и менять. Это очень удобно, чтобы настроить скорость разным слоям картинки     float pos = 0; //переменная для позиции картинки     private RawImage image; //создаем объект нашей картинки               void Start () {         image = GetComponent<RawImage>();//в старте получаем ссылку на картинку         }                    void Update () {         //в апдейте прописываем как, с какой скоростью и куда мы будем двигать нашу картинку           pos += speed;             if (pos > 1.0F)                 pos -= 1.0F;             image.uvRect = new Rect(pos, 0, 1, 1);        }     } Сохраняем скрипт и добавляем его, как компонент к каждому слою. Скорости у меня такие: Back 0.0001 Middle 0.00015 Top 0.00024 Наслаждаемся успешной работой Если все было сделано правильно, мы получим умопомрачительный эффект, от которого просто невозможно оторвать глаз. У вас остались вопросы или возникли трудности? Пишите в комментариях.
Асинхронне програмування та співпрограми в Python

Автор: Олексій Орленко

Введение  В этом цикле статей будет рассмотрено асинхронное программирование при помощи сопрограмм в языке Python. В данной мы рассмотрим основные понятия и термины, которыми будем оперировать в дальнейшем, вкратце познакомимся с историей асинхронного программирования и состоянием дел в этой области на сегодняшний день. Также Вы узнаете о том, что такое сопрограммы и чем они могут быть полезны при написании кода в асинхронном стиле. Во второй статье будет рассмотрена реализация сопрограмм при помощи расширенных возможностей генераторов в Python (PEP 342), в третьей мы рассмотрим модуль asyncio (PEP 3156), который стал частью стандартной библиотеки в Python 3.4 и доступен в виде отдельного пакета для Python 3.3, а четвёртая статья цикла будет посвящена асинхронным функциям и сопрограммам в Python 3.5 с использованием нового синтаксиса async/await (PEP 0492). Понятие асинхронного программирования и сопрограмм Наверное, сегодня все уже слышали о Node.js и знают причины возрастания его популярности: один язык для фронтенда и бекенда (что в рамках данной статьи нас не интересует) и то, что он является платформой для построения асинхронных неблокирующих веб-серверов. Другой известной технологией, основанной на данной модели, является веб-сервер nginx, который часто используется на высоконагруженных проектах, занимая первое место по частоте использования среди 10000 самых посещаемых сайтов в мире (согласно данным W3Techs). Так что же такое асинхронное программирование и почему оно становится таким популярным, особенно в highload-проектах? На самом деле, асинхронное программирование существовало ещё на заре вычислительной техники, так как было важно максимально использовать аппаратные ресурсы машины. Но не так давно оно стало чуть ли не стандартной парадигмой программирования, настолько, что можно сказать, что большинство написанных в наши дни приложений являются асинхронными объектно-ориентированными программами. Давайте для наглядности рассмотрим это на примере графических интерфейсов пользователя. Что происходит, когда пользователь не производит никаких действий? Ничего. Программа должна ждать, пока пользователь укажет ей, что делать. Это ожидание можно реализовать в виде постоянных проверок: «а не нажал ли пользователь на кнопку?», «а не поставил ли пользователь курсор в поле ввода?». Таким образом, вычислительные ресурсы тратятся просто на то, чтобы проверить, не случилось ли что-нибудь. К счастью, практически все UI-фреймворки построены иначе. Они реализуют систему обработки событий. Любое действие пользователя – это событие, и разработчик может привязать к нему код – обработчик события. Это настолько привычный паттерн, что многие разработчики даже не задумываются, как он работает, хотя следовало бы. Например, представьте, что на экране есть три кнопки и пользователь каким-то образом нажимает на них практически одновременно. Запустятся ли три разных обработчика событий? Как правило, ответ – нет. Наиболее распространённая архитектура системы обработки событий – однопоточная, лишь с одним потоком исполнения. Однопоточные системы обработки событий практически всегда реализуются при помощи очереди событий или сообщений. Идея состоит в том, что, когда программа ничего не делает, её поток смотрит на наличие новых событий в очереди. Когда происходит событие, оно добавляется в очередь и, если поток исполнения в это время не делает ничего другого, он забирает следующее событие из очереди и выполняет соответствующий обработчик. Таким образом, события добавляются в очередь и UI-фреймворк предоставляет диспетчер, который выполняется в том же потоке, что и обработчики, вызывая их по мере надобности. В любой момент времени поток находится либо в каком-то обработчике события, либо в диспетчере, ожидая следующего события. Возникает логичный вопрос: каким же образом событие попадает в очередь, если поток исполнения занят обработкой другого события? Дело в том, что у операционной системы много потоков и тот код, который действительно взаимодействует с пользователем, выполняется отдельно от нашей программы и лишь посылает ей сообщения. Это пример асинхронной системы, так как мы не знаем, в каком порядке будет выполнятся код. Обработчики событий, с точки зрения программы, могут выполняться произвольно. Но в данной модели обработчики событий являются неделимыми действиями. И тут возникает проблема: если обработчик событий выполняется слишком долго, интерфейс как бы «подвисает». Причина в том, что пока обработчик события не вернул управление в диспетчер, следующий обработчик не будет выполнен. Решением является минимизировать количество работы, которое выполняет обработчик события. Но что если ему требуется совершить какие-то вычисления или загрузить данные с сервера? Очевидный ответ – выполнять обработчик этого события в отдельном потоке. Однако в JavaScript есть лишь один поток исполнения, а в Python, как известно, проблемой многопоточных приложений, которая значительно их замедляет, является Global Interpreter Lock (GIL). Тут мы подходим к тому, что существует два вида многозадачности: вытесняющая и кооперативная. Потоки и процессы используют вытесняющую многозадачность. Это значит, что операционная система производит квантование времени и постоянно переключается между разными потоками, сохраняя и восстанавливая их контекст выполнения. При использовании кооперативной многозадачности ветви кода, которые исполняются параллельно, сами отдают управление в определённые моменты времени. Кооперативная многозадачность как способ одновременного выполнения отдельных программ устарела и не используется в современных операционных системах, однако, идеи, заложенные в неё, оказываются очень полезными для организации выполнения асинхронного кода и позволяют при грамотном использовании максимально использовать вычислительные ресурсы в рамках одного потока (а при комбинировании этого подхода с традиционной многопоточностью, как в async/await в C#, можно строить крайне эффективные приложения). Можно построить обработчик события из множества асинхронных функций обратного вызова (callback-функций), которые управляются общим циклом событий, как это делается в Node.js, однако такой код сложно отлаживать и поддерживать. Значительно упрощают его паттерны Promise и Future, однако Python и некоторые другие языки программирования поддерживают механизм, который позволяет в данном случае обойтись без callback-функций – сопрограммы. Сопрограмма (coroutine) – это компонент программы, обобщающий понятие подпрограммы, который дополнительно поддерживает множество входных точек (а не одну, как подпрограмма) и остановку и продолжение выполнения с сохранением определённого положения. Сопрограммы в данном случае удобны тем, что позволяют писать асинхронный код в синхронном стиле. В последующих статьях мы рассмотрим механизмы их реализации в Python.
Приховування методу С#

Автор: Редакція ITVDN

Введение В данной статье Вы узнаете, как скрывать метод и как вызывать скрытые элементы базового класса. Рассмотрим это на конкретных примерах. Создадим в Visual Studio консольное дополнение с классом Student. У класса Student два поля: FirstName и LastName, и метод, который выводит FirstName и LastName на экране консоли. using System; namespace methodHidingInCSharp {        class Student {              public string FirstName;              public string LastName;              public void PrintName() {                     Console.WriteLine(FirstName + " " + LastName);              }        }        class Program {              static void Main(string[] args) {              }        } } Создадим класс DiplomaStudent и GraduateStudent, которые унаследуют класс Student. using System; namespace methodHidingInCSharp {        class Student {              public string FirstName;              public string LastName;              public void PrintName() {                     Console.WriteLine(FirstName + " " + LastName);              }        }        class DiplomaStudent : Student {        }        class GraduateStudent : Student {        }        class Program {              static void Main(string[] args) {              }        } } Если нужно создать объект класса DiplomaStudent и GraduateStudent, это можно сделать главным методом, например:  class Program {        static void Main(string[] args) {              DiplomaStudent ds = new DiplomaStudent();              ds.FirstName = "Sam";              ds.LastName = "Fisher";              ds.PrintName();              GraduateStudent gs = new GraduateStudent();              gs.FirstName = "Aiden";              gs.LastName = "Pearce";              gs.PrintName();        } } Так как классы DiplomaStudent и GraduateStudent наследуют класс Student, можно вызывать всех участников, кроме частных (приватных). При запуске проекта получим следующий результат: Результат вышел таким, как и предполагалось. Но есть небольшая проблема. Невозможно установить, какой из этих двух результатов DiplomaStudent, а какой – GraduateStudent. Для этого создадим дополнение к имени (studentType)  с указанием Diploma или Graduate. Таким образом, просто смотря на имена, можно определить, какой из них DiplomaStudent, а какой – GraduateStudent. Вернемся к проекту. Теперь стоит создать метод PrintName, который будет одинаковым для обоих производных классов, и добавить DiplomaStudent и GraduateStudent, чтоб сделать их более читабельными и понятными. class DiplomaStudent : Student {        public void PrintName() {              Console.WriteLine(FirstName + " " + LastName + " - Diploma Student");        } } class GraduateStudent : Student {        public void PrintName() {              Console.WriteLine(FirstName + " " + LastName + " - Graduate Student");        } } Как известно, тот же метод используется в базовом классе. class Student {        public string FirstName;        public string LastName;        public void PrintName() {              Console.WriteLine(FirstName + " " + LastName);        } } В результате дочерний класс скрывает реализацию базового класса. При построении кода появляется зеленая волнистая линия. Если навести курсор мыши на линию, можно увидеть объяснение: если нужно специально скрыть унаследованный член, используйте новое ключевое слово. Чтобы скрыть унаследованный от производного класса член, используйте новое ключевое слово, например: class DiplomaStudent : Student {        public new void PrintName() {              Console.WriteLine(FirstName + " " + LastName + " - Diploma Student");        } } class GraduateStudent : Student {        public new void PrintName() {              Console.WriteLine(FirstName + " " + LastName + " - Graduate Student");        } } Запустите программу: Результат оправдал ожидания. Рассмотрим, как вызвать метод назад после скрытия. Существует несколько способов. Первый – использование основных ключевых слов. class DiplomaStudent : Student {        public new void PrintName() {              base.PrintName();        } } class GraduateStudent : Student {        public new void PrintName() {              Console.WriteLine(FirstName + " " + LastName + " - Graduate Student");        } Базовый класс реализуется, когда мы выполним программу. Второй способ – превратить тип переменной дочернего класса в базовый класс. using System; namespace methodHidingInCSharp {        class DiplomaStudent : Student {              public new void PrintName() {                     Console.WriteLine(FirstName + " " + LastName + " - Diploma Student");              }        }        class GraduateStudent : Student {              public new void PrintName() {                     Console.WriteLine(FirstName + " " + LastName + " - Graduate Student");              }        } В двух предыдущих случаях реализовывается метод дочернего класса и используется новое ключевое слово, чтобы скрыть реализацию базового метода. То есть, когда Вы запускаете приложение, оно печатает реализацию дочернего класса.  class Program {        static void Main(string[] args) {              DiplomaStudent ds = new DiplomaStudent();              ds.FirstName = "Sam";              ds.LastName = "Fisher";              re I have type - casted the child class reference variable into a base class                     ((Student)ds).PrintName();              GraduateStudent gs = new GraduateStudent();              gs.FirstName = "Aiden";              gs.LastName = "Pearce";              ((Student)gs).PrintName();        } } Запустите программу.  В предыдущем результате Вы получили базовую реализацию. Третий способ состоит в создании переменной базового класса, которая указывает на дочерний класс. class Program {        static void Main(string[] args) {              DiplomaStudent ds = new DiplomaStudent();              ds.FirstName = "Sam";              ds.LastName = "Fisher";              re I have type - casted the child class reference variable into a base class                     ((Student)ds).PrintName();              GraduateStudent gs = new GraduateStudent();              gs.FirstName = "Aiden";              gs.LastName = "Pearce";              ((Student)gs).PrintName();        } } Результат тот же. Источник: http://www.c-sharpcorner.com/UploadFile/219d4d/method-hiding-in-C-Sharp/
Створення Web API в MVC6

Автор: Редакція ITVDN

Введение ASP.Net Web API – это основа создания HTTP услуг широкого спектра клиентов, таких как браузеры, мобильные телефоны, планшеты и так далее. API должна быть совместима с современными браузерами, чтобы использовать эти услуги в простой форме. Мы можем быстро и просто сбрасывать служебные данные в браузер, а также приложения. Необходимость в Web API Если Вы нуждаетесь в Web Service и Вам не нужно SOAP, то API ASP.Net –лучший выбор. Он строит простые HTTP сервисы, основанные на базе существующей WCF. ASP.Net Web API на основе HTTP легко определяются. У них открытый исходный код. Легкая архитектура подходит для устройств с ограниченной шириной полосы, например, смартфонов. Создание простой Web API в ASP. NET MVC 6 Запустите Visual Studio 2015 Preview. В меню Файл выберите New > Project. В диалоговом окне New Project нажмите Tempates > Visual C# > Web и выберите ASP. NET шаблон проекта Web-приложений. Назовите проект "WebApplication1" и нажмите OK. В диалоговом окне New ASP.NET Project выберите "ASP.NET 5.0 Empty” шаблон. Проект включает в себя следующие файлы:   Global.json содержит настройки решения. В project.json находятся настройки проекта. Project_Readme.html – read me файл. Startup.cs содержит встроенный код конфигурации. Откройте файл Project.json. Добавьте библиотеки классов (class libraries) в разделе зависимостей (dependencies). ​ "dependencies": {           "Microsoft.AspNet.Server.IIS": "1.0.0-beta1",           " "Microfost.AspNet.Diagnostics": "1.0.0-beta1" } Затем откройте Startup.cs с кодом, показанным ниже.  public class Startup    {         public void Configure(IApplicationBuilder app)         {             // For more information on how to configure your application, visit http://go.microsoft.com/fwlink/?LinkID=398940              app.UseWelcomePage();             // app.UseMvc();          }     }  После отладки Visual Studio перейдите на http://localhost:port/ в браузере. Создание Web API Мы создадим Web API, чтобы упорядочить список клиентских продуктов. Сначала нужно добавить ASP.Net MVC6 в приложение. Добавьте пакет MVC6 в список зависимостей в Project.json. Используйте код ниже. "dependencies": {         "Microsoft.AspNet.Server.IIS": "1.0.0-beta1",         "Microsoft.AspNet.Diagnostics": "1.0.0-beta1",         "Microsoft.AspNet.Mvc": "6.0.0-beta1"       } Затем добавьте MVC в request pipeline в Startup.cs. Добавьте Using для Microsoft.Framework.DependencyInjection.   Добавьте следующий метод в Startup класс. using System; using Microsoft.AspNet.Builder; using Microsoft.AspNet.Http; using Microsoft.Framework.DependencyInjection;//add new  namespace WebApplication1 {     public class Startup     {         public void Configure(IApplicationBuilder app)         {                      app.UseWelcomePage();              app.UseMvc();         }         public void ConfigureServices(IServiceCollection services)         {             services.AddMvc();         }     } } Добавьте модель using System; using System.ComponentModel.DataAnnotations; namespace WebApplication1.Model {     public class Customer     {         public int CustomerId { get; set; }         [Required]         public string Name { get; set; }     } } Добавьте контроллер  using Microsoft.AspNet.Mvc; using System.Collections.Generic; using System.Linq; using WebApplication1.Model; namespace WebApplication1.Controllers {     public class HomeController : Controller     {                 static readonly new List<Customer> _items = new List<Customer>()             {                 new Customer  { CustomerId = 1, Name = "Henry" },                 new Customer { CustomerId = 2, Name = "John" },             };         public IEnumerable<Customer> Get()         {             return _items;         }         public IActionResult GetById(int id)         {             var its = _items.FirstOrDefault(x => x.CustomerId == id);             if (its == null)             {                 return HttpNotFound();             }             return new ObjectResult(its);         }         public void CreateCustomer([FromBody] Customer item)         {             if (!ModelState.IsValid)             {                 Context.Response.StatusCode = 400;             }             else             {                 item.CustomerId = 1 + _items.Max(x => (int?)x.CustomerId) ?? 0;                 _items.Add(item);                 string url = Url.RouteUrl("GetByIdRoute", new { id = item.CustomerId },                     Request.Scheme, Request.Host.ToUriComponent());                 Context.Response.StatusCode = 201;                 Context.Response.Headers["Location"] = url;             }         }         public IActionResult DeleteItem(int id)         {             var item = _items.FirstOrDefault(x => x.CustomerId == id);             if (item == null)             {                 return HttpNotFound();             }             _items.Remove(item);             return new HttpStatusCodeResult(204);         }     } } Выше описывается класс HomeController. Маршрутизация Атрибут маршрутизации определяет URL шаблоны контроллера. [Route("api/[controller]")] Методы HTTP [HttpGet], [HttpPost] и [HttpDelete] – атрибуты, определяющие методы HTTP для контроллера. public IEnumerable<Сustomer> Get() { }  //[HttpGet]  public IActionResult GetById(int id) { } //[HttpGetbyid}  public void СreateСustomer([FromBody] Сustomer item) { } // [HttpPost]  public IActionResult DeleteItem(int id) { } //[HttpDelete] {Customerid: int} int ограничивает переменную до соответствия целому числу, чтобы URL-адреса совпадали. http://localhost/api/home/1 http://localhost/api/home/42 Из этой статьи Вы узнали, как создавать Web API в MVC 6, используя модели, контроллер и HTTP методы. Источник: http://www.c-sharpcorner.com/UploadFile/85ed7a/create-web-api-in-mvc-6/
Читабельність програмного коду

Автор: Редакція ITVDN

Введение Вам приходилось возвращаться к фрагменту кода, написанному год или месяц назад? Каково это было?  Это было просто, или приходилось вникать в код исходя из заметок? Если у Вас не получается быстро разобраться в коде и Вы задаете себе вопрос: «Что же я тогда имел ввиду?» – Вы определенно делаете что-то не так. Скорее всего, Ваш код работает правильно. И Вы знали его вдоль и поперек, когда над ним работали. Почему же Вы не можете его вспомнить сейчас? Быть может, он не был написан достаточно доступно и не соответствовал стандартам машинного кода? В этой статье Вы найдете полезную информацию о том, как научиться писать доступный не только для себя, но и для остальных разработчиков код. Пример использования стандарта оформления кода Рассмотрим это на следующем примере метода в C#: public string Transform(List<DateTime> s)         {             string d = null;             foreach (DateTime kc in s)             {                 if (kc > DateTime.Now)                 {                     d = d + kc + "\n";                 }                 else                 {                     d = d + "Delayed\n";                 }             }             return d;         } Скорее всего, Вы не можете понять, какую функцию он выполняет и как его можно использовать. Но после короткой реконструкции кода мы получим:  public string GetText(List<DateTime> arrivalTimes)         {             var stringBuilder = new StringBuilder();             foreach (DateTime arrivalTime in arrivalTimes)             {                 if (arrivalTime > DateTime.Now)                 {                     stringBuilder.AppendLine(arrivalTime.ToString());                 }                 else                 {                     stringBuilder.AppendLine("Delayed");                 }             }             return stringBuilder.ToString();         } Или если мы обратимся к оператору “?:”, мы получим:  public string GetText(List<DateTime> arrivalTimes)         {             var stringBuilder = new StringBuilder();             foreach (DateTime arrivalTime in arrivalTimes)             {                 string message = arrivalTime > DateTime.Now ? arrivalTime.ToString() : "Delayed";                 stringBuilder.AppendLine(message);             }             return stringBuilder.ToString();         } Что произошло с кодом? Некоторые изменения сделали наш код более читабельным: 1. Имя метода получило более понятное название. 2. Мы изменили названия переменных: 1. “kc” изменена на arrivalTime, 2. “s” изменена на arrivalTimes, 3. “d” изменена на stringBuilder, Это облегчит понимание того, за что отвечает каждая переменная и как она используется. 3. Скобки были стандартизированы. 4. Добавлены вкладки для читаемости, размещения и разметки в скобках. 5. Оператор “?:” был введен для того, чтобы сократить наш код до четырех строчек. 6. Добавлен класс StringBuilder, чтобы избежать конкатенации (“string” + “string”). Кто-то может возразить, что создание экземпляра StringBuilder будет замедлять метод из-за его распределения памяти. Но стоит помнить, что конкатенация струн создает много мусора в памяти, который вынужден чистить Garbage Collector. Считается, что ~ 5 непрерывных строк равны созданию реализации StringBuilder, так что если список состоит из пяти или более элементов, то производительность этого метода будет увеличена. А для больших коллекций этот метод будет работать в несколько раз быстрее. Правильный способ составления кода Давайте перейдем к другому примеру. Рассмотрим класс пользователя: public class User         {             public bool HasConfirmedEmail { get; set; }             public bool HasActiveAccount { get; set; }             public bool IsLoggedIn { get; set; }             public bool HasPremiumAccount { get; set; }             public bool StatusAutoGenerated { get; set; }             public DateTime LastStatusChange { get; set; }             public bool SetStatus(string status)             {                 // write to Data Base                 return true;             }         }  Метод, отвечающий за обновление статуса пользователя, который должен проверить, все ли свойства в правильном состоянии: public string UpdateStatus(string status, User user)         {             if (user.HasActiveAccount)             {                 if (user.HasConfirmedEmail)                 {                     if (user.IsLoggedIn)                     {                         if (user.HasPremiumAccount)                         {                             if (!user.StatusAutoGenerated)                             {                                 if (user.LastStatusChange < DateTime.Now.AddDays(-1))                                 {                                     if (user.SetStatus(status))                                     {                                         return "Updated";                                     }                                 }                             }                         }                     }                 }             }             return "Fail";         } Хотя этот код более понятный, чем первый, представленный в этой статье, он все еще не соответствует стандартам программного кода. Вот пример того, как этот код можно сделать немного лучше: const string OK = "Updated";         const string FAIL = "Fail";         public string UpdateStatus(string status, User user)         {             if (!CanUpdateStatus(user)) return FAIL;             if (!user.SetStatus(status)) return FAIL;             return OK;         }         public static bool CanUpdateStatus(User user)         {             if (!user.IsLoggedIn) return false;             if (!user.HasActiveAccount) return false;             if (!user.HasConfirmedEmail) return false;             if (!user.HasPremiumAccount) return false;             if (user.StatusAutoGenerated) return false;             if (!(user.LastStatusChange < DateTime.Now.AddDays(-1))) return false;             return true;         } Основные изменения, внесенные в этот код, чтобы улучшить его читабельность: Static method был создан, чтобы проверить возможность обновления статуса пользователя. Это делает метод UpdateStatus более понятным с первого взгляда. Кроме того, логика метода CanUpdateStatus может быть повторно использована в других частях системы, если они являются public и static. Все " if " были заменены, чтобы уменьшить вложения. Количество скобок значительно уменьшилось, и код гораздо легче читать и компилировать. Еще одним преимуществом данного условия является его масштабируемость. Представьте себе, что класс User имеет теперь еще три свойства, которые должны быть проверены перед обновлением статуса – будут необходимы еще три "if" выписки. Теперь Вы можете добавить только три (не более) линии в методе CanUpdatedStatus. Строки, которые представляют сообщения, были удалены из тела методов и были введены в постоянные переменные. Это помогает поддерживать код, потому что независимо от числа использований кода есть только одно место, где Вы должны изменить содержание сообщения, если это необходимо. Но, на самом деле, все тексты должны быть помещены в проект внешних ресурсов. Источник: http://blog.goyello.com/2014/12/11/clean-code/ 
Flappy Bird за 30 хвилин

Автор: Олег Загородній

Введение Не так давно была популярной такая мобильная игра, как Flappy Bird.  Причем по разным причинам из магазинов она пропала также неожиданно, как и появилась. Однако, учитывая ее популярность, сразу образовалось множество клонов. Не столь качественных, конечно. Но почему их было много? Все из-за того, что сама игра делается довольно просто и быстро. И в этой небольшой статье мы, разумеется, рассмотрим, как же сделать такую игрушку, как Flappy Bird. Все ресурсы (спрайты, шрифты) принадлежат непосредственно их авторам. Ресурсы Все, что нам понадобится из ресурсов игры – это несколько спрайтов и шрифт, как в оригинале. Спрайты найдены на просторах интернета. Шрифт был скачан по ссылке: http://www.dafont.com/04b-19.font?text=Flappy+Number Подготовка игровых объектов Сначала сделаем префаб игрока, то есть птичку. Для этого создаем на сцене пустой объект с именем Bird. Внутрь него помещаем объект-спрайт с именем Body и в свойство Sprite компонента Sprite Renderer помещаем спрайт нашей птички (из папки Sprites). Теперь на саму птичку (объект Bird) прикрепляем компонент Circle Collider 2D и задаем его радиусу значение 0.45. Также необходимо прикрепить компонент Rigidbody 2D. Здесь, пожалуйста, не перепутайте. Нам необходим именно 2D компонент, а не обычный Rigidbody. Ему задаем значение гравитации (Gravity Scale) равным 2.45 и запрещаем передвижение по оси X, чтобы наша птичка неожиданно не улетела куда-то в сторону. Теперь создаем C# скрипт c именем BirdHelper и тоже прикрепляем его к птичке (объект Bird). После всего этого перетягиваем объект Bird из окна Hierarchy в окошко Project, создав таким путем префаб птички. То есть в итоге на префабе Bird должно быть четыре компонента: Transform, Circle Collider 2D, Rigidbody 2D и скрипт Bird Helper. С главным героем пока что покончили. Приступим теперь к единственным препятствиям в игре – трубам. Мы с Вами поступим очень хитро. Так как в процессе игры каждая преграда – это пара труб (одна сверху, другая – снизу), их длину можно было бы регулировать и кодом в момент создания. Ведь если нижняя труба короткая, то верхняя – длинная. Но мы пойдем более простым путем. Наша преграда будет сразу состоять из двух длинных труб, и мы просто-напросто будем их ставить выше или ниже. Как на картинке ниже, где светлая рамка – границы дисплея. Что ж, дабы заделать префаб преграды, создаем на сцене пустой объект с именем Pipes и помещаем внутрь него два объекта-спрайта с именами TopPipe и BottomPipe. В Каждому из них в свойство Sprite компонента Sprite Renderer перетаскиваем спрайт Pipe (из папки Sprites). Объекту BottomPipe ставим положение по оси Y -4.5 (отрицательное). С объектом TopPipe проделываем аналогичные манипуляции, но позиция по оси Y будет 4.5, и еще необходимо повернуть его на 180 градусов вокруг оси Z. Почти готово. Осталось только настроить коллайдеры и прикрепить скрипт. Начнем с коллайдеров. Прикрепим  на объект Pipes компонент Box Collider 2D. А лучше сразу три. Первый настроим таким образом, как на картинке. Просто немного подкорректируем размер и зададим позиции по оси X значение -4.5. Как вы, думаю, уже догадались - это будет коллайдер для нижней трубы. Следующий Box Collider 2D настроим аналогично предыдущему, только позиция по оси X будет 4.5. Теперь последний коллайдер. Он, на самом деле, будет триггером, и с помощью него  мы сможем отследить, когда же игрок преодолел текущее препятствие. Вот такие настройки должны быть у этого коллайдера (уже триггера). И под конец создаем скрипт с именем PipesHelper и прикрепляем его на объект Pipes. Теперь перетягиваем объект Pipes из окна Hierarchy в окно Project, создав таким путем префаб для препятствий. Остался только фон. Создаем пустой объект с именем Background. Помещаем в него два объекта-спрайта с именами Part1 и Part2 и в свойство Sprite компонента Sprite Renderer помещаем спрайт Background. Объекту Part1 задаем размеры X: 2.6, Y: 2.6. С объектом Part2 выполняем такие же действия, но еще сдвигаем его вправо на 7.2 юнитов по оси X. После этого в объект Background помещаем еще два объекта-спрайта с именами Ground1 и Ground2. Им назначаем спрайты Ground из папки Sprites. Вот так должны выглядеть настройки объектов Ground1 и Ground2. Напоследок необходимо добавить коллайдер для земли и создать анимацию движения фона. Прикрепляем компонент Box Collider 2D на объект Background. Для того, чтобы создать анимацию, выделяем Background в окне Hierarchy и в окошке Animation нажимаем кнопку Create. Назовем ее BackgroundFloating. Вся задача данной анимации – передвижение фона влево, чтобы создать эффект, как будто игрок на самом деле летит вправо. После того, как анимация будет создана, на объект Background автоматически прикрепится компонент Animator и будет создан Animator Controller. Нам осталось только перейти в окно Animator и установить значение скорости анимации 0.2. Под конец создаем скрипт GameHelper и цепляем его на игровую камеру. На этом, пожалуй, все игровые приготовления завершены. Непосредственно сам процесс разработки Начнем, я так думаю, из скрипта главного персонажа. То есть BirdHelper’a. Реализация полета птички, как в оригинале, довольно проста. Под действием силы гравитации она будет постоянно падать, а при нажатии клавиши, допустим, Space, мы применим к ней силу по направлению вверх, используя метод AddForce на компоненте Rigidbody2D. using UnityEngine; public class BirdHelper : MonoBehaviour {     public float force;     private new Rigidbody2D rigidbody;     void Awake()     {         rigidbody = GetComponent<Rigidbody2D>();     }     void Update()     {         if (Input.GetKeyDown(KeyCode.Space))             rigidbody.AddForce(Vector2.up * (force - rigidbody.velocity.y), ForceMode2D.Impulse);         rigidbody.MoveRotation(rigidbody.velocity.y * 2.0F);     } } С помощью метода MoveRotation мы совершаем поворот птицы в зависимости он величины и знака значения текущего ее ускорения. Поле force у нас открытое и, разумеется, отобразится в окне Inspector. Напишем там 8. Это будет сила “прыжка”. Со скриптом главного героя почти всё. Вернемся к нему немножко позже. Перейдем к трубам. Их задача - двигаться на игрока, ведь сам по себе он лишь летает по вертикальной оси в процессе игры и так и не сдвинется по горизонтальной. Движение препятствий можно реализовать статическим методом MoveTowards структуры Vector3. using UnityEngine; public class PipesHelper : MonoBehaviour {     [SerializeField]     private float speed;            void Start()     {         Vector2 position = transform.position;         position.y = Random.Range(-1.5F, 2.5F);         transform.position = position;         Destroy(gameObject, 6.0F);     }        void Update()        {         transform.position = Vector2.MoveTowards(transform.position, transform.position - transform.right, speed * Time.deltaTime);        } } При появлении препятствие будет выбирать случайную позицию по оси Y, но такую, чтобы не было видно конца верхней или нижней трубы. Затем каждый кадр будет двигаться влево. Для этого, задавая конечную точку движения, мы вычитаем из текущей позиции препятствия вектор, направленный вправо от него. Также через 6 секунд объект Pipes будет уничтожен, чтобы не нагружать устройство, так как он свою задачу уже выполнил. К полю speed, представляющему скорость движения препятствия, мы применили атрибут SerializeField, чтобы оно было отображено в Inspector’e, ведь мы его закрыли. Перед тем, как перейти к скрипту GameHelper, добавим на игровую сцену объект-текст с именем ScoreText и настраиваем, как на рисунке ниже. Это будет текст для отображения количества очков игрока. Еще давайте добавим объект-кнопку с именем RestartButton. Она будет появляться, когда игрок проиграет, то есть при столкновении с каким-либо препятствием. В свойство SourceImage компонента Image нашей кнопки перетащите спрайт Button из папки Sprites. Вот настройки кнопки. А вот настройки текста внутри кнопки RestartButton. Непосредственно объект кнопки нужно деактивировать. Иерархия объектов на сцене будет выглядеть следующим образом. Какая же задача скрипта GameHelper? Он будет отвечать за генерацию новых препятствий, подсчет очков и их отображение. А еще за перезапуск уровня. Давайте глянем код. using System.Collections; using UnityEngine; using UnityEngine.UI; public class GameHelper : MonoBehaviour {     [SerializeField]     private Text scoreText;     private GameObject pipes;     public Button restartButton;     [HideInInspector]     public int score;     void Awake()     {         pipes = Resources.Load<GameObject>("Pipes");     }     void Start()     {         StartCoroutine(GeneratePipes());     }     void Update()     {         scoreText.text = "Score: " + score;     }     IEnumerator GeneratePipes()     {         Vector2 position;         while(true)         {             position = transform.position;             position.x += 6.0F;             Instantiate(pipes, position, Quaternion.identity);             yield return new WaitForSeconds(2.0F);         }              }     public void Restart()     { # if UNITY_5_2         Application.LoadLevel(Application.loadedLevel); #endif         Time.timeScale = 1.0F;     } } В первую очередь не забудьте подключить пространства имен UnityEngine.UI для работы с элементами пользовательского интерфейса и System.Collections, ведь там находится необходимый нам интерфейс IEnumerator, который мы будем использовать для карутины. Метод Reset отвечает за перезапуск уровня при нажатии соответствующей кнопки. Значит, настройки компонента Button объекта ResetButton необходимо немножко подправить. Так как сцена у нас не тяжелая, то мы можем позволить себе просто перезагружать ее полностью. Но, так как свойство timeScale статическое, его значение стоит снова установить в 1.0, чтобы при перезагрузке сцены время шло своим чередом. Метод GeneratePipes является карутиной и его задача – бесконечным циклом создавать препятствия с задержкой между каждыми в 2.0 секунды. Запускается карутина методом StartCaroutine при старте игры. А в методе Awake подгружается из папки Resources модель препятствия. В поля scoreText и restartButton, которые будут отображены в окне Inspector, необходимо перетащить соответствующие объекты текста очков и кнопки рестарта игры. Каждый кадр в свойство text объекта scoreText будет передавать текущее количество очков, которое хранится в поле score. Почти закончили. Осталось немного дополнить скрипт BirdHelper. using UnityEngine; public class BirdHelper : MonoBehaviour {     public float force;     private new Rigidbody2D rigidbody;     private GameHelper;     void Awake()     {         rigidbody = GetComponent<Rigidbody2D>();         gameHelper = Camera.main.GetComponent<GameHelper>();     }     void Update()     {         if (Input.GetKeyDown(KeyCode.Space))             rigidbody.AddForce(Vector2.up * (force - rigidbody.velocity.y), ForceMode2D.Impulse);         rigidbody.MoveRotation(rigidbody.velocity.y * 2.0F);     }     void OnCollisionEnter2D (Collision2D collision)     {         gameHelper.restartButton.gameObject.SetActive(true);         Time.timeScale = 0.0F;     }     void OnTriggerExit2D (Collider2D other)     {         gameHelper.score++;     } } Метод OnTriggerExit2D сработает в момент выхода игрока из триггера, который находится внутри препятствия и добавит нам одно очко. OnCollisionEnter2D будет вызван при столкновении с любым коллайдером, а это значит, что игрок проиграл. Время остановится и активируется кнопка перезапуска игры. На этом, пожалуй, всё. Скачивайте проект, изучайте, дополняйте, переделывайте либо просто удаляйте. Спасибо большое всем за внимание. Удачи в начинаниях и творческих успехов!
Що таке адаптивна верстка і навіщо вона потрібна

Автор: Редакція ITVDN

Что такое адаптивная верстка? Преимущества мобильного адаптивного дизайна Почему адаптивный дизайн важен для бизнеса Как создать адаптивный дизайн Плавающая Сетка (Fluid Grid) Гибкий текст и изображения Медиа-запросы Пользовательское тестирование адаптивных сайтов Браузерное и устройство-зависимое тестирование на адаптивный дизайн Вдохновение от других адаптивных сайтов Будущее адаптивного дизайна для мобильных устройств Является ли ваш сайт Mobile-Friendly? Подводя итоги От редакции ITVDN Еще в 2015 году Google внедрил изменения в алгоритмы своей поисковой системы, которые теперь учитывают адаптированность сайта под мобильные устройства как важный пункт при ранжировании сайта. Дата была удачно названа Мобилгеддон (Mobilegeddon), как сравнение с Армагеддоном. Одно только такое введение требований от поисковиков к наличию мобильной версии сайта может оправдать важность адаптивного дизайна. Проще говоря, веб-сайт должен быть удобным для просмотра на смартфоне. Требования к адаптивной верстке включают в себя такие элементы дизайна, как: читаемый текст без необходимости его увеличения; достаточное количество места для целей касания (tap targets); отсутствие горизонтальной прокрутки. На сегодня уже почти 4 миллиарда пользователей используют смартфоны для серфинга в Интернете. Веб-сайты, не оптимизированные для всех небольших экранов смартфонов, имеют рейтинг в поисковых системах ниже тех, что выполнены адаптивно. Более 50% поисковых запросов в Интернете теперь происходит с мобильного устройства. Чтобы ваш веб-сайт мог работать с карманными устройствами (не создавая отдельное приложение), вам для начала стоит признать – адаптивная вёрстка важна для пользователей смартфонов. Давайте рассмотрим более детально, почему и как это происходит. Прежде всего ... а что такое адаптивная верстка и мобильный дизайн, и почему вы вообще должны о них заботиться?   Что такое адаптивная верстка? Адаптивный веб-дизайн (RWD - Responsive web design) создает систему, позволяющую одному сайту (с одним URL-адресом и одним источником контента) реагировать и адаптироваться к размерам устройства пользователя. Адаптивный веб-сайт создан с использованием верстки с гибким макетом, который подстраивается под размеры экрана устройства. По-сути, благодаря адаптивной верстке, ваш веб-сайт будет отлично выглядеть и хорошо работать как на настольном компьютере (или ноутбуке), так и на планшете, и в браузере мобильного телефона. В прошлом разработчики создавали более одного сайта, для соответствия страниц экранам разных размеров. С учетом того, что на рынке сегодня представлено много типов устройств, это кажется совершенно неэффективным… верно? Сам термин «адаптивная верстка» был фактически придуман в 2010 году веб-дизайнером Итаном Маркоттом. Сегодня адаптивная верстка в веб-дизайне уже не новая тенденция, а скорее проверенный временем способ мышления, стоящий за созданием сайтов. На сегодня наличие адаптивного веб-сайта больше не является еще одной возможностью для развития вашей инфраструктуры — это уже необходимость! Преимущества мобильного адаптивного дизайна Преимущество адаптивного макета номер один— это получение гарантии того, что любой пользователь на любом устройстве будет иметь наилучшие возможности взаимодействия с контентом на вашем сайте. Адаптивная верстка веб-сайта также является отличным способом улучшить контент на вашем сайте. Вы сможете убедиться, что те, кто использует мобильное устройство, видят всю важную информацию. Благодаря особенностям алгоритма Google, адаптивный веб-дизайн повышает видимость сайта в поисковых системах, поскольку он удобен для просмотра на мобильных устройствах. Сайт с качественным представлением контента на мобильном устройстве будет находиться в результатах поиска выше, чем сайт, хорошо отображающий контент только на десктопах.   Почему адаптивный дизайн важен для бизнеса Расширяется охват клиентов благодаря захвату пользователей небольших устройств (планшетов и смартфонов); Постоянный опыт работы с широкой аудиторией, который может увеличить количество потенциальных клиентов, продажи и конверсии; Аналитика, отслеживание и отчетность по версиям сайтов для десктопов и мобильных устройств могут быть в одном месте; Затраты времени и стоимость управления контентом снижается; Более 60 % запросов в Google на конец первого квартала 2019 делаются с мобильных устройств. Обратите внимание, что есть еще два способа, с помощью которых можно обеспечить взаимодействие пользователя с сайтом через мобильные приложения. Первый называется динамическим показом (Dynamic Serving), в котором используется один и тот же URL-адрес, но разные коды HTML и CSS. Страницы распознают устройство, на котором они просматриваются, и предоставляют соответствующий код. Второй способ — это вообще отдельный мобильный сайт. Когда пользователи посещают сайт с мобильного устройства, они отправляются на другой - мобильный URL-адрес. Вам следует выяснить, какой вариант лучше всего подходит для вашего присутствия в Интернете, прежде чем остановиться на одном. Учтите, что на Google ежедневно приходится более 5,6 миллиардов поисковых запросов. Рекомендуемая Google конфигурация для сайтов, оптимизированных для смартфонов, - это сайты с адаптивным веб-дизайном. Google даже предлагает тест на адаптивность сайта под мобильные устройства, чтобы вы могли увидеть, насколько легко посетитель может использовать вашу страницу на мобильном устройстве. Вы просто вводите URL страницы и получаете оценку. Как создать адаптивный дизайн Как сделать адаптивную верстку? Есть несколько моментов, о которых стоит подумать при создании адаптивного макета. Это процесс, который требует определенной системы проектирования и иерархии контента среди различных устройств. Три основных компонента адаптивного веб-дизайна включают в себя: Гибкая (плавающая) сетка - fluid grid; Гибкий текст и изображения; Медиа-запросы. Рассмотрим каждый из этих элементов детальнее.   Плавающая Сетка (Fluid Grid) Сетка является ключевым элементом для создания адаптивного макета. На сегодня сетки уже не являются чем-то новым. Веб-дизайнеры использовали сетки для создания веб-сайтов с самого начала. Однако в прошлом эти сетки имели фиксированную ширину и не позволяли поддерживать плавную компоновку. Гибкая сетка, используемая для адаптивных веб-сайтов, обеспечит вам гибкость и масштабируемость дизайна. Элементы будут иметь постоянный интервал, пропорции и смогут настраиваться на определенную ширину экрана в процентах. Гибкий текст и изображения Способ отображения текста зависит от того, на каком устройстве пользователь просматривает ваш сайт. Однако текст должен оставаться читаемым, несмотря ни на что. На адаптивных веб-сайтах есть возможность увеличить размер шрифта и высоту строки (расстояние между каждой строкой текста) для удобочитаемости. Гибкий текст и изображения настраиваются в пределах ширины макета, в соответствии с иерархией содержимого, заданной с помощью CSS (таблицы стилей). Текст на сайте с адаптивной версткой теперь может быть разборчивым независимо от устройства конечного пользователя. Благодаря гибкому контейнеру (внутри сетки) текст может переноситься с увеличением размера шрифта на небольших устройствах. Гибкие изображения могут оказаться более сложными из-за времени загрузки в небольших браузерах устройств. Но эти изображения могут масштабироваться, обрезаться или исчезать в зависимости от того, какой контент необходим для мобильных устройств. Медиа-запросы Медиа-запросы - это код, который обеспечивает гибкость макета на адаптивных веб-сайтах. Медиа-запросы определяют код CSS, который будет применен соответственно, в зависимости от размеров и ориентации устройства (например, книжная ориентация iPhone или альбомная ориентация iPad и т. д.). Медиа-запросы допускают существование несколько макетов дизайна, которые будут использовать одну и ту же HTML-кодированную веб-страницу. Все три указанных элемента являются основой адаптивной верстки. Однако есть и другие важные средства, которые могут помочь определить акценты и усовершенствовать адаптивный веб-дизайн ваших сайтов для мобильных устройств. Обратите внимание на: Пользовательское тестирование адаптивных сайтов Информация о том, как пользователи взаимодействуют с вашим сайтом, - бесценна и точно стоит того, чтобы заплатить за ее получение. Существует множество способов провести пользовательское тестирование, чтобы получить максимально полезную обратную связь. Такие сайты, как UserTesting.com, предоставляют пользователям тестирование за небольшую плату или бесплатно. Различные методы, такие как тестирование in-the-wild и карточная сортировка (Card Sorting), также могут помочь обнаружить неожиданные болевые точки и слабые места в использовании вашего продукта.   Браузерное и устройство-зависимое тестирование на адаптивный дизайн Убедитесь, что ваш адаптивный дизайн и верстка совместимы со всеми соответствующими браузерами и сохраняет целостность вашего пользовательского опыта и дизайна. Не полагайтесь только на изменение размеров окна браузера при тестировании адаптивного веб-дизайна для мобильных устройств. Попробуйте просмотреть сайт на как можно большем количестве физических устройств. Вы будете удивлены тем, что можно обнаружить при переходе от одной операционной системы к другой, от одного устройства – к другому.   Вдохновение от других адаптивных сайтов Точно также, как и выполняя любой другой дизайн-проект, обратитесь к опыту других людей. Найдите другие адаптивные веб-сайты, которые творчески обыгрывают концепцию адаптивного веб-дизайн. Задумайтесь над следующими вопросами: Какие веб-сайты или приложения вы часто используете на своем мобильном телефоне или других портативных устройствах? Почему вы предпочитаете один сайт другим, которые могут предоставлять аналогичные услуги? Предпочитаете ли вы использовать их на смартфоне или на настольном компьютере? Поиск ответов на эти вопросы может помочь вам найти слабые места, которые вы, возможно, никогда не замечали, во время ежедневного использования своего вебсайта. Будущее адаптивного дизайна для мобильных устройств Мы знаем, что Google требует следующих оптимизированных элементов для эффективного взаимодействия с пользователями мобильных интерфейсов, используя адаптивный веб-дизайн: Текст, с читаемым размером без необходимости его принудительного увеличения; Контент, который умещается на экране устройства, без необходимости горизонтальной прокрутки; Ссылки и кнопки, расположенные на достаточном расстоянии друг от друга, чтобы не было затруднений в работе с интерфейсом; Разумное время загрузки страниц; Не используйте Flash! Эти правила адаптивной верстки очень важно соблюдать. Рост числа мобильных устройств — это только начало перехода к более удобному использованию Интернета. Нужно быть уверенными, что пользователи могут просматривать ваш сайт в любом месте на любом устройстве, самые разнообразные мобильные носимые устройства становятся все более популярными. Важным моментом будет учитывать современные размеры гаджетов, чтобы понимать в вашей адаптивной верстке какие разрешения учитывать. К примеру, на 2019 год все еще лидирующим остается разрешение экранов - 360х640. Является ли ваш сайт Mobile-Friendly? Пройдите тест Google, чтобы узнать, насколько адаптирован ваш сайт для мобильных устройств. Ваш сайт получил зеленый свет? Отлично, значит вы прошли тест Google на адаптивность. Вы уже прочитали выше причины, почему адаптивный дизайн важен для пользователей вашего сайта. Вместо зеленого видите большие красные иксы? Значит вам следует начать предпринимать определенные шаги для оптимизации работы вашего сайта под пользователей мобильных устройств. Помните, что изменения в алгоритмах Google и требованиях к адаптивному дизайну в настоящее время мало учитывают планшеты, но, если вы сделаете ваш сайт полностью адаптивным - вы будете впереди тех конкурентов, которые этого еще не сделали!   Подводя итоги Согласно требованиям сегодняшнего времени, ваш веб-сайт должен отлично выглядеть и хорошо работать как на настольном компьютере, так и на планшете, и в браузере смартфона. Адаптивный веб-дизайн может помочь вам достичь этого. В этой статье мы ответили на общий вопрос «что такое адаптивный дизайн?». Есть три компонента адаптивного веб-дизайна: плавающая сетка, гибкий текст и изображения и медиазапросы. Помните о важности адаптивного веб-дизайна для вашего бизнеса.  Выполнение вашего сайта по всем правилам адаптивной верстки поможет вам: Увеличить охват потребителей на всех устройствах; Поддерживать постоянный качественный пользовательский опыт, который увеличивает удержание аудитории на сайте; Консолидировать данные аналитики, отслеживания и отчетности; Сократить время и стоимость управления контентом; Конкурировать в своей отрасли с другими брендами. Google привлекает к коммерческим сайтам более 85% трафика мобильного поиска и рекомендует применять на ваших сайтах адаптивный дизайн. Поскольку адаптивный веб-дизайн удобен для мобильных устройств, он помогает улучшить видимость в поисковых системах, что, в свою очередь, может увеличить количество посетителей вашего сайта. Увеличение трафика приводит к лучшей генерации потенциальных клиентов, дополнительным конверсиям и увеличению продаж - три основные причины, по которым вам нужен адаптивный веб-дизайн!   От редакции ITVDN Мы с вами рассмотрели необходимость адаптивной верстки для каждого современного сайта. Такой подход является конкурентным преимуществом для компании из любой сферы бизнеса. Если говорить о разработчике, то навыки создания адаптивных web-сайтов будут существенным конкурентным преимуществом при поиске работы. Специальные знания и навыки вы можете получить, обучаясь по видео курсам ITVDN. В первую очередь это курсы: HTML5&CSS3 Advanced Практический курс по верстке лендинга Создание адаптивного сайта с Bootstrap 3 Также вам могут быть интересны другие курсы и технологии, которые входят в программу обучения по специальностям Верстальщик сайтов и FrontEnd разработчик. По материалам статьи за авторством Сони Грегори.
Розробка під Android - поради початківцям

Автор: Армен Маїлян

Овладейте языком Хорошие навыки работы со средой разработки и другими правильными инструментами разработки Знание компонентов приложения Понимание фрагментации, Android-приложений, потоков, загрузчиков и задач Правильный выбор необходимых инструментов разработки и последние советы Вывод Разработка приложений под различные мобильные платформы (Android,  iOs) – это то, на что ориентируются многие опытные и начинающие разработчики программного обеспечения. Ведь именно приложения делают телефоны «умными» смартфонами. Благодаря своим преимуществам приложения кардинально изменили возможности и функции вчерашних «звонилок». Выбирая разработку под Android как целевую платформу, у нас есть выбор между Java и Kotlin - основными языками программирования для этой платформы. Сейчас мы не будем вдаваться в детали их различий, и отвечать на вопрос «на чем писать приложения для Android?». Мы уже затрагивали этот вопрос недавно в соответствующей статье «Kotlin vs Java: что лучше для Android-разработки?». Сегодня мы остановимся на Java. Попробуем сформулировать основные советы в разработке приложений под андроид для начинающих.   Овладейте языком На сегодняшний день Java и XML являются двумя основными языками, используемыми при разработке приложений под Android. Поэтому знание и владение этими языками программирования и разметки является необходимым условием для разработки приложения под Android.  Задав себе вопрос «с чего начать программирование под андроид?», вы получите достаточно простой ответ – изучите основной язык разработки и основы ООП. Знания основ языка программирования Java должны включать в себя: • Понимание и применение пакетов (Packages) в Java; • Общее понимание ООП, понятия объектов и классов; • Понимание механизмов наследования, понимание и умение работать с интерфейсами; • Работа со строками и числовыми значениями, работа с дженериками; • Понимание функционирования коллекций и работы с ними; • Параллелизм. Правильное понимание Java и XML поможет вам создать/разработать более надежное и элегантное приложение для Android.   Хорошие навыки работы со средой разработки и другими правильными инструментами разработки Очень важно, чтобы, прежде чем приступить к полноценной разработке своего приложения, вы были хорошо знакомы с инструментами автоматизации сборки, а также с таким инструментом, как IDE - интегрированной средой разработки. В основном рекомендуется использовать Android App Studio IDE или Eclipse в качестве среды разработки. Применение их поможет вам изучить основы разработки и поможет вам улучшить качество вашего кода. Также советуем вам изучить такие механизмы как Apache Maven, Apache Ant и Gradle, поскольку они предоставляют собой мощный набор инструментов, помогающих управлять вашими сборками.   В процессе разработки важно, чтобы вы умели использовать инструменты и концепции контроля версий. Изучите git, а затем создайте репозиторий git-source (создав учетную запись в Bitbucket или GitHub). Чтобы получить представление об основных понятиях и условиях работы платформы, вы можете воспользоваться Git Pocket Guide.   Знание компонентов приложения Компоненты приложения — при разработке андроид-приложений выступают в роли основных строительных блоков. В свою очередь каждый из таких блоков представляет из себя отдельную точку, с помощью которой в ваше приложение может войти система. Хотя каждый из компонентов существует как отдельная сущность и играет свою отдельную роль, есть ряд компонентов, которые зависят друг от друга. При этом не все из них окажутся фактическими точками входа. Среди компонентов приложения выделяют пять разных типов, каждый из которых выполняет определенную роль и имеет свой собственный жизненный цикл, согласно которому он будет создаваться и уничтожаться. Они включают: Операции (Activity): это компонент андроид, представляющий один экран с пользовательским интерфейсом (к примеру, приложение для работы с электронной почтой может иметь одну Activity, отображающую список входящих писем, другую Activity - составляющую e-mail, и третью - читающую эти письма). Операции работают вместе, чтобы сформировать единый пользовательский опыт в андроид-приложении. Несмотря на это, каждая из Activity является независимой. Службы: компонент приложения в андроид, работающий в фоновом режиме и обеспечивающий выполнение длительных Activity и удаленных процессов. Этот компонент пользовательский интерфейс не предоставляет (например, пока пользователь обращается к интерфейсу другого приложения, он может проигрывать музыку в фоне). Поставщики содержимого или Content providers: компонент андроид-приложения, управляющий общим перечнем данных приложения. Используя этот компонент данные, хранимые в базе данных SQLite, в Интернете или файловой системе, могут быть запрошены или изменены (если допускает поставщик содержимого). Этот компонент также применим как для записи, так и чтения тех данных, что являются частными, а не общими для вашего приложения. Широковещательные приёмники или Broadcast receivers: компонент андроид-приложения, отвечающий на широковещательные сообщения, общие для системы. Большая часть таких сообщений происходят от системы. Broadcast receivers могут создавать в строке состояния уведомления, которые предупреждают пользователя, когда происходит широковещательное событие, хотя и не имеют пользовательского интерфейса. Как правило такой приёмник выполняет только минимальный объем работы, выступая для других андроид-компонентов в роли шлюза. Активация компонентов: асинхронное сообщение, также имеющее название - намерение (Intent). Активирует 3 из 4 компонентов (то есть сервисы, Broadcast receivers и Activity). Intents также связывают отдельные компоненты друг с другом во время выполнения, вне зависимости от того, принадлежит ли вашему приложению данный компонент или нет.   Понимание фрагментации, Android-приложений, потоков, загрузчиков и задач   Android — это фрагментированный рынок с множеством видов устройств и различных версий операционной системы. Обратите внимание, что, чем больше ваше устройство поддерживает различных устройств и/или версий, тем больше оно требует обслуживания и тестирования и, соответственно, больше будет сопутствующих расходов. Вам также потребуются соответствующие шрифты, ресурсы и макеты, которые помогут обеспечить наилучшее взаимодействие с различными характеристиками экрана. Вы придется держать во внимании всё множество поддерживаемых Android датчиков и средств пользовательского интерфейса.   Распределение версий Android по данным statista.com   Иногда, для выполнения фоновых задач, вам приходится использовать службы, которые должны выполняться непрерывно. Но, в ряде случаев, применение их может оказаться невозможным. Если вы хотите создать отличный и удобный пользовательский интерфейс, всегда следите за тем, чтобы поток никогда не блокировался. Поэтому длинные операции (вычисления, ввод-вывод, сеть и т. д.) должны выполняться асинхронно в фоновом режиме (в основном в другом потоке выполнения). Вот почему важно изучить средства параллелизма языка Java.   Правильный выбор необходимых инструментов разработки и последние советы Что нужно для создания приложения на андроид? Чтобы начать программировать под Android, вам подойдут весьма простые инструменты — это персональный компьютер с Mac или OS Windows, Linux. Сами же инструменты разработки ( IDE Eclipse, плагин ADT и Android SDK) – распространяются бесплатно. Android имеет некоторые уникальные параметры, которые вам следует учитывать при написании приложений под Android. Среди них: Производительность и скорость отклика: вы всегда должны реагировать на ввод пользователя в течение пяти секунд, в противном случае приложение выдаст ошибку ANR (ANR: application not responding - приложение не отвечает). Единственный доступный вариант - принудительно закрыть приложение. Пользователи заметят лаги более 100 мс: как уже упоминалось выше, поток пользовательского интерфейса никогда не должен блокироваться, потому что он только один. Ограниченные ресурсы: Wake-Lock (механизм, который заставляет устройство выполнять определенные действия, несмотря на рекомендацию менеджера батареи перевести устройство в спящий режим), следует использовать с осторожностью. Не обращайтесь лишний раз к элементам оборудования (например, GPS или акселерометру), потому что такие действия быстро разряжает аккумулятор.   Вывод Мы с вами рассмотрели ряд простых советов для тех, кто начинает программировать под андроид. Надеемся, что данные рекомендации помогут вам создавать новые высококачественные приложения. Если вы только раздумываете над направлением, в котором вам следует развиваться – рекомендуем посмотреть видео «Как стать Android разработчиком». В этом видео вы также сможете получить подходящие для вас ответы на вопрос «как начать программировать под Android?». Если вы уже определились с направлением развития и желаете получать знания в сфере разработки под Android - рекомендуем вам ознакомиться с нашим курсом подготовки Android Developer.  Если вы уже имеете определенные навыки в разработке и ищете дополнительных знаний по отдельным технологиям - вам наверняка будет полезен курс «Автоматизация сборки проектов с помощью Apache Maven». В свой очередь портал ITVDN.com желает вам успехов в обучении, интересных проектов и высокой зарплаты. По материалам статьи от Эшна Верна.
Notification success