Developer от Бога

DV

воскресенье, 9 июля 2017 г.

Java

Java, язык программирования высокого уровня, а это значит что вам необязательно знать электронику и регистры процессоров. Когда я решил заняться разработкой мобильных приложений и программ для компьютеров, долго думал что же выбрать, С++ или Java. Все-таки выбрал Java, конечно не забывая подглядывать в книги по C++.
Да, Java проигрывает в скорости работы приложений минимум на 80% и потребляют больше памяти. Но подобные языки программирования, которые не привязаны к конкретной операционной системе - это будущее. С каждым годом, компьютеры и разная переносимая электроника становятся все быстрее в обработке инструкций, и имеют все больше оперативной памяти. Также появляются новые компании со своей продукцией - новыми процессорами и операционными системами. Монополия на рынке процессоров Intel, AMD рушится под натиском новых чипов с архитектурой ARM и других, а дальше будет все еще более разнообразнее. Microsoft также есть о чем беспокоится, хоть мы замечаем их успешные попытки "подсадить" на Windows разработчиков мобильных приложений.
Но вернемся к Java. Данный язык программирования хорошо продуман, и имеет историю развития более 20 лет. Прежде всего нужно знать общий принцип работы программ на Java. Приложения разрабатываются на JDK, "весом" в несколько сотен мегабайт, а работают на JVM - виртуальной машине Java, которая занимает от 10 мегабайт до нескольких сотен килобайт, в зависимости на чем будут использоваться программы( компьютер или какое-то мобильное устройство). Скачать Java машину можно на официальном сайте.



В даном курсе программирования Java , будем больше обращать внимание на те моменты, по которым больше всего вопросов. Но повторим общие стандарты Java с СИ-подобными языками:


int a = 1;// Переменная типа int - хранит целые числа.
double b = 2.3; // Переменная типа double - хранит числа с плавающей точкой.
float c = 4.56;// Переменная типа double - хранит большие числа с плавающей точкой.
char d = 's';//Переменная типа char - хранит символы в одинарных кавычках.
String str "iVARIOUS";// Переменная типа String, хранит строки.
boolean = true; // Переменная типа boolean - хранит только два значения: true и false.
int[] arr = new int[9];// создания массива на 9 чисел.


{// участок кода без имени, в нем можно объявлять переменные с именами которые уже объявлялись, это не вызовет ошибку.
int a = 7;
double b = 8.3;
}

public void func(){ // Участок кода с именем, вызываться только по имени func(), и не возвращает значений.
//....
}

public int func(){// Участок кода с именем, вызываться только по имени func(), и возвращает значение типа int (как и любого типа).
//....
return 25;
}

public int func(int a){// Участок кода с именем, вызываться только по имени func(), принимает значения, и возвращает значение типа int (как и любого типа).
//....
return a;
}

Основы рисунка

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



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

Java. Классы и Обьекты

Java является языком объектно-ориентированным, это значит что программа состоит из объектов, или классов, которые по своей сути - не зависимы. То есть - если в Java объекте нет метода (или функции , как это принято называть в языках программирования СИ ) который возвращает (return) или предоставляет доступ к присвоению значений своим элементам, вы эти элементы никак не сможете использовать. Java Класс, по структуре чем то напоминает корпускулярно-волновой дуализм света. То-есть, в одних ситуациях свет ведет себя как электромагнитная волна, а в других как частица(с объектом можно вести себя как с переменной типа int, давая ей имена и присваивать значения, а можно вести себя и более шире ).
В СИ подобных языках ( к которым относится и Java) есть переменная типа int.
Int принимает в некотором диапазоне целые значения: 1, 2, 3... Int создается записью:

Int 'название';

Объект из класса создается подобным образом - 'тип' плюс 'название'.
Только если описание типа int уже заложено языком Java, то тип объекта вы должны сами придумать какой вам нужен, создав класс.
Например, класс содержащий размеры чего-то, должен иметь внутри себя имеет запись вида:

public class size {

int x=2;
int y=4;
int z=6;

}

Это был создан только Java класс (тип) ( что то вроде вашей собственной переменной int, но которая может хранить не одно число, а много разных).
Но чтоб создать переменную (вернее-Объект), нужно ей дать еще имя, для
int x;

'int'-это тип переменной, х - название переменной и имя участка в памяти, в которой хранится некое значение. Как типу int нельзя присвоить значение (int = 5), так и классу нельзя присвоить значение ( public class size{} , size = 5 ). Класс это только описание переменной, как буд-то вы сказали: "int-должен хранить только целые числа!".
Работа начинается после создания объекта:


public class size {

int x=2;
int y=4;
int z=6;

}


size object_a;//Имя указателя на Физически существующий объект в памяти.
object_a = new size();// Физически существующий объект в памяти.
size object_b; //Имя указателя на Физически существующий объект в памяти.
object_b=object_a;// Копирование физического адреса на который указывает object_a;
//.....................
object_b=new size();//Создание нового объекта
object_b=object_a;// Создание копии объекта object_a;

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

STM32

В последнее время, в мобильных телефонах, планшетах, и в прочих переносимых устройствах используются чипы с ядрами ARM. Если изучить чип одного производителя, то совсем не трудно будет перейти к другому производителю микроконтроллеров. Разница будет только в работе с периферийными блоками в микроконтроллерах.
Микроконтроллер можно представить как маленький компьютер, со своей оперативной и постоянной памятью, а также со своими входами/выходами для общения с внешним миром. Ядра ARM как уже упоминалось ( хоть и разные конечно) схожи от всех производителей, а вот периферию ( постоянную память , АЦП/ЦАП , интерфейсы передачи/приема данных ) производители ставят на свое усмотрение какую хотят.
И Это важно понять сразу, микроконтроллер похож на материнскую плату к которой присоединены разные вышеперечисленные устройства , и эти устройства для программиста - независимы от ядра (их перед работой нужно включать, настраивать как совершенно отдельные элементы, хотя они и находятся в одной микросхеме с ядром).
В микроконтроллерах есть регистры, регистры содержат данные. Одни данные (числа) используются для вычислений, другие данные (числа) для настроек микроконтроллера. В регистры записываются только двоичные числа. Регистры могут быть 8, 16, 32, 64 - битные, это значит что за один такт микроконтроллер может что то сделать с данными в регистре (записать/прочитать, произвести какое то вычисление). Чем больше разрядность регистров, тем большее число может быть обработано/перемещено за один такт.
Данный курс, будет посвящен работе с микроконтроллером STM32F407VG установленный на плате DISCOVERY, на ней есть все необходимое для изучения процессов протекающих внутри микроконтроллера, а также программатор, прошивающий как установленный МК так может использоваться для прошивки других МК. В качестве среды, будем использовать редактор Keil который можно скачать совершенно бесплатно на сайте компании, единственный минус бесплатной версии - это размер кода не должен превышать 32 килобайта. Но написать программу в 32 килобайта будет очень непросто, поэтому в первые несколько лет можно спокойно не заморачиваться по поводу редакторов.

3D Моделирование

Художники компьютерной графики, особенно направления в 3D, делятся на два типа: те кто умеют рисовать руками, и те кто не умеет. Тем кто рисует реально, намного проще, так как 90% у них уже есть то что надо для этого, а половину функций редакторов графики даже знать не нужно, остальные 10% нужных качеств занимает зависимость от аппаратных средств и знание самого редактора 3D графики.
Мы будем изучать 3D моделирование в редакторе Autodesk Maya , скачать последнюю версию Maya можно на официальном сайте, бесплатной 30-ти дневной версии вполне хватит чтоб пройти небольшой курс и создавать - любые 3D сцены: от дизайна интерьеров, архитектуры, и до работы в игровой индустрии, а перейти на любой другой редактор можно будет в любой момент, так как все 3D редакторы работают по одним и тем же принципам.
Мы не будем рассматривать средства выделения, перемещения и т.д, так как с этим не бывает проблем. Рассмотрим основные методы, которых достаточно чтоб сделать любую задачу быстро и качественно.
Объекты в 3D графике состоят из полигонов (плоскостей), ребер, и вершин которыми можно манипулировать - выделять, перемещать, изменять размеры, резать по полам. В основном, процесс моделирования (возьмем персонажа) происходит так: берется примитив - плоскость, из граней которой выдавливают новые грани и полигоны по образу "из головы". Есть методы для тех кто не обладает художественным обьемным мышлением : загружая в редактор фотографии человека или предмета с разных сторон,"наращивая" поверх них полигоны.
Но даже и этот метод устаревает, так как есть программы ( той же Autodesk) которые прямо по фотографиям с разных ракурсов создают модели в 3D, а остается только подправить несовершенство 3D сканеров.
Несмотря на тяжелые 3D редакторы с тысячами функций, для создания сцен любого уровня нужно всего с десяток, рассмотренных в этом курсе.

STM32. Программирование на СИ

Прежде чем перейти к стандартной программе с миганиями светодиодов стоит упомянуть о подготовительных действиях. Скачать на официальном сайте редактор Keil , в программе есть методы для скачивания нужных, что то вроде библиотек для работы с нужным микроконтроллером. В даном случае мы, будем использовать микроконтроллер STM32F4 установленный на демонстрационной плате Discovery. Микроконтроллер очень мощный, технологичный, нафаршированный всевозможной периферией и по цене - дешевле многих "раскрученных" 8-ми битных микросхем.
Когда я был на стадии изучения работы с этим микроконтроллером, удивил тот факт, что огромное количество уроков написаны с примерами использующих "филологичесский" способ программирования, а именно с использованием библиотек StdPeriph, это предусматривает изучать огромное количество слов без представления о том как работает микроконтроллер. Ведь намного проще записать:

*((uint32_t*)0x40020C00)=0b1010101000000000;


где сразу видно какие биты и какого регистра включены, а в документации можно увидеть что они делают, чем запоминать огромное количество слов из структур, где иногда ради установки одного бита нужно помнить 5-6 слов. Новичкам, не знакомым со всеми терминами и архитектурой микроконтроллера такой способ довольно запутанный.
Поэтому, никаких библиотек в примерах не будет. При создании проекта в Keil, не используйте имена проектов, документов, и папок в которых есть пробелы, это вызывает ошибку и недоумение у новичков - в чем дело.
Работать будем с широким использованием указателей и адресами.

WEB Разработка

WEB программирование, прежде всего это - разработка сайтов , и программ на сервере, которые определяют поведение сайта. Для написания этого сайта,iVARIOUS, использовались знания HTML,CSS,PHP,MYSQL. Стоит упомянуть то что огромную роль играет еще JavaScript, но его я стараюсь использовать минимум, в связи с тем, что программы на JavaScript зависят от версий и разработчиков браузеров. Также от версий и разработчиков браузеров в меньшей степени зависит и технология CSS, поэтому лучше использовать стандартные теги для описания элементов. Нет смысла описывать все HTML теги ( слова ), которые делают цвет текста, расположение элементов на странице и т.д , в интернете этой информации достаточно. Я разработал этот сайт всего за три месяца, не зная совершенно ничего о HTML,CSS,PHP,MYSQL.

HTML - язык гипертекстовой разметки, это еще не программирование, который говорит о размерах элементов, цвете , размере шрифта. Если нужно вывести заголовок страницы крупным шрифтом, пишем h1 Заголовок h1.
Есть размеры и поменьше - h2, h3, h4, h5, h6. Также с помощью HTML сообщает браузеру где на странице элементы будут в виде таблицы (table), списка , где будет картинка, видео и их размеры на странице.

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

Javascript - интерпретируемый язык программирования, тут уже начинается логика программ и поведения сайтов. Команды Javascript выполняются на компьютерах пользователей, поэтому в случае если на сайте будут происходить разные ресурсоемкие вычисления ( например интерактивные проекты в 3D ) лучше переложить работу вычислений на компьютеры пользователей. К сожаленью проблемы JavaScript те же что и в CSS, разные браузеры по разному понимают работу JavaScript.

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

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

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

пятница, 7 июля 2017 г.

Android. ListActivity


Списки в Android бывают простыми (в виде текста или картинок) и более сложные - комбинированные. В этом примере будет рассмотрен сложный список элементов (комбинация текст + изображение, которое использовалось в приложении по выбору эффекта для фотографии ). Для этого нужны два класса : BaseAdapter , который заполняет список данными, и ListActivity который этот список отображает. Любое активити, имеет один layout в момент времени, и его выводит на активное окно. Так как в примере сложный список ( содержит в одной ячейке изображение и текст ) то для такого списка понадобится отдельный layout файл в где будут созданы все нужные элементы (изображения, кнопки, текст и т.д) которые нужно отобразить в одной ячейке. Читать этот layout, и загружать в BaseAdapter будет важный класс - LayoutInflater. BaseAdapter заполняет layout ячейку (в примере это - элементы ImageView, TextView) нужными данными , и возвращает ListActivity, который повторяет эту операцию до вывода всего списка. LayoutInflater может использоваться не только для оформления элемента списка, но также для создания более сложных диалоговых окон чем обычно с одной или двумя кнопками, это такой контейнер который способен перемещать в нужное место для вывода layout файлы.


  1. public class FilterManager extends ListActivity {
  2. private FilterManagerAdapter adapter;
  3. @Override
  4. public void onCreate(Bundle savedInstanceState) {
  5. super.onCreate(savedInstanceState);
  6. setContentView(R.layout.filter_manager_activity);
  7. adapter = new FilterManagerAdapter(this);
  8. setListAdapter(adapter);
  9. }
  10. public void onListItemClick(
  11. ListView parent, View v, int position, long id) {
  12. }
  13. } }


В классе BaseAdapter эть несколько методов которые вызываются при каждом "рисовании" элемента списка : public int getCount() { return style.length; } - сообщает сколько раз нужно повторить итерацию ( в этом случае ровно столько сколько элементов в массиве - количество фильтров). А getView(int position, View convertView, ViewGroup parent) подставляет нужные изображения и текст и возвращает уже заполненный элемент списка в активность на котором все отображается. 



  1. public class FilterManager extends ListActivity {
  2. private FilterManagerAdapter adapter;
  3. @Override
  4. public void onCreate(Bundle savedInstanceState) {
  5. super.onCreate(savedInstanceState);
  6. setContentView(R.layout.filter_manager_activity);
  7. adapter = new FilterManagerAdapter(this);
  8. setListAdapter(adapter);
  9. }
  10. public void onListItemClick(
  11. ListView parent, View v, int position, long id) {
  12. }
  13. } }


В ListActivity важен метод public void onListItemClick(), в нем сообщается о нажатии на элемент списка

Android. RenderScript. C

В прошлой статье был рассмотрен пример кода использования RenderScript с помощью обычных Java - классов. Такой подход можно использовать в обычной обработке изображений (размытие, матрица свертки). Чтоб использовать все ресурсы RenderScript - не обойтись без написания кода на языке С (стандарта С99). Такой код пишется в отдельный файл с расширением .rs и содержится в специальной папке с названием "rs". Пример программы будет выглядеть следующим образом: Java файл.


  1. import android.content.Context;
  2. import android.graphics.Bitmap;
  3. import android.os.Handler;
  4. import android.support.v8.renderscript.Allocation;
  5. import android.support.v8.renderscript.RenderScript;
  6. import ivarious.xyz.Filters.ScriptC_draw;
  7. public class RenderScriptClass {
  8. Context context;
  9. Handler handler;
  10. private Bitmap mBitmapIn;
  11. private Bitmap mBitmapsOut;
  12. private RenderScript mRS;
  13. private Allocation mInAllocation;
  14. private Allocation mOutAllocations;
  15. private ScriptC_draw mScript;
  16. public MiandrRenderScript(Context arg, Bitmap arg_){
  17. mBitmapIn = arg_;
  18. context = arg;
  19. mBitmapsOut = Bitmap.createBitmap(mBitmapIn.getWidth(),mBitmapIn.getHeight(), Bitmap.Config.ARGB_8888);
  20. mRS = RenderScript.create(context);
  21. runScript();
  22. }
  23. public void runScript() {
  24. mInAllocation = Allocation.createFromBitmap(mRS,mBitmapIn);
  25. mOutAllocations = Allocation.createTyped(mRS, mInAllocation.getType());
  26. mScript = new ScriptC_draw(mRS);
  27. mScript.set_inputAlloc(mInAllocation);
  28. mScript.set_bWidht(mBitmapIn.getWidth());
  29. mScript.set_bHeight(mBitmapIn.getHeight());
  30. mScript.forEach_median(mInAllocation, mOutAllocations);
  31. mOutAllocations.copyTo(mBitmapsOut);
  32. }
  33. } }  


Этот класс принимает в конструктор два параметра Context и Bitmap. Контекст нужен для инициализации RenderScript, а Bitmap - входящее изображение для обработки. Также нужно Создать копию по параметрам входящего изображения для результата (mBitmapsOut). Входящий и исходящий Bitmap должен быть сконфигурирован как .ARGB_8888 или .ARGB_4444 ( с альфа каналом ), на других форматах могут быть ошибки. Подключение файла .rs происходит через класс ScriptC_"название файла" ( в данном случае это ScriptC_draw - файл draw.rs). Запуск процесса идет через функцию mScript.forEach_"название функции"(...);


  1. rs_allocation inputAlloc;
  2. uchar4 __attribute__((kernel)) median(uchar4 in, uint32_t x, uint32_t y)
  3. {
  4. uchar4 arrpix[9];
  5. uchar4 buff;
  6. if((x<bWidht-1) && (y<bHeight-1)&&(x>=1) && (y>=1)){
  7. arrpix[0] = rsGetElementAt_uchar4(inputAlloc, x -1, y - 1);
  8. arrpix[1] = rsGetElementAt_uchar4(inputAlloc, x , y - 1);
  9. arrpix[2] = rsGetElementAt_uchar4(inputAlloc, x +1, y - 1);
  10. arrpix[3] = rsGetElementAt_uchar4(inputAlloc, x -1, y );
  11. arrpix[4] = rsGetElementAt_uchar4(inputAlloc, x , y );
  12. arrpix[5] = rsGetElementAt_uchar4(inputAlloc, x +1, y );
  13. arrpix[6] = rsGetElementAt_uchar4(inputAlloc, x -1, y+1);
  14. arrpix[7] = rsGetElementAt_uchar4(inputAlloc, x , y+1);
  15. arrpix[8] = rsGetElementAt_uchar4(inputAlloc, x +1, y+1);
  16. for(int i=0; i<=4; i++)
  17. for(int i=0; i<=8; i++){
  18. if(arrpix[i].r>arrpix[i+1].r){
  19. buff.r = arrpix[i].r; arrpix[i].r = arrpix[i+1].r;
  20. arrpix[i+1].r = buff.r;}
  21. if(arrpix[i].g>arrpix[i+1].g){
  22. buff.g = arrpix[i].g; arrpix[i].g = arrpix[i+1].g;
  23. arrpix[i+1].g = buff.g;}
  24. if(arrpix[i].b>arrpix[i+1].b){
  25. buff.b = arrpix[i].b; arrpix[i].b = arrpix[i+1].b;
  26. arrpix[i+1].b = buff.b;}
  27. }
  28. return arrpix[4];
  29. }
  30. else return in;
  31. } else return in;
  32.  

Стоит отметить сразу во избежании путаницы: Функция uchar4 __attribute__((kernel)) median(...) вызывается каждый раз для каждого пикселя отдельно, то есть функция обрабатывает только один пиксель за один раз, который передается в параметрах функции uchar4 in. Если необходимо задать цвет пикселя в зависимости от окружающих пикселей ( как на примере - медианный фильтр ) то необходимо в файл .rs передать целый обьект: mScript.set_"название переменной в файле .rs"(mInAllocation); В самом файле .rs создать переменную которая будет принимать байты пикселей всего изображения: rs_allocation inputAlloc; Функция uchar4 __attribute__((kernel)) median(...) также может принимать от системы координаты текущего пикселя uint32_t x, uint32_t y, от которого можно вычислять остальные. В конечном результате доступ к другим пикселям можно получить используя функцию uchar4 buff = rsGetElementAt_uchar4(inputAlloc, x + a , y + b); В RenderScript есть несколько типов переменных - векторов. Каждый вектор содержит данные о пикселе. Получать доступ к ним можно через оператор - точку : in.r = 255.

Android. RenderScript

Выше перечисленные методы и классы не подходят по обработке более сложных алгоритмов в разумных временных рамках чем простое преобразование цвета . Некоторое решение можно получить разбив изображение на части и обрабатывать их в разных потоках. Как видно в таблице, даже в случае разбивки изображения размеров 1920х1080 на 5 частей и обработки его в разных потоках на смартфоне Samsung Galaxy S5 , можно добиться скорости преобразования до 15 секунд с применением медианного фильтра. Много это времени или достаточно - зависит от ситуации и предназначения приложения. Но однозначно можно сказать, что для обработки видео в реальном времени или использования несколько разных алгоритмов одновременно - скорости явно не достаточно. Конечно решений есть несколько, это использование OpenGL или Android NDK который позволяет писать код на языках С. У них есть свои недостатки, такие как сложность, которая отвлекает от алгоритмов на разного рода стандартные интерфейсы, или зависимость от платформы. Идеальным решением есть использование, в некотором смысле, гибридной технологии RenderScript. Особенностью которого его есть возможность писать алгоритмы на языке С и обрабатывать код на более низком уровне, с последующей компиляцией уже на телефоне перед установкой приложения, для достижения максимальной совместимости. Разработчики постарались максимально найти компромисс между переносимостью, простотою использования и скоростью работы программ. Саму работу в RenderScript можно вести двумя способами - используя Java классы с готовыми решениями для таких операций как размытие, применение матрицы свертки, и некоторых других. Или более гибкий второй способ - написание алгоритмов на языке С99 с некоторыми ограничениями. Обработка изображений RenderScript длится если не мгновенно то иногда в десятки раз быстрее тех же алгоритмов работающих на Java. Пример кода на Java для использования RenderScript (Матрица свертки) :


  1. float[] matrix =
  2. {
  3. -1; -1; -1;
  4. -1; 9; -1;
  5. -1; -1; 1;}
  6. Bitmap src;
  7. resultt = Bitmap.createBitmap(src.getWidth(),src.getHeight(), Bitmap.Config.ARGB_8888);
  8. RenderScript renderScript = RenderScript.create(context);
  9. Allocation input = Allocation.createFromBitmap(renderScript, src);
  10. Allocation output = Allocation.createFromBitmap(renderScript, resultt);
  11. ScriptIntrinsicConvolve3x3 convolution = ScriptIntrinsicConvolve3x3
  12. .create(renderScript, Element.U8_4(renderScript));
  13. convolution.setInput(input);
  14. convolution.setCoefficients(matrix);
  15. convolution.forEach(output);
  16. output.copyTo(resultt); ut); 



Матрица свертки отдельная тема, если коротко - это произведение чисел матрицы на соответствующие пиксели участка изображения. Такой, на первый взгляд простой алгоритм выдает самые разные результаты после обработки в зависимости от цифр в матрице (в массиве). Сама работа программы идет примерно так : на вход (Bitmap) подается изображение, и создается с такими же параметрами Bitmap для результата. RenderScript - главный класс который принимает контекст. Allocation input - выделенная память для данных ( например изображения ), в качестве аргументов конструктора принимает объект RenderScript и само изображение в виде Bitmap. ScriptIntrinsicConvolve3x3 - создание матрицы 3х3, в которую далее отправляются ссылка на память с данными (Allocation input) , и матрица в виде массива, после чего вызывается главный метод forEach() который запускает обработку кода на низком уровне. В качестве параметра метод forEach() принимает участок памяти (Allocation output) для помещения туда результата обработки. В конечном итоге метод output.copyTo() преобразовывает данные к нужному виду и в указанный Bitmap. После чего возможен уже вывод или сохранение обработанного изображения. В следующих примерах будет показан способ работы с кодом непосредственно на языке С. 

Android. Bitmap. Canvas. Рисование


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



  1. Bitmap bitmap = BitmapFactory.decodeResource( context.getResources() , R.drawable.image);
  2. ImageView.setImageBitmap(bitmap);
  3. int hColor = 0;
  4. int r, g, b;
  5. for (int x = 0; x < bitmap.getWidth(); x++)
  6. for (int y = y; y < bitmap.getHeight(); y++) {
  7. hColor = bitmap.getPixel(x, y);
  8. r = (hColor>>16)&0xff;
  9. g = (hColor>>8)&0xff;
  10. b = hColor&0xff;
  11. bitmap.setPixel(x, y, Color.rgb( r/2, g/2, b/2));
  12. } tmap.setPixel(x, y, Color.rgb( r/2, g/2, b/2));



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


  1. r = Color.red(bitmap.getPixel(x, y));
  2. g = Color.green(bitmap.getPixel(x, y));
  3. b = Color.blue(bitmap.getPixel(x, y));  



Работа напрямую с Bitmap эффективна при работе с изображением в глобальном плане, однако когда необходимо выводить стандартные фигуры ( текст, линии , прямоугольники, круги, эллипсы, заливка цветом и другое) более эффективно использовать класс Canvas. Он сам решает математику по рисованию фигур в двумерном пространстве на пикселях. Более того, по возможности нужно всегда использовать стандартные классы в Android, так как они имеют хорошую оптимизацию на уровне виртуальной машины ( глубже на уровне железа ) всегда будут работать быстрее пользовательских решений подобных задач, используя неявные связи между Bitmap и виртуальной машиной. Если для заливки Bitmap цветом нужно использовать цикл, и перебрать все пиксели, что займет некоторое время, то класс Canvas справится с этим практически мгновенно. Объект данного класса принимает в конструктор Bitmap, на котором нужно рисовать, и объект типа Paint который задает параметры кисти.


  1. Canvas canvas = new Canvas(bitmap);
  2. canvas.drawLine(x, y, x + a, y + b, Paint);
  3. ...
  4. canvas.drawRect(x, y, x + a, y + b, Paint);
  5. ImageView.setImageBitmap(bitmap); , Paint);


Описывать все методы рисования Canvas не имеет смысла, они просты и в документации достаточно описаны. В самом Android SDK эти методы также можно посмотреть.

Android. Android.hardware.camera2. Сохранение фото

В прошлом примере был показан способ воспроизведения видео с камеры в реальном времени на поверхность TextureView, в этом примере будет показан комплекс действий по захвату полноценного изображения с камеры и сохранения его в папке с фотографиями. На схеме выше изображены три шага по работе с камерой в Android API 21 и выше (класс android.hardware.camera2).

1) Извлекаем нужные статичные данные о камере из класса CameraDevice, а  подкласса CameraDevice.StateCallback и его метода обратного вызова onOpened(CameraDevice camera) узнаем о готовности камеры к работе. Через  CameraManager.openCamera() открываем камеру.

2) CameraCaptureSessi создает сессию, а в CaptureRequest.Builder (постройщик запроса) заносятся параметры запроса (воспроизведение на выбранную поверхность SurfaceTexture или iImageReader c последующим сохранением). Ход запросов можно отслеживать через объект CameraCaptureSession.CaptureCallback  и метод обратного вызова
onCaptureCompleted() который вызывается системой по успешном окончании захвата изображения. В нем как правило можно переконфигурировать запрос CaptureRequest.Builder
и перенаправить изображение на класс iImageReader для последующего сохранения, вместо обычного TextureView при обычном воспроизведении.

3) В ImageReader и  его метод setOnImageAvailableListener() устанавливаем класс обратного вызова ImageReader.OnImageAvailableListener, метод которого  public void onImageAvailable(ImageReader reader) вызывается как только изображение будет готово для последующей обработки (сырое изображение передается входному аргументу метода).


В созданном классе ImageSaver проходит комплекс действий по обработке байтов изображения в нужный формат и его сохранению в папку с фотографиями.

  1. import android.content.Context;
  2. import android.content.Intent;
  3. import android.graphics.ImageFormat;
  4. import android.graphics.SurfaceTexture;
  5. import android.hardware.camera2.CameraAccessException;
  6. import android.hardware.camera2.CameraCaptureSession;
  7. import android.hardware.camera2.CameraDevice;
  8. import android.hardware.camera2.CameraManager;
  9. import android.hardware.camera2.CaptureRequest;
  10. import android.hardware.camera2.TotalCaptureResult;
  11. import android.media.Image;
  12. import android.media.ImageReader;
  13. import android.net.Uri;
  14. import android.os.Environment;
  15. import android.os.Handler;
  16. import android.os.HandlerThread;
  17. import android.support.v4.content.ContextCompat;
  18. import android.support.v7.app.AppCompatActivity;
  19. import android.os.Bundle;
  20. import android.view.Surface;
  21. import android.view.TextureView;
  22. import android.view.View;
  23. import android.widget.ImageButton;

  24. import java.io.File;
  25. import java.io.FileOutputStream;
  26. import java.io.IOException;
  27. import java.nio.ByteBuffer;
  28. import java.text.SimpleDateFormat;
  29. import java.util.Arrays;
  30. import java.util.Date;



  31. public class MainActivity extends AppCompatActivity {

  32. private TextureView iTextureView;
  33. private CameraDevice iCameraDevice;

  34. private HandlerThread iBackgroundHandlerThread;
  35. private Handler iBackgroundHandler;
  36. private ImageReader iImageReader;

  37. private CameraCaptureSession iPreviewCaptureSession;
  38. private CaptureRequest.Builder iCaptureRequestBuilder;
  39. private ImageButton foto;

  40. private File iImageFolder;
  41. private String iImageFile;


  42. @Override
  43. protected void onCreate(Bundle savedInstanceState) {
  44. super.onCreate(savedInstanceState);
  45. setContentView(R.layout.activity_main);

  46. createImageFolder();

  47. iTextureView = (TextureView) findViewById(R.id.textureView);
  48. foto = (ImageButton) findViewById(R.id.cameraImageButton2);





  49. foto.setOnClickListener(new View.OnClickListener() {
  50. @Override
  51. public void onClick(View v) {
  52. try {iPreviewCaptureSession.capture(iCaptureRequestBuilder.build(), iPreviewCaptureCallback, null); }
  53. catch (CameraAccessException e) { e.printStackTrace(); }
  54. } }); }




  55. private TextureView.SurfaceTextureListener iSurfaceTextureListener =
  56. new TextureView.SurfaceTextureListener() {
  57. @Override
  58. public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
  59. iImageReader = ImageReader.newInstance(1920,1080, ImageFormat.JPEG, 1);
  60. iImageReader.setOnImageAvailableListener(iOnImageAvailableListener,null);
  61. connectCamera();}

  62. @Override
  63. public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) { }
  64. @Override
  65. public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
  66. return false;
  67. }
  68. @Override
  69. public void onSurfaceTextureUpdated(SurfaceTexture surface) {}};






  70. private CameraDevice.StateCallback iCameraDeviceStateCallback = new CameraDevice.StateCallback() {
  71. @Override
  72. public void onOpened(CameraDevice camera) {iCameraDevice = camera; startPreview(); }

  73. @Override
  74. public void onDisconnected(CameraDevice camera) { camera.close(); iCameraDevice = null; }

  75. @Override
  76. public void onError(CameraDevice camera, int error) {camera.close(); iCameraDevice = null; }
  77. };






  78. private final ImageReader.OnImageAvailableListener iOnImageAvailableListener = new
  79. ImageReader.OnImageAvailableListener() {
  80. @Override
  81. public void onImageAvailable(ImageReader reader) {
  82. iBackgroundHandler.post(new ImageSaver(reader.acquireLatestImage()));} };



  83. private class ImageSaver implements Runnable {

  84. private final Image iImage;

  85. public ImageSaver(Image image) { iImage = image; }

  86. @Override
  87. public void run() {
  88. ByteBuffer byteBuffer = iImage.getPlanes()[0].getBuffer();
  89. byte[] bytes = new byte[byteBuffer.remaining()];
  90. byteBuffer.get(bytes);

  91. FileOutputStream fileOutputStream = null;

  92. try {fileOutputStream = new FileOutputStream(iImageFile);
  93. fileOutputStream.write(bytes); }

  94. catch (IOException e) { e.printStackTrace(); }

  95. finally {
  96. iImage.close();

  97. Intent mediaStoreUpdateIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
  98. mediaStoreUpdateIntent.setData(Uri.fromFile(new File(iImageFile)));
  99. sendBroadcast(mediaStoreUpdateIntent);

  100. if(fileOutputStream != null) {try {fileOutputStream.close();}
  101. catch (IOException e) { e.printStackTrace(); }}}}}






  102. private CameraCaptureSession.CaptureCallback iPreviewCaptureCallback = new
  103. CameraCaptureSession.CaptureCallback() {

  104. @Override
  105. public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request, TotalCaptureResult result) {
  106. super.onCaptureCompleted(session, request, result);

  107. try {
  108. iCaptureRequestBuilder.addTarget(iImageReader.getSurface());
  109. iPreviewCaptureSession.capture(iCaptureRequestBuilder.build(), run2, null);

  110. } catch (CameraAccessException e) { e.printStackTrace(); }

  111. }
  112. };









  113. @Override
  114. protected void onResume() {
  115. super.onResume();

  116. startBackgroundThread();

  117. if(iTextureView.isAvailable()) {

  118. connectCamera();
  119. } else {
  120. iTextureView.setSurfaceTextureListener(iSurfaceTextureListener);
  121. }
  122. }

  123. private void connectCamera() {
  124. CameraManager cameraManager = (CameraManager)getSystemService(Context.CAMERA_SERVICE);

  125. try { ContextCompat.checkSelfPermission(this, android.Manifest.permission.CAMERA);
  126. cameraManager.openCamera("0", iCameraDeviceStateCallback, null);}
  127. catch (CameraAccessException e) { e.printStackTrace(); }}






  128. CameraCaptureSession.StateCallback run = new CameraCaptureSession.StateCallback(){

  129. @Override
  130. public void onConfigured(CameraCaptureSession session) {

  131. iPreviewCaptureSession = session;
  132. try {iPreviewCaptureSession.setRepeatingRequest(iCaptureRequestBuilder.build(),null, null);}
  133. catch (CameraAccessException e) {e.printStackTrace();}}

  134. @Override
  135. public void onConfigureFailed(CameraCaptureSession session) {} };






  136. CameraCaptureSession.CaptureCallback run2 = new
  137. CameraCaptureSession.CaptureCallback() {
  138. @Override
  139. public void onCaptureStarted(CameraCaptureSession session, CaptureRequest request, long timestamp, long frameNumber) {

  140. super.onCaptureStarted(session, request, timestamp, frameNumber);

  141. try {createImageFileName();}
  142. catch (IOException e) { e.printStackTrace(); } } };





  143. private void startPreview() {
  144. SurfaceTexture surfaceTexture = iTextureView.getSurfaceTexture();
  145. surfaceTexture.setDefaultBufferSize(1920,1080);
  146. Surface previewSurface = new Surface(surfaceTexture);

  147. try {
  148. iCaptureRequestBuilder = iCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
  149. iCaptureRequestBuilder.addTarget(previewSurface);
  150. iCameraDevice.createCaptureSession(Arrays.asList(previewSurface,iImageReader.getSurface()),run, null);}

  151. catch (CameraAccessException e) {e.printStackTrace();}}




  152. private void startBackgroundThread() {
  153. iBackgroundHandlerThread = new HandlerThread("Camera2Foto");
  154. iBackgroundHandlerThread.start();
  155. iBackgroundHandler = new Handler(iBackgroundHandlerThread.getLooper()); }


  156. private void createImageFolder() {
  157. File imageFile = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
  158. iImageFolder = new File(imageFile, "Camera2Foto");
  159. if(!iImageFolder.exists()) {
  160. iImageFolder.mkdirs(); } }



  161. private File createImageFileName() throws IOException {
  162. String timestamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
  163. String prepend = "IMAGE_" + timestamp + "_";
  164. File imageFile = File.createTempFile(prepend, ".jpg", iImageFolder);
  165. iImageFile = imageFile.getAbsolutePath();
  166. return imageFile; }


  167. } return imageFile; }

четверг, 6 июля 2017 г.

Android. Камера. Android.hardware.camera2

С появлением Android API >21 интерфейс работы с камерой принципиально изменился. В основном причина - совершенствование техники и аппаратного обеспечения которое позволяет дать разработчикам гораздо больше возможностей. Первое что нужно понять - принцип работы функций обратного вызова, которые вызываются не программой, а самой Операционной Системой Android после наступления некоторого события (например - камера готова к использованию или кадр готов к чтению). Все остальное можно понять с краткого описания в документации и примера приложения которое просто открывает камеру, без перегрузки кода пока лишними элементами типа - кнопок, сохранения кадров и т.п. Для начала нужно разобрать минимум действий для запуска камеры.
Пакет Android.hardware.camera2 предоставляет интерфейс для разных устройств-камер, подключенных к Android. Он заменяет устаревший класс  Camera. Классы этого пакета создают потоковый интерфейс, который принимает входные данные запросов для захвата одного кадра, захвата одного изображения с каждого запроса, а затем выводит этот результат, плюс - набор буферов вывода изображений. Запросы обрабатываются таким образом, что несколько запросов может находиться в процессе сразу. Поскольку устройство камеры представляет собой поток из нескольких этапов, имея несколько запросов в одновременно требуется поддерживать полный фреймрейт на большинстве устройств Android.
Ниже перечислены некоторые классы Сamera2:
Типы нужных запросов, а также доступные на устройстве камеры определены в классе CameraManager

CameraDevices предоставляет набор статической информации описывающий аппаратное устройство, доступные параметры и параметры вывода для данного устройства. Эта информация предоставляется через объект CameraCharacteristics, и доступна через getCameraCharacteristics(String).

Для того, чтобы захватить кадр или поток изображений с камеры, приложение должно сначала создать сеанс захвата с набором выходных Surfaces для использования вместе с устройством камеры, с createCaptureSession (List, CameraCaptureSession.StateCallback, Handler). Каждый Surfaces должен быть предварительно сконфигурирован с соответствующим размером и форматом (если это применимо), чтобы соответствовать размерам и форматам, доступные для данной модели камеры. Целевой Surfaces может быть получен из различных классов: SurfaceView, SurfaceTexture  через Surface(SurfaceTexture), MediaCodec, MediaRecorder, Allocation и ImageReader.

Как правило, предварительный просмотр изображений отправляется на SurfaceView или TextureView (через его SurfaceTexture). Захват изображений JPEG или RAW буфера для DngCreator может быть произведен ImageReader с форматами JPEG и RAW_SENSOR. Применение управляемой обработки данных камеры в Renderscript, OpenGL ES, или непосредственно в управляемом машинном коде лучше всего делать с типом YUV, SurfaceTexture и ImageReader с форматом YUV_420_888, соответственно.


После этого приложение должно создать CaptureRequest , который определяет все необходимые параметры для захвата одного изображения. Запрос также перечисляет, какие из сконфигурированных выходных Surfaces следует использовать для этого захвата. CameraDevice имеет собственный метод для создания запроса постройщика (builder) для данного варианта использования, который оптимизирован для работы приложения в Android устройстве.


  1. import android.content.Context;
  2. import android.graphics.ImageFormat;
  3. import android.graphics.SurfaceTexture;
  4. import android.hardware.camera2.CameraAccessException;
  5. import android.hardware.camera2.CameraCaptureSession;
  6. import android.hardware.camera2.CameraDevice;
  7. import android.hardware.camera2.CameraManager;
  8. import android.hardware.camera2.CaptureRequest;
  9. import android.media.ImageReader;
  10. import android.support.v4.content.ContextCompat;
  11. import android.support.v7.app.AppCompatActivity;
  12. import android.os.Bundle;
  13. import android.view.Surface;
  14. import android.view.TextureView;
  15. import android.widget.ImageView;

  16. import java.util.Arrays;

  17. public class MainActivity extends AppCompatActivity {



  18. private CameraDevice mCameraDevice;
  19. private TextureView mTextureView;
  20. private CameraCaptureSession mPreviewCaptureSession;
  21. private CaptureRequest.Builder mCaptureRequestBuilder;



  22. private ImageReader mImageReader = ImageReader.newInstance(1920,1080, ImageFormat.JPEG, 1);

  23. private TextureView.SurfaceTextureListener mSurfaceTextureListener = new TextureView.SurfaceTextureListener() {
  24. @Override
  25. public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {connectCamera();}
  26. @Override public void onSurfaceTextureSizeChanged(SurfaceTexture surface,int width,int height){}
  27. @Override public boolean onSurfaceTextureDestroyed(SurfaceTexture surface){return false;}
  28. @Override public void onSurfaceTextureUpdated(SurfaceTexture surface){}
  29. };


  30. private CameraDevice.StateCallback mCameraDeviceStateCallback = new CameraDevice.StateCallback(){
  31. @Override
  32. public void onOpened(CameraDevice camera) {mCameraDevice = camera; startPreview();}

  33. @Override
  34. public void onDisconnected(CameraDevice camera) { camera.close(); mCameraDevice = null;}

  35. @Override
  36. public void onError(CameraDevice camera, int error) {camera.close(); mCameraDevice = null;}
  37. };


  38. @Override
  39. protected void onCreate(Bundle savedInstanceState) {
  40. super.onCreate(savedInstanceState);
  41. setContentView(R.layout.activity_main);


  42. mTextureView = (TextureView)findViewById(R.id.textureView);
  43. mTextureView.setSurfaceTextureListener(mSurfaceTextureListener);
  44. }


  45. private void connectCamera() {
  46. CameraManager cameraManager = (CameraManager)getSystemService(Context.CAMERA_SERVICE);

  47. try { ContextCompat.checkSelfPermission(this, android.Manifest.permission.CAMERA);
  48. cameraManager.openCamera("0", mCameraDeviceStateCallback, null);}
  49. catch (CameraAccessException e) { e.printStackTrace(); }}



  50. CameraCaptureSession.StateCallback run = new CameraCaptureSession.StateCallback(){

  51. @Override
  52. public void onConfigured(CameraCaptureSession session) {

  53. mPreviewCaptureSession = session;

  54. try {mPreviewCaptureSession.setRepeatingRequest(mCaptureRequestBuilder.build(),null, null);}
  55. catch (CameraAccessException e) {e.printStackTrace();}}
  56. @Override
  57. public void onConfigureFailed(CameraCaptureSession session) {}

  58. };

  59. private void startPreview() {
  60. SurfaceTexture surfaceTexture = mTextureView.getSurfaceTexture();
  61. surfaceTexture.setDefaultBufferSize(1920,1080);
  62. Surface previewSurface = new Surface(surfaceTexture);

  63. try {
  64. mCaptureRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
  65. mCaptureRequestBuilder.addTarget(previewSurface);
  66. mCameraDevice.createCaptureSession(Arrays.asList(previewSurface,mImageReader.getSurface()),run, null);}

  67. catch (CameraAccessException e) {e.printStackTrace();}}

  68. }

TCP/IP. Socket

TCP - протокол передачи данных на IP датаграммах. Проще говоря - IP протокол передает данные, а TCP поверх него управляет и контролирует доставку. Так как сам протокол IP не гарантирует доставку данных, то TCP интерфейс считает отправленные и полученные данные, а в случае потери данных делает необходимое для повторной отправки. Обмен данными по протоколу TCP возможен как между приложениями на одном компьютере, так и между приложениями на разных удаленных компьютерах или устройствах. Именно поэтому он является основой в современном мире передачи данных от встраиваемых систем в локальных сетях до сетей Интернет. Самая известная  часть TCP это - Socket, который принято называть - конечной точкой. С простой точки зрения Socket являет собой некий идентификатор, связующим звеном между клиентом и сервером, с помощью которого происходит обмен данными. Socket содержит в себе IP адрес в целом, и номер порта в частности. Каждое приложение, если нужно, способно общаться с внешним миром через свой собственный порт. Если двум одновременно работающим приложениям присвоить одинаковые номера портов - это вызовет конфликт в одном из приложений при попытке использовать уже занятый порт. Рассмотрим простое консольное приложение которое связывается с сервером и читает его страницу. По сути это простой "Клиент" который инициирует общение, отправляет данные (например HTTP запрос) по которым сервер понимает чего от него хотят (например -  вернуть веб страницу). Чтоб не загромождать код, в нем упущены условия в случае ошибки, поэтому перед запуском клиента нужно убедиться что сервер работает



  1. #define WIN32_LEAN_AND_MEAN
  2. #include <conio.h> 
  3. #include <winsock2.h>
  4. #include <ws2tcpip.h>
  5. #include "stdafx.h"
  6. using namespace std;

  7. #pragma comment (lib, "Ws2_32.lib")

  8. #define DEFAULT_BUFLEN 4048
  9. #define DEFAULT_PORT "80"

  10. int main()
  11. {
  12. WSADATA wsaData;
  13. SOCKET ConnectSocket = INVALID_SOCKET;
  14. struct addrinfo *result, hints;
  15. char sendbuf[] = "GET http://ivarious.xyz HTTP/1.0 \r\n\r\n ";
  16. char recvbuf[DEFAULT_BUFLEN]=" ";
  17. int recvbuflen = DEFAULT_BUFLEN;

  18. WSAStartup(MAKEWORD(2, 2), &wsaData);

  19. ZeroMemory(&hints, sizeof(hints));
  20. hints.ai_family = AF_INET;
  21. hints.ai_socktype = SOCK_STREAM;
  22. hints.ai_protocol = IPPROTO_TCP;

  23. getaddrinfo("77.222.42.240", DEFAULT_PORT, &hints, &result);

  24. ConnectSocket = socket(result->ai_family, result->ai_socktype,result->ai_protocol);
  25. connect(ConnectSocket, result->ai_addr, (int)result->ai_addrlen);
  26. send(ConnectSocket, sendbuf, sizeof(sendbuf), 0);
  27. recv(ConnectSocket, recvbuf, DEFAULT_BUFLEN, 0);

  28. printf("%s", recvbuf);

  29. closesocket(ConnectSocket);
  30. WSACleanup();

  31. _getch();

  32. return 0;
  33. }




WSAStartup(MAKEWORD(2, 2), &wsaData) - инициализация библиотеки TCP.
struct addrinfo - структура с данными адреса и некоторыми настройками.
getaddrinfo("77.222.42.240", DEFAULT_PORT, &hints, &result) - обрабатывает строки IP адреса и порта к нужному виду.

socket(result->ai_family, result->ai_socktype,result->ai_protocol);
connect(ConnectSocket, result->ai_addr, (int)result->ai_addrlen) - создание и подключение сокета.
send(ConnectSocket, sendbuf, sizeof(sendbuf), 0) и recv(ConnectSocket, recvbuf, DEFAULT_BUFLEN, 0) соответственно отправка данных (HTTP запрос к серверу) и буфер для текстового ответа от сервера.

Бронецький Замок. 3D реконструкція

Сприймаючи відступ монголо-татар на чолі з Батиєм як явище тимчасове, в другій половині 13 століття в Угорському королівстві поспіхом зводяться різного роду об'єкти військового значення. Саме в цей час починають будувати перші укріплення на горі Бронецького Замку. Основне призначення замку це - контроль кордону королівства з боку карпат, його шляхів, а також як місце дислокації невеликих військових загонів. Завдяки розміщенню в важкодоступному місці на крутих скелястих схилах, з відсутністю зручних шляхів підходу, замок важко було успішно атакувати чи непомітно до нього наблизитись.
Перша згадка про нього датується 1265 роком, коли замок штурмував Іштван V, який постійно намагався захопити трон свого батька короля Бели IV піднімаючи повстання. В 1273 році королем Ласло IV Кун (сином Іштвана V) була видана грамота в якій згадується Бронецький замок, що був відібраний у ворогів його батька. Останній король династії Арпадів - Ендре III був вимушений зруйнувати деякі прикордонні фортеці через мирний договір з іншим претендентом на трон - герцогом Альбрехтом Австрійським. В 1291 році замок був зруйнований. Ця дата й стала офіційним відходом замку від політичний й воєнних справ. Уже в 1336 році в межевих описах місцевості він згадується як руїна Бронецького Замку або «destructum castrum Baranka».
Проте замок далі продовжував відігравати роль в житті людей. Часто він використовувався різними мандрівниками чи авантюристами як місце відпочинку з дороги, куди вони заселялись на декілька днів чи навіть тижнів й місяців. Останне в деякій мірі підтверджується записами від 1511 року угорським військовим писарем Габор Тидошем, принаймні він згадує руїни Бронецького замку як "viatorum ruinas" або руїни мандрівників. Й немає сумнівів що руїни замку бачили немало різних людей того часу.
Вже близько 700 років як замок лежить в руїнах, й можна було б взагалі все сприймати як легенду, проте залишки кам'яних стін роблять його цілком історичним об'єктом, одним з двох Закарпатських Лицарських Замків.

Середнянський замок. 3D реконструкція

Єдиний в Україні Замок тамплієрів знаходиться на Закарпатті - Середнянський Замок. Він собою представляє чотирикутну башту донжон (або башта останньої оборони) яку використовували тільки в найважчих випадках. В мирний час основне його призначення це - митний пост який контролював соляні шляхи в цьому районі. Інженерне рішення типове для таких споруд того часу. Навколо башти мали місце вали й рови, а також мур заввишки до 4 метрів з напівкруглими вежами по кутах, сліди яких на даний момент вже не збереглись. Перший камінь замку був закладений в 1146 році на кошти Фруа де Барманьє, а прообразом слугували давньоримські прикордонні сторожові вежі на Рейні та Дунаї. Після ліквідації ордену тамплієрів 1312 року почався переділ власності тутешніх земель між угорським королем та кількома родами, один з яких були рід Другетів. В 15 столітті боротьба Другетів достигла свого апогею з родом Палочі, тоді Другети змушені були поступитись замком. В 1526 році в битві з турками гине останній представник роду Палочі й замок переходить до роду Добо. Саме в цей період краї починає активно розвиватись виноградарство й створюються найбільші винні підвали. До 18 століття замок багато разів ще міняв своїх володарів, але для історії назавжди цей замок залишиться Замком Ордену Тамплієрів.

Android. HttpURLConnection. Чтение WEB HTML


Соединение устройства с внешним миром часто бывает очень запутанным даже на простых примерах, благодаря огромному количеству версий ОС Android. Официальная документация подает скупой пример работы HttpURLConnection и при изучении мне пришлось долго разбираться с причиной не работающего кода на устройстве с Android 6.0 взятых из разных источников. Имеющаяся информация на форумах и блогах очень отрывистая, и не дает полного примера работающей простой программы которая читает HTML страницы сайта.
Эта тема охватывает два вопроса: класс AsyncTask и собственно HttpURLConnection.
AsyncTask - это класс который создает отдельный поток для сложных вычислений, с ним работа идет проще чем с ранее рассмотренным Thread, так как не требует создания дополнительных классов типа Hndler и Runnable (AsyncTask создает их сам). AsyncTask - нужен для того, чтоб вынести работу с сетью в отдельный поток. Попытка установить сеть из основного потока вызовет исключение.
Для начала создадим класс HttpConnect который и будет устанавливать контакт с сайтом, а один из его методов будет принимать строку-адрес и возвращать строку с HTML кодом страницы. На данном этапе нет смысла перегружать статью описаниями обьектов и их методов которые участвуют в классе HttpConnect ( тут уже действительно можно обойтись беглым взглядом по документации). Цель статьи увидеть заветную строку HTML кода в своей первой программе. 

HttpConnect.java
  1. import java.io.BufferedReader;
  2. import java.io.IOException;
  3. import java.io.InputStreamReader;
  4. import java.net.HttpURLConnection;
  5. import java.net.URL;

  6. public class HttpConnect {
  7. public static String HttpConnect(String uri)
  8. {
  9. BufferedReader reader = null;

  10. try {
  11. URL url = new URL(uri);
  12. HttpURLConnection con = (HttpURLConnection) url.openConnection();

  13. StringBuilder sb = new StringBuilder();
  14. reader = new BufferedReader(new InputStreamReader(con.getInputStream()));
  15. String line;

  16. while( (line = reader.readLine()) != null )
  17. {
  18. sb.append(line + "\n");
  19. }

  20. return sb.toString();

  21. } catch (Exception e) {
  22. return e.toString();
  23. }finally
  24. {
  25. try {
  26. if (reader != null)
  27. reader.close();
  28. } catch (IOException e) {
  29. e.printStackTrace();
  30. }
  31. }


  32. }

  33. } }
Программа проста: в поле ввода вносится адрес, и после нажатия кнопки происходит чтение HTML сайта. В класс MainActivity.java внесем следующие дополнения:
  1. import android.app.Activity;
  2. import android.os.AsyncTask;
  3. import android.os.Bundle;
  4. import android.view.View;
  5. import android.widget.Button;
  6. import android.widget.EditText;
  7. import android.widget.TextView;

  8. public class MainActivity extends Activity implements View.OnClickListener{
  9. HttpConnect htp;
  10. TextView textView;
  11. EditText editText;
  12. Button enter;
  13. HTTP_Thread mt;
  14. @Override
  15. protected void onCreate(Bundle savedInstanceState) {
  16. super.onCreate(savedInstanceState);
  17. setContentView(R.layout.activity_main);
  18. htp = new HttpConnect();

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

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

  21. enter = (Button) findViewById(R.id.button);
  22. enter.setOnClickListener(this);




  23. }
  24. public void onClick(View v){

  25. mt = new HTTP_Thread(editText.getText().toString());
  26. mt.execute();

  27. }



  28. class HTTP_Thread extends AsyncTask <Void, Void, String>{
  29. String URL;
  30. public HTTP_Thread (String arg){ URL = arg;}

  31. protected String doInBackground(Void... params) {

  32. URL = htp.HttpManager(URL);

  33. return URL;
  34. }

  35. @Override
  36. protected void onPostExecute(String s) {
  37. super.onPostExecute(s);
  38. textView.setText(s);
  39. }

  40. }

  41. } }
Нас тут больше всего интересует класс AsyncTask, он имеет три параметра, в нашем случае это . Последний параметр указывает выходные данные так как мы хотим вернуть строку с HTML кодом. У AsyncTask есть несколько методов которые вызываются в разные периоды работы программы в новом потоке, но обязательный из них это doInBackground(), в нем и закладывается основная работа интересующей задачи. doInBackground() может конфигурироватся по разному, но нам нужно чтоб он возвращал строку с HTML кодом. Строка возвращается, но в метод onPostExecute() который вызывается после завершении задачи в потоке, в этом методе можно свободно обращаться к внешним объектам главной активности, и выводить в текстовые поля строки.

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />