Страницы

Поиск по вопросам

вторник, 16 октября 2018 г.

Как объединить два потока аудио в реальном времени?

Получаю с помощью AudioRecord от диктофона поток аудио в виде массива short array[]. Пробовал этот массив записывать в память с разрешением .pcm. Воспроизвести получилось его на компьютере с помощью программы Audacity указав свойства аудио, так как нет заголовков.
Во время его получения в реальном времени нужно его объединять с фоновой музыкой и записать в файл. (Желательно в mp3). Есть идеи как это можно сделать?


Ответ

В данный момент сделал следующим образом:
Получаю массив байтов 10 секундного фона из wav файла Также wav файл можно просмотреть на компьютере в Hex Editor Neo, в которой можно увидеть значения в десятичной системе исчисления , а так же их значения в кодировки ASCII (Так хранится wav данные используя контейнер RIFF, правда например частота дискретизации не совпадает со значениями в аудио-плеерах, если брать примеры аудио) Настраиваю получение аудио с диктофона с такими же настройками, как и сам аудио файл Далее по необходимости можно прочитать информацию о wav файле согласно его структуре После нахожу среди этого массива, именно поток с аудио данными. Для этого нам нужно найти название секции data которая занимает 4 байта, после нее идут 4 байта в десятичной системе исчисления с размером потока аудио в байтах (у меня этот размер так же не совпадает пока), после них идет массив сигнала. Поиск индекса первого значения с аудио данными:
private int readStreamIndex(byte[] backgroundBytes) { int startIndex = 0; for (int i = 0; i < backgroundBytes.length && i < 100; i++) { if (backgroundBytes[i] == 100 && backgroundBytes[i + 1] == 97 && backgroundBytes[i + 2] == 116 && backgroundBytes[i + 3] == 97) { startIndex = i; break; } }
return startIndex + 8;//4 байта - название сегмента + 4 байта размер сегмента } В следующем шаге складываю к байтам фона, байты аудио с микрофона полученные от AudioRecord и записываю в файл (То есть заголовок объединенного wav файла остается от фонового, но можно и свой указать) Проигрываю wav Вместо того что бы записывать в wav, можно с помощью MediaCodec кодировать в aac и сохранять в файл m4a c помощью MediaMuxer

Задача о кирпичной кладке

Имеется слой кирпичной кладки размером N на M клеток. Один кирпич занимает ровно две смежных клетки. Требуется рассчитать один из вариантов размещения кирпичей в следующем слое кладки.
Ограничение: ни один из кирпичей нового слоя не должен полностью совпадать положением ни с одним кирпичом предыдущего слоя.
Входные данные - матрица NxM, представляющая слой кладки, заполненная парами чисел от 1 до NxM/2, где каждая пара чисел представляет собой один кирпич.
Пример входных данных и пример ответа:

т.е. на входе матрица:
{ { 1, 1, 2, 2, 3, 3, 4, 4 }, { 5, 5, 6, 6, 7, 7, 8, 8 }, { 9, 9, 10, 10, 11, 11, 12, 12 }, { 13, 13, 14, 14, 15, 15, 16, 16 }, }
на выходе:
{ { 1, 2, 3, 4, 5, 6, 7, 8 }, { 1, 2, 3, 4, 5, 6, 7, 8 }, { 9, 10, 11, 12, 13, 14, 15, 16 }, { 9, 10, 11, 12, 13, 14, 15, 16 }, }
Тестовый метод для проверки корректности слоя:
public bool CheckLayers(int n, int m, int[,] current, int[,] previous) { for (int i = 0; i < n; ++i) { for (int j = 0; j < m; ++j) { if (i < n - 1 && current[i, j] == current[i + 1, j] && previous[i, j] == previous[i + 1, j]) return false; if (j < m - 1 && current[i, j] == current[i, j + 1] && previous[i, j] == previous[i, j + 1]) return false; } } return true; }


Ответ

Итак, рассмотрим всевозможные участки 2х2 клетки слоя кладки:

Как видно, для любого участка 2х2 клетки всегда есть возможность подобрать пару кирпичей для установки в следующем слое: т.е. если в участке один из кирпичей расположен "горизонтально" (на самом деле весь слой горизонтальный, но для удобства обозначения я его поместил вертикально), то берем пару "вертикальных" кирпичей, иначе берем пару "горизонтальных". На картинках красными прямоугольниками я обозначил возможное размещение кирпичей в следующем слое. Обратите внимание, если на выбранном участке 2х2 все клетки заняты "половинками", т.е. нет ни одного целого кирпича, то пару кирпичей в следующем слое можно разместить как вертикально, так и горизонтально, т.е. вышеописанный алгоритм работает и для этого случая.
Таким образом, если оба входных значения N и M четные, то слой кладки можно разбить на участки 2х2 клетки и заполнить их по вышеописанному алгоритму. Для четных N и M решение всегда существует и тривиально.
Теперь, рассмотрим случай, когда N или M нечетно. Следует заметить, что если одновременно и N, и M являются нечетными, то мы не можем замостить слой целым количеством кирпичей, т.е. таких входных данных быть не может и этот случай мы не рассматриваем.
Итак, один из размеров слоя кладки нечетный. Рассмотрим случай, когда нечетной является "ширина" M, если нечетна "высота" N, то мы всегда можем транспонировать входные данные и воспользоваться этим же алгоритмом.
"Разобьем" кладку на "горизонтальные" полосы высотой 2 клетки (высота четная, поэтому это возможно) и рассмотрим каждую полосу независимо от других. Для такой полосы мы обязаны разместить хотя бы один из кирпичей вертикально, причем этот кирпич должен стоять в 0, 2, 4, ..., (M-3) или (M-1) вертикали (если у нас вертикальный кирпич расположен в вертикали с нечетным индексом, то будет еще как минимум один (два даже) вертикальный кирпич в вертикали с четным индексом и мы будем рассматривать именно его). Что примечательно - слева и справа от вертикального кирпича окажется четное число вертикалей.
Т.е. для каждой горизонтальной полосы высотой 2 клетки мы просматриваем вертикали с четными индексами и если на какой-то из этих вертикалей не расположен вертикальный кирпич, то мы помещаем вертикальный кирпич в следующем слое, а участки слева и справа от него заполняем по алгоритму для четных N и M. Если же в полосе 2xM все четные вертикали заняты вертикальными кирпичами, то решения нет.
Доказать корректность алгоритма я не могу, но подобрать опровергающий пример входных данных мне не удалось.
Остается только перевести этот алгоритм в код.
Наивная реализация на C#:
class Layer { int[,] array; int n, m; public bool IsTransposed { get; set; } = false; public int N => IsTransposed ? m : n; public int M => IsTransposed ? n : m;
public Layer(int[,] bricks) => (n, m, array) = (bricks.GetLength(0), bricks.GetLength(1), bricks); public Layer(int n, int m) : this(new int[n, m]) { }
public int this[int row, int column] { get => IsTransposed ? array[column, row] : array[row, column]; set => array[IsTransposed ? column : row, IsTransposed ? row : column] = value; }
public Layer GenerateNext() { if (N % 2 + M % 2 == 2) return null; var next = new Layer(N, M); var index = 0; if (N % 2 + M % 2 == 0) { for (int row = 0; row < N; row += 2) for (int column = 0; column < M; column += 2) Fill(row, column); } else { if (N % 2 == 1) IsTransposed = next.IsTransposed = true; for (int row = 0; row < N; row += 2) { bool exist = false; int reservedColumn = 0; for (int column = 0; column < M; column += 2) if (this[row, column] != this[row + 1, column]) { exist = true; reservedColumn = column; break; } if (!exist) return null; next[row, reservedColumn] = next[row + 1, reservedColumn] = ++index; for (int column = 0; column < reservedColumn; column += 2) Fill(row, column); for (int column = reservedColumn + 1; column < M; column += 2) Fill(row, column); } IsTransposed = next.IsTransposed = false; } return next;
void Fill(int r, int c) { if (this[r, c] == this[r, c + 1] || this[r + 1, c] == this[r + 1, c + 1]) { next[r, c] = next[r + 1, c] = ++index; next[r, c + 1] = next[r + 1, c + 1] = ++index; } else { next[r, c] = next[r, c + 1] = ++index; next[r + 1, c] = next[r + 1, c + 1] = ++index; } } }
public override string ToString() { int max = this[0, 0]; for (int row = 0; row < N; ++row) for (int column = 0; column < M; ++column) if (this[row, column] > max) max = this[row, column]; int d = max.ToString().Length; var format = "{0," + d + "} "; var sb = new StringBuilder(); for (int row = 0; row < N; ++row) { for (int column = 0; column < M; ++column) sb.AppendFormat(format, this[row, column]); sb.AppendLine(); } return sb.ToString(); } }
Пример использования:
int[,] bricks = { { 1, 1, 2, 2, 7, 3, 4, 4 }, { 5, 9, 6, 6, 7, 3, 8, 12 }, { 5, 9, 10, 10, 11, 11, 8, 12 }, { 13, 13, 14, 18, 15, 15, 16, 16 } }; var layer = new Layer(bricks); var nextLayer = layer.GenerateNext(); Console.WriteLine(layer); if (nextLayer != null) Console.WriteLine(nextLayer); else Console.WriteLine("Нет решения!");

Интеграционное тестирование .Net приложений

Здравствуйте, мне необходимо реализовать интеграционные тесты настольного .Net приложения, которые будут запускаться после каждого билда. Тестируется бизнес-логика, описан ряд сценариев, которые нужно проверить. Писать юнит-тесты и пытаться замокать все компоненты - не очень здравая идея, нужно тестировать именно приложение. Юнит-тесты уже есть. Запускать тесты хотелось бы автоматически, используя Bamboo или Jenkins, сами тесты писать на C#. Была идея использовать Jmeter, но там только java. Подскажите пожалуйста, есть ли какое-то средство для реализации интеграционных тестов под .net, какой-нибудь аналог JMeter? Насколько правильно реализовывать интеграционные тесты, используя MSTest или NUnit? Как правильно запускать нужное окружение, на котором проводить тестирование?


Ответ

Писать интеграционные тесты, используя MSTest вполне нормально как и NUnut или XUnit это же все лишь фреймворки для выполнения теста как такового. А какой уж это будет тест: Unit, интеграционный, UI, уже дело того, что вы проверяете внутри теста.
По поводу окружения, тут есть разные подходы, например вы можете развернуть MSTest Agent на виртуальных машинах с нужным окружением и подключить их к контроллеру. Для выбора на каком конкретно контроллере вы хотите запустить тест используйте соответствующий .testsettings и .runsettings файлы, у NUnit агентов примерно все так же. При таком подходе код теста выполняется на агенте.
Либо если вы тестируете UI, то можете использовать Selenium Grid, и управлять окружением на уровне Selenium Node. тогда у вас будет один агент например NUnit на нем будет выполняться теста и для работы с UI от будет обращаться к Grid для определенного окружения, а тот уже выбирает свободную ноду.
Резюмирую: Одно окружение устанавливается на все агенты одного контроллера или при другом подходе на все ноды одного грида.
Для выбора окружения нужно выбрать соответствующий контролер или грид.

Алгоритм увеличения изображения, содержащего градиент

Стоит задача: увеличить изображение 100х100 с четырмя каналами, (красный, зелёный, синий, альфа-канал; содержит сильно размытое пятно), до размера 2500х2500, и вывести на заранее подготовленный графический контекст устройства. Фактически кисть, наподобии кистей в графическом редакторе Adobe Photoshop. Проблема состоит как раз в увеличении (ресайзе). Вот исходное изображение (или кисть; показываю только альфа канал, остальное не принципиально): Нарисовано стадартной кистью Photoshop. А вот, что получается после увеличения моим алгоритмом (ещё раз: не обращайте внимания на цвет: он устанавливается рандомом). Я использовал ленийное интерполирование. Вы тоже видите эти шероховатости? Так вот: вся проблема в них. Эти шерховатости находятся в альфа-канале, т.к. цвет у всей кисти однородный. Из-за них, при рисовании линии кистью, образуется страшная грязь. Чтобы понять природу этих неровностей, я решил нарисовать в Photoshop'е полоску шириной 1 пикс., представляющую собой плавный градиент от белого к синему, а потом снова к белому. Далее я увеличил по ширине каждую полоску сначала своим алгоритмом, потом в Photoshop'е: alt text http://plasmon.rghost.ru/38526229/image.png alt text http://rghost.ru/38526272/image.png На первом рисунке опять видно неровности. Вот почему, по моему мнению, возникают неровности:alt text http://rghost.ru/38526320/image.png Здесь по оси x - номер пикселя, y - цвет. Синие точки - исходные, красные - аппроксимированнные линейной интерполяцией. Синий график показывают идеальную интерполяцию сплайнами, но она слишком долго выполняется на компьтере. Так вот, видите ломаный чёрный график линейной интерполяци? Вот эти самые углы ломаной, мне кажется, образуют такие шероховатости.
В Photoshop'е в настройках стоит интерполяция "Bicubic (best for smooth gradients)", но "Linear" и "Bicubic" в Photoshop'е дают одинаковый с моим алгоритмом результат. Так что можно считать, что "Bicubic (best for smooth gradients)" в Photoshop'е изображение сначала увеличивается линейной интерполяцией а потом применяется банальный фильтр размытия.

Итак. Все фильтры размытия которые я нашёл в интернете работают крайне медленно (в т.ч. мой). Скорее всего, Photoshop использует графический ускоритель. Но возможно ли как-нибудь реализовать мою задачу только с помощью CPU? И чтобы работало максимум секунду. Жду ваших предложений.

//горизонтальная интерполяция между исходными пикселами for y:=1 to setedbrush.h_orig do for x := 1 to setedbrush.w_orig-1 do begin x_0:=round(x*k); x_x:=x_0+1; x_1:=round((x+1)*k); y_y:=round(y*k);
while(x_x<>x_1) do begin cl:=round((temp[x_1,y_y].r-temp[x_0,y_y].r)/(x_1-x_0))+temp[x_0,y_y].r; temp[x_x,y_y].r:=cl;
cl:=round((temp[x_1,y_y].g-temp[x_0,y_y].g)/(x_1-x_0))+temp[x_0,y_y].g; temp[x_x,y_y].g:=cl;
cl:=round((temp[x_1,y_y].b-temp[x_0,y_y].b)/(x_1-x_0))+temp[x_0,y_y].b; temp[x_x,y_y].b:=cl;
cl:=round((temp[x_1,y_y].a-temp[x_0,y_y].a)/(x_1-x_0))+temp[x_0,y_y].a; temp[x_x,y_y].a:=cl;
inc(x_0); inc(x_x); end; end;
Здесь x_0 - крайний известный левый пиксел, соответственно x_1 - правый; x_x - расчитываемый пиксел, y_y - текущая строчка. Формула на основании подобия прямоугольных треугольников.

Результат уже лучше, чем прежде. Но при рисовании линии - грязь. Чтож, придётся сглаживать... Хотя, может быть, дело в картинке. alt text http://rghost.ru/38553575/image.png


Ответ

Вы предполагаете почему они возникают, а вы постройте реальный график изменения яркости вдоль одной какой-нибудь линии.
Cледующий пиксел рассчитывается через предыдущий, так будет накапливаться ошибка. Попробуйте считать по формуле относительно крайних точек, понятно что так ещё одно умножение потребуется, но будет точнее. Сначала проверьте лучше ли станет интерполяция, если считать по формуле:
round((temp[x_1,y_y].a-temp[x_0,y_y].a)*(x_x-x_0)/(x_1-x_0)) + temp[x_0,y_y].a
и x_0 во внутреннем цикле не менять

Анимированные действия в Gmail

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

Получается что к ListView через addHeaderView добавили некий хедер. Но, вопрос, как они отслеживают что идет утягивание списка вниз и в этот момент перекрывается ActionBar текстовым полем с надписью "Пролистните вниз, чтобы обновить"? Поиск по ключевым словам в гугле особого успеха не дал.
Второй вопрос: как сделано удаление Item'a через слайд в сторону?

Я реализовал эту функцию через android-swipetodismiss, но это не оригинальное решение команды Google и оно имеет несколько недостатков в отличии от реализации Gmail


Ответ

могу посоветовать только по первому поглядеть вот это либу https://github.com/chrisbanes/ActionBar-PullToRefresh у самого прикрутить не вышло быстро, а так как не было острой необходимости, то и бросил.

Проблема с переполнением памяти при загрузке картинок с интернета Universal Image Loader

Итак. Есть задача, есть отдельная активити для ленты новостей. Т.е. пользователь прокручивает ленту и на экране выводятся посты с картинками и текстом. Всё это грузится с определённого сайта и пользователь может листать ленту бесконечно прокрутив 100-200 или даже 1000 постов с картинками.
Я решил использовать для этих целей UIL, так как из описания понял, что она не только загружает картинки с интернета, но и управляет кэшем и сама выгружает из памяти то что не видно в данный момент на экране.
Но проблема в том, что после загрузки 30-40 картинок дальше ничего не грузилось (просто вместо картинок была заглушка "Ошибка". А в логе писалось про outOfMemory. Я решил попробовать другие библиотеки, в том числе и Picasso, Fresco. Но проблема оставалась.
Изначально алгоритм был таков - посты загружались пачками по 10 штук сразу в контейнер (разметку для каждого поста я брал из отдельного лайоута и подключал при помощи LayoutInflater). Потом подумав, что может проблема в этом способе, я решил использовать RecycleView с адаптером. Но это тоже не помогло. Теперь я вообще переписал всё с нуля и для чистоты эксперимента решил загружать на экран только картинки, без лишней разметки, адаптеров, инфлаттеров и прочего. Только картинки, которые грузятся в динамически создаваемые ImageView.
И всё равно проблема та же. Попробовав настройки UIL, я вроде бы добился более менее приемлемого результата — теперь уже грузится не 30-40 картинок, а 100-130. Но этого мало, надо чтобы можно было грузить бесконечное число картинок.
Если я правильно понимаю, то картинки просто не удаляются из памяти, когда я дальше прокручиваю ленту и из-за этого память переполняется. На одном форуме мне сказали, что подобные библиотеки (я правда в тот момент спрашивал про Picasso, но суть та же) не предназначены для решения подобных задач и надо реализовывать выгрузку и загрузку картинок из памяти самостоятельно.
Скажите, что я делаю не так?
package com.freescribbler.freescribbler;
import android.app.Activity; import android.content.SharedPreferences; import android.graphics.Bitmap; import android.os.AsyncTask; import android.os.Bundle; import android.util.Log; import android.view.View; import android.widget.Button; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.RelativeLayout; import android.widget.Toast; import com.nostra13.universalimageloader.cache.disc.impl.UnlimitedDiskCache; import com.nostra13.universalimageloader.cache.disc.naming.HashCodeFileNameGenerator; import com.nostra13.universalimageloader.core.DisplayImageOptions; import com.nostra13.universalimageloader.core.ImageLoader; import com.nostra13.universalimageloader.core.ImageLoaderConfiguration; import com.nostra13.universalimageloader.core.assist.ImageScaleType; import com.nostra13.universalimageloader.core.assist.QueueProcessingType; import com.nostra13.universalimageloader.utils.StorageUtils; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import java.io.File; import java.util.ArrayList;
public class HomeActivity extends Activity { serverAPI serverAPI1; //Класс запросов к серверу SharedPreferences myPref; //Настройки доступа к серверу final String USER_CONNECTID = "user_connectid"; //ID final String USER_TOKEN = "user_token"; //Токен int lastPost = 0; Boolean morePostLoad = false; int Counter; //Счётчик выведенных постов ImageLoader imageLoader; int imgCount=0; ArrayList iv = new ArrayList();
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_home);
serverAPI1 = new serverAPI(); //инициализируем класс loadLoginData();//загружаем данные доступа к серверу Counter = 0; //инициализируем счётчик
imageLoader = ImageLoader.getInstance(); // Получили экземпляр imageLoader.init(ImageLoaderConfiguration.createDefault(HomeActivity.this)); File cacheDir = StorageUtils.getCacheDirectory(this); ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(this) .memoryCacheExtraOptions(480, 800) // default = device screen dimensions //.diskCacheExtraOptions(480, 800, null) .threadPoolSize(3) // default .threadPriority(Thread.NORM_PRIORITY - 2) // default .tasksProcessingOrder(QueueProcessingType.FIFO) // default .denyCacheImageMultipleSizesInMemory() //.memoryCache(new LruMemoryCache(5 * 1024 * 1024)) //.memoryCacheSize(5 * 1024 * 1024) .memoryCacheSizePercentage(13) // default .diskCache(new UnlimitedDiskCache(cacheDir)) // default .diskCacheSize(20 * 1024 * 1024) //.diskCacheFileCount(100) .diskCacheFileNameGenerator(new HashCodeFileNameGenerator()) // default .writeDebugLogs() .build();
//Picasso.with(HomeActivity.this).setIndicatorsEnabled(true); //Включаем дебаг у пикассо new AsyncTaskGetPosts().execute();// Запускаем асинхронное выполнение загрузки данных с сервера, с последующим выводом постов
Button loadMore = (Button) findViewById(R.id.loadMoreButton); loadMore.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (morePostLoad) { new AsyncTaskGetPostsMore().execute(); }
} }); }
//Загружаем данные для запросов к серверу void loadLoginData() { myPref = getSharedPreferences("myPref", MODE_PRIVATE);
//Проверяем, существует ли токен if (myPref.contains(USER_TOKEN)) { serverAPI1.token = myPref.getString(USER_TOKEN, ""); serverAPI1.connectid = myPref.getString(USER_CONNECTID, ""); Log.e("Токен существует", serverAPI1.token); } else { serverAPI1.token = null; serverAPI1.connectid = null; Toast toast = Toast.makeText(getApplicationContext(), "Пользователь не залогинен", Toast.LENGTH_LONG); toast.show(); Log.e("Токена нет", "null"); } }
//Здесь мы получаем от сервера картинки public class AsyncTaskGetPosts extends AsyncTask { @Override //Создаём асинхронную задачу в фоне protected String doInBackground(String... params) { return serverAPI1.loadPosts(30); //Запрашиваем 30 постов }
@Override //То что выполнится по завершении фонового процесса protected void onPostExecute(String result) { super.onPostExecute(result); //вызываем метод, где обрабатываем JSON и выводим картинки makePosts(result); morePostLoad = true; } }
//Здесь мы получаем от сервера картинки public class AsyncTaskGetPostsMore extends AsyncTask { @Override //Создаём асинхронную задачу в фоне protected String doInBackground(String... params) { return serverAPI1.loadPosts(20, lastPost); //Запрашиваем 20 постов }
@Override //То что выполнится по завершении фонового процесса protected void onPostExecute(String result) { super.onPostExecute(result); //вызываем метод, где обрабатываем JSON и выводим картинки makePosts(result); morePostLoad = true; } }
//Метод вывода постов void makePosts(String postData) { try { //Создаём объект JSON JSONObject jsnResp = new JSONObject(postData); JSONArray jposts = jsnResp.getJSONArray("data"); lastPost = jsnResp.getInt("lastpost"); //Цикл вывода картинок for (int i = 0; i < jposts.length(); i++) { JSONObject curentPost = new JSONObject(jposts.getString(i));
String picURL; //переменная для хранения ссылок на картинки //Узнаём дал ли сервер картинку if (curentPost.getString("pics").equals("-1")) { //При негативном результате ссылка будет null Log.d("PICS", "is -1"); picURL = null; } else { //Позитивный результат JSONArray picsInPost = curentPost.getJSONArray("pics"); JSONObject picTest = new JSONObject(picsInPost.getString(0)); //Вытаскиваем ссылку для картинки picURL = picTest.getString("picname"); //Создаём ImageView Log.d("IMGCOUNT", String.valueOf(imgCount));
iv.add(new ImageView(HomeActivity.this));
LinearLayout lL = (LinearLayout) findViewById(R.id.linearL); LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams( RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
lL.addView(iv.get(imgCount), lp); //Вставляем его в нужный Layout
//Загружаем картинку из интернета в ImageView /*Picasso.with(HomeActivity.this) .load(picURL) //ссылка .placeholder(R.drawable.loading) //картинка загрузки .error(R.drawable.error) //картинка ошибки .into(iv); //сюда мы грузим картинку*/ DisplayImageOptions options = new DisplayImageOptions.Builder() .showImageOnLoading(R.drawable.loading) // resource or drawable .showImageOnFail(R.drawable.error) // resource or drawable //.resetViewBeforeLoading(false) // default .cacheInMemory(true) // default .cacheOnDisk(true) // default .imageScaleType(ImageScaleType.EXACTLY) // default .bitmapConfig(Bitmap.Config.RGB_565) // default .build(); imageLoader.displayImage(picURL, iv.get(imgCount), options); imgCount++; } Log.d("COUNTER", String.valueOf(Counter)); Counter++; } } catch (JSONException e) { e.printStackTrace(); } } }


Ответ

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

Android Как активировать ночной режим

Реализую в приложении "ночной" режим. Как заставить Google maps API v2 работать в "Дарк" режиме?


Ответ

В Google Maps насколько я знаю нет предустановленого ночного режима. То что вы видите используя Google Maps достигается установкой стилей карты с указанием какие элементы какими цветами закрашивать.
вот стили для ночного режима в формате json
[ { "featureType": "all", "elementType": "geometry", "stylers": [ { "color": "#242f3e" } ] }, { "featureType": "all", "elementType": "labels.text.stroke", "stylers": [ { "lightness": -80 } ] }, { "featureType": "administrative", "elementType": "labels.text.fill", "stylers": [ { "color": "#746855" } ] }, { "featureType": "administrative.locality", "elementType": "labels.text.fill", "stylers": [ { "color": "#d59563" } ] }, { "featureType": "poi", "elementType": "labels.text.fill", "stylers": [ { "color": "#d59563" } ] }, { "featureType": "poi.park", "elementType": "geometry", "stylers": [ { "color": "#263c3f" } ] }, { "featureType": "poi.park", "elementType": "labels.text.fill", "stylers": [ { "color": "#6b9a76" } ] }, { "featureType": "road", "elementType": "geometry.fill", "stylers": [ { "color": "#2b3544" } ] }, { "featureType": "road", "elementType": "labels.text.fill", "stylers": [ { "color": "#9ca5b3" } ] }, { "featureType": "road.arterial", "elementType": "geometry.fill", "stylers": [ { "color": "#38414e" } ] }, { "featureType": "road.arterial", "elementType": "geometry.stroke", "stylers": [ { "color": "#212a37" } ] }, { "featureType": "road.highway", "elementType": "geometry.fill", "stylers": [ { "color": "#746855" } ] }, { "featureType": "road.highway", "elementType": "geometry.stroke", "stylers": [ { "color": "#1f2835" } ] }, { "featureType": "road.highway", "elementType": "labels.text.fill", "stylers": [ { "color": "#f3d19c" } ] }, { "featureType": "road.local", "elementType": "geometry.fill", "stylers": [ { "color": "#38414e" } ] }, { "featureType": "road.local", "elementType": "geometry.stroke", "stylers": [ { "color": "#212a37" } ] }, { "featureType": "transit", "elementType": "geometry", "stylers": [ { "color": "#2f3948" } ] }, { "featureType": "transit.station", "elementType": "labels.text.fill", "stylers": [ { "color": "#d59563" } ] }, { "featureType": "water", "elementType": "geometry", "stylers": [ { "color": "#17263c" } ] }, { "featureType": "water", "elementType": "labels.text.fill", "stylers": [ { "color": "#515c6d" } ] }, { "featureType": "water", "elementType": "labels.text.stroke", "stylers": [ { "lightness": -20 } ] } ]
Вам необходимо положить их в ресурсы(/res/raw/style_json.json) и вызвать GoogleMap.setMapStyle()
googleMap.setMapStyle(MapStyleOptions.loadRawResourceStyle(this, R.raw.style_json));
Есть подробная инструкция по адресу: https://developers.google.com/maps/documentation/android-api/styling

Голосовое управление

Всем добра! Ребят, нужна Ваша помощь. Я хочу добавить голосовое управление в приложении. Возможно ли это вообще? Если да, то что использовать и с чего начать вообще? Буду рад любым советам!


Ответ

Попробуйте начать с Google Cloud Speech API
Есть примеры использования на Java
На последнем Google I/O был доклад, на котором показывали робота, управляемого голосом. Ну и вообще на YouTube туториалов уже много.

ListView с возможностью перетаскивания элементов

Вот увидел такое ListView и мне нужно сделать аналогичное. Что-бы пользователь мог задавать порядок элементов списка путем перетаскивание. Есть ли готовое решение или может как-то можно самому написать?


Ответ

Библиотека https://github.com/bauerca/drag-sort-listview сделана именно для вашего функционала. Она достаточно популярная и проверенная.
Интерфейс у неё полностью кастомизируемый. Выглядит список примерно так :

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

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


Ответ

Добываете сервер. Пишете серверную часть своей игры. Делаете match-making для игроков (подбор пар). Например, все игроки, по нажатию своих кнопок, рапортуют серверу что готовы играть, а он подбирает пары между ними. Когда есть пара, создаете на сервере для нее "комнату", это типа логический мини-серверок для игры, где игроки могут обмениваться данными друг с другом (ходы и т.п.). Выдаете игрокам доступ в комнату. Игроки заходят (если не передумали) и начинают игру.

Медицинское разрешение на приложение

Есть приложение для расчета дозы инсулина (диабет) на Android. Нужно ли получать медицинское разрешение на его распространение в России, в частности в российском сегменте GooglePlay?


Ответ

Согласно постановлению Правительства РФ от 16.04.2012 N 291 "Положение о лицензировании медицинской деятельности" в перечне работ (услуг), составляющих медицинскую деятельность нет ничего похожего на вашу деятельность. Это же просто автоматизация ручного подсчета, если правильно понимаю.

Что такое extern “C++”?

Знаю, что extern "C" используют для отключения декорирования имён, что часто применяется для экспортируемых функций.
А что делает extern "C++"?

Заголовочный файл corecrt.h содержит
//-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // // C++ Secure Overload Generation Macros // //-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ #ifndef RC_INVOKED #if defined __cplusplus && _CRT_SECURE_CPP_OVERLOAD_SECURE_NAMES
#define __DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_0(_ReturnType, _FuncName, _DstType, _Dst) \ extern "C++" \ { \ template \ inline \ _ReturnType __CRTDECL _FuncName(_DstType (&_Dst)[_Size]) _CRT_SECURE_CPP_NOTHROW \ { \ return _FuncName(_Dst, _Size); \ } \ }
Макрос дальше используется в stdio.h
__DEFINE_CPP_OVERLOAD_SECURE_FUNC_0_0( char*, gets_s, char, _Buffer)
для объявления функций с массивом фиксированного размера:

Компилятор Visual Studio 2015.


Ответ

Для связи с другими языками программирования в C++ введена спецификация, называемая linkage specification
Из стандарта C++ (7.5 Linkage specifications)
1 All function types, function names with external linkage, and variable names with external linkage have a language linkage. [ Note: Some of the properties associated with an entity with language linkage are specific to each implementation and are not described here. For example, a particular language linkage may be associated with a particular form of representing names of objects and functions with external linkage, or with a particular calling convention, etc. —end note ] The default language linkage of all function types, function names, and variable names is C++ language linkage. Two function types with different language linkages are distinct types even if they are otherwise identical.
Стандартом поддерживается два литерала, используемых для связи с другими языками. Это
extern "C++"
и
extern "C"
Другие литералы в этой спецификации могут поддерживаться самостоятельно реализациями компиляторов.
Например, могут в программе на языке C++ использоваться имена (например, функции) из языка программирования Fortran. В этом случае эти имена могут предваряться спецификацией
extern "FORTRAN"

Распределенные демоны

Всем привет!
Есть вопрос, какие есть способы реализовать отказоустойчивые демоны, в которых в один момент времени может работать всего один экземпляр?
Предположим, есть набор задач, который запускается в определенное время:
Каждый час генерировать метрику для проекта Каждые 10 минут выполнять обновление записей в таблице Каждые 15 минут удалять записи из таблицы Каждые 20 минут делать увеличение какого-то числа в определенных записях в таблицах
Это какой-то абстрактный набор задач, который выполняется в фоновом режиме на проекте. Указанными задачи управляет главный поток, который решает когда им запускаться. Можно считать это обычным кроном.
Задачи, являются минимальной сущностью и не требуют распараллеливания. Предположим, генерация метрики это выполнение десятков запросов и агрегирование результатов по определенным формулам, но все это происходит за 1-2 секунды. Это можно распараллелить, но в этом нет необходимости.
Предположим, данные воркеры работают на одном сервере, но вдруг данный сервер может упасть ночью, когда все администраторы и разработчики будут спать. Естественно системы мониторинга начнут всех будить, но хотелось бы не нарушать сон людей и чтобы вычисления моментально перешли на другой сервер, если один сервер упал.
Естественно, задачи не должны запускаться одновременно на двух серверах, так как будут проблемы с согласованностью данных, например при выполнении задачи 4.
В идеале, я представляю себе решение, когда есть минимум демон в двух экземплярах на разных серверах, который ставят задачи при наступлении времени в какую-нибудь очередь типа AMQP, а n-демонов воркеров на других серверах берут их из очереди и выполняют. Тут есть проблема, как синхронизировать 2 демона, которые ставят эти задачи, чтобы они не поставили их два раза..

Я так понимаю есть следующие способы синхронизации процессов на разных серверах и дата-центрах:
1) Синхронизация конкурирующих процессов через какое-то кластерное хранилище, например Redis. Ставится блокировка, другой демон (или задача) не запускается, пока установлен флаг блокировки. Читал еще про способы блокировки демонов в кластере RabbitMQ, когда процесс при запуске ставит эксклюзивный worker с определенным названием на очередь для подобных действий и другой демон пытается установить свою блокировку, и если не получается, закрывается до лучших времен.
2) Автоматический запуск другого экземпляра демона или задачи через систему мониторинга в случае падения основного демона.
3) Явная проверка существования другого демона (задачи) по открытому порту и распределение весов. В демон прописываем открытый порт у другого демона и смотрим, работает ли он сейчас и отвечает на запросы? Если нет, забираем корону и выполняем задачи(у). Такой аналог VRRP.
Есть ли более элегантные способы решения задачи? Или какой из этих способов наиболее оптимален и чаще всего используется по вашему опыту? Может есть какие-то распределенные планировщики? А может есть какие-нибудь книги по архитектуре подобных демонов?


Ответ

TL; DR: избежать дупликации работы нельзя, поэтому необходимо запускать идемпотентные задачи. Если необходима синхронизация работы, необходимо брать распределенное решение, позволяющее достигать консенсуса, например etcd, consul, riak, cassandra, zookeeper. Redis и RabbitMQ использовать нельзя, потому что они к консенсусу не имеют абсолютно никакого отношения. Вероятнее всего, если вы имеете дело с несколькими узлами, у вас либо кто-то назначает один узел главным (и тогда просто надо выполнять все задачи на нем), либо у вас есть какой-то оркестратор, в котором вероятнее всего есть понятие one-time job и который стоит использовать, чтобы не набивать шишки самостоятельно.
Short story long
Перед ответом на этот вопрос сначала стоит обозначить, с чем вообще планируется борьба.
В распределенных системах есть несколько основных проблем, которые всплывают практически всегда. Здесь упоминается "падение сервера" и "работа двух воркеров над одной задачей одновременно". Первая на самом деле гораздо шире и называется разделением сети (network partition) и обозначает просто то, что связь с узлом распределенной системы потеряна. Это не значит, что узел прекратил работу или прекратил работу насовсем - это может быть перезагрузка сетевого оборудование, сборка мусора в одном из передаточных звеньев или на узле или просто товарищ майор не выдержал просмотра трафика в реалтайме и вышел пока покурить. При этом разделение сети может носить совсем причудливые формы, вроде того, что трафик от А к Б не идет, а наоборот - идет, или что А и Б свободно общаются с В, но друг с другом - никак. Ровно здесь все три предлагаемых варианта отваливаются - если работающий воркер не может продлить блокировку, если сервис мониторинга не может увидеть рабочий процесс, если другой рабочий процесс не может увидеть первый рабочий процесс - это все не значит, что работа не продолжается. И не значит, что она не оборвалась. Это просто состояние неизвестности.
Вторая проблема обычно характеризуется как "византийская проблема" - возможность одного из узлов распределенной системы отдавать некорректную информацию, преднамеренно или случайно, конкретно в данном случае это будет означать выдачу двух одинаковых задач (в то время как система не имеет права этого делать).
Кроме вышеописанных проблем необходимо еще упомянуть типы доставки сообщений. В идеальном мире существует три режима доставки - at-most-once, exactly-once, at-least-once. К сожалению, процесс приема и подтверждения сообщения - это две операции, которые не могут сложиться в одну атомарную операцию, и из-за этого режим exactly-once технически невозможен даже на холостом ходу: разделение сети всегда может произойти в тот момент, когда узел принял сообщение, но не подтвердил его (при этом не имеет значения, подтвердил он просто прием сообщения, или провел все необходимую обработку над ним - важно то, что система теперь в состоянии коробки Шредингера, обработка либо запущена, либо нет, но об этом нельзя узнать до восстановления сети, а время этого восстановления неизвестно). Поэтому распределенная система может либо доставить сообщение не более одного раза, либо доставлять его до тех пор, пока не получит подтверждение.
Здесь уже должно быть видно, что идеальную ситуацию - когда ровно один воркер получает сообщение, отрабатывает его, и успешно завершает его - реализовать технически невозможно. Пока воркер работает исправно, все хорошо; как только он падает в процессе выполнения задачи - система повисла в неконсистентном состоянии. В этот момент никто не знает, была ли выполнена задача (может, воркер просто не смог отправить подтверждение, либо наоборот, рухнул сразу после приема), поэтому остается только либо оставить все как есть, либо отправить задачу на новое выполнение. При этом если с встреча с разделением сети - это не самая частая штука, которую большинство компаний готовы перетерпеть, то выбрасывающий при обработке задачи исключение воркер - это повседневная реальность недоработок, в которой придется залезать внутрь и править состояние системы руками.
Но, конечно, решение у этой проблемы есть. Если задача будет идемпотентна и атомарна, либо будет состоять из последовательности атомарных шагов, то ее можно будет запускать произвольное количество раз, не опасаясь за результат. Фактически, это просто следование ACI(D) 2.0. Если же задачу невозможно сделать атомарной - например, если один шаг не может включать в себя менее двух сайд-эффектов (нельзя одновременно отправить письмо и записать подтверждение об отправке в базу - всегда есть вероятность, что одна операция провалится, а вторая нет) - то можно попробовать минимизировать эти последствия.
Проще всего это будет пояснить на примере. Можно развить задачу из самого вопроса: пусть сервис в качестве рекламной акции в конце каждого месяца выбирает двадцать случайных пользователей и возвращает им стоимость самой дорогой покупки. Конечно, финансовая сторона проекта будет не в восторге, если внезапно компенсируется в два раза больше, чем планировалось, поэтому необходимо гарантировать однократное выполнение задачи. В этом случае задачу можно разбить на следующие этапы:
Выборка двадцати случайных пользователей с историей покупок Сохранение этого результата в базе данных по заранее известному первичному ключу / ключам. Если ключи существуют - работает кто-то другой, прекратить работу, либо работать в параллель. Открытие транзакции на возврат денег для каждого пользователя и запись идентификатора этой транзакции в ту же базу данных. Если идентификатор уже существует - кто-то работает в параллель, отменить транзакцию. Итерация по всем пользователям и закрытие каждой незакрытой транзакции
Вне зависимости от того, сколько воркеров работают параллельно, пока они заранее знают нужные ключи в базе данных, они не могут помешать друг другу - ну, до тех пор, пока алгоритм не подвержен византийским ошибкам. Фактически, это реализация примитивной журналируемой системы - если приложение такое позволяет, можно писать идентификаторы транзакций до их создания и просто делить такую задачу на "план" и "исполнение".
Как было сказано выше, такое не всегда возможно - например, та же рассылка писем. В этом случае имеет смысл уменьшить количество работы, разбивая пользователей на N шардов, обрабатывая каждый шард как отдельный таск. В этом случае максимальная цена ошибки - размер шарда, отталкиваясь от которого и стоит проектировать задачу.
Конечно, для этого всего бывают нужны внешние средства синхронизации. Обычно можно использовать ту же базу данных, в которую пишет приложение - большинство решений, даже распределенных, поддерживают CAS-операции, на основе которых можно построить ограниченную по времени блокировку (пока воркер жив, он постоянно обновляет запись блокировки с указанием нового времени истечения). Самые известные инструменты, которые могут позволить это решить - это Riak, Cassandra, Zookeeper, Consul, Etcd. Что до реплики по поводу других означенных инструментов:
RabbitMQ - это сервис очередей, и в то время как он действительно поддерживает работу в режиме кластера и действительно на нем можно реализовать забавную схему блокировки (до тех пор, пока сообщение-синхронизатор не будет проглочено упашим приложением), он на самом деле не дает никаких гарантий по поводу консенсуса и без особых проблем может несколько раз доставить одно и то же сообщение, разработчики прямо предупреждают о split-brain. Кроме того, остается проблема с двумя режимами доставки (at-least-once / at-most-once), которые не могут гарантировать, что сообщение получит ровно один узел, поэтому сам по себе - без атомизации/идемпотентизации задач - он никак делу не поможет. Доставить до воркеров сообщение о том, что задача готова к исполнению - это легко.
Redis - это невероятный переоцененный инструмент, получивший свою популярность просто за счет удобного интерфейса, являющийся едва ли не худшим инфраструктурным выбором среди всех своих аналогов. Помимо всех остальных проблем, у него так же прямо прописано в документации There is always a window of time when it is possible to lose writes during partitions, что, в общем, является не единственным способом потерять данные в этой волшебной системе, а потеря данных в данном кейсе ведет к дубликации ответственных за задачу хостов и самой работы.
И, наконец, то, с чего стоило начинать ответ. Если вы добрались до распределенных систем, то у вас с большой вероятностью уже есть либо инфраструктура, выбирающая мастер-сервер, либо некоторый оркестратор (e.g. Kubernetes), в котором наверняка есть понятие one-time job в этом случае все проблемы по поводу консенсуса стоит переложить на плечи этих инструментов. В то же время, оркестратор сам будет следить за тем, чтобы процессы ваших воркеров были запущены. Это, конечно, не спасет от невозможности гарантии однократного выполнения задачи, но возведение инфраструктуры просто для запуска задач - тоже тот еще геморрой.
Если же вы все-таки хотите управлять ставящими задачи демонами самостоятельно, то вас спасет вышеупомянутая блокировка через consul/etcd/etc.
Мне правда надо это все держать в голове перед разработкой приложения?
Следует. На самом деле, как и было сказано, то же разделение сети - штука, с которой вы обязательно встретитесь, но не обязательно большой число раз. Если это экономически невыгодно - следует проанализировать вместе с руководителями возможные риски и уменьшить объем работы. Тем не менее, провал фонового обработчика по вине разработчика - практически ежедневная ситуация, поэтому я бы советовал все-таки держаться зеленой дорожки. Если по каким-то причинам не хотите этого делать - можно раздавать задачи через брокер сообщений (RabbitMQ в нормальных условиях должен корректно отрабатывать однократную посылку сообщения, пока держится TCP-коннект к клиенту) и делать дополнительную синхронизацию на выполнение задачи через один из вышеописанных инструментов.

Правильная организация многопоточной работы Java

Предисловие
Работаю с технологией longpoll-инга. То есть суть работы заключается в том, что я делаю запросы к определенному серверу, и, в случае наличия обновлений, сервер возвращает мне их список, если нет - через определенное время мне возвращается пустой ответ. Если сказать немного конкретнее - я работаю с VK API и личными сообщениями, поэтому время обработки каждого обновления из списка крайне критично.
Проблема
Когда я получаю большой список обновлений, они, естественно, обрабатываются каждое по порядку. Их может быть до 256 за запрос, а запросов я могу делать до 3 в секунду как минимум, итого - 750 обновлений каждую секунду вполне себе возможный вариант. Допустим, время обработки одного обновления (которое оповещает о входящем сообщении) занимает десятую часть секунды. Потом вспоминаем об общем их количестве и понимаем, что, в таком случае, до следующего сообщения очередь дойдет очень нескоро. Нас это, конечно же, не устраивает.
Как я пытался эту проблему решить
Естественно, подумал я, логичнее всего будет отдавать обработку всего этого в новый поток, дабы каждое новое сообщение обрабатывалось мгновенно после получения, а значит задержек быть не должно. Изначально я без задней мысли написал везде new Thread(() -> handle(...)).start(); и думал, что проблем теперь не будет. Лишь потом я задумался, что в таком случае на каждое обновление будет создаваться новый анонимный поток, и таких потоков уже может быть по 750 штук каждую секунду.
Изначально (первые несколько минут) все работало отлично, затем задержки стали расти и расти, в итоге дойдя до десятков минут. Вполне логично было предположить, что где-то происходят косяки, засоряется память, сеть и всё прочее, видимо, потоки сами не очень-то и хотели закрываться сразу после выполнения обработки события. Я подключил профайлер, ничего особого не увидел, как ни странно - по нагрузке на память, процессор и прочее, мои классы и объекты были где-то далеко в низу, а в топе были char[] и String, с непонятными кракозябрами в содержании. А запущенных потоков было всего не более 150, и созданных объектов не более 3 миллионов.
Дабы исправить создание огромного количества "беспризорных" потоков, я организовал действие примерно так: я делаю запрос к longpoll-серверу, получаю кучу обновлений, отдаю их обработчику. Обработчик - класс, наследующийся от потока, который просто на вечном цикле берёт обновления из очереди и обрабатывает их. По сути, тут всего один поток, работающий параллельно с главным, и я просто убрал задержки между запросами для получения обновлений, однако они всё также обрабатываются друг за другом. Как я это организовал (исходный код) можно увидеть здесь - в классе LongPoll само взаимодействие с сервером, в классе UpdatesHandler непосредственно обработка.
Вопрос
Как лучше в данном случае организовать работу или хотя бы отследить, из-за чего случаются проблемы? В логе всё хорошо, веду полное логгирование через log4j.
Основная проблема заключается в том, что спустя несколько минут, обработка сообщений начинает занимать неприлично большое количество времени, хотя изначально при запуске всё работает как часы. Даже нагрузка не так влияет на это, дело тут в чём-то другом, и в чём - я не могу пока понять.
Мне предложили попробовать executor service, который, вроде бы, создает пул потоков, сам ими управляет и переиспользует, но я не знаю, поможет ли это и вообще в этом ли проблема? И если использовать его всё-таки, то как правильнее поступить - создать 750 потоков, которые будут переиспользоваться? А не многовато ли? А если нет, то какая разница, один поток или десять будут обрабатывать сотни обновлений, пусть будет задержка не в минуту, а немного меньше, это не тот результат, который нужен.
Желаемый результат
Чтобы обработка каждого обновления проходила асинхронно, и чтобы каждое обновление не ожидало окончания обработки предыдущего, также и чтобы запрос за новым списком обновлений не ожидал окончания обработки всех предыдущих обновлений. Но необходимо, чтобы не было потрачено огромное количество ресурсов, которые ограничены, и чтобы был учтен большой объем обновлений, и чтобы работало раз и на всё время стабильно :) Проблема ещё в том, что обработка одного сообщения может занять как сотую часть секунды, так и секунд 5 в некоторых случаях, поэтому сделать всего потоков 10 не будет смысла.


Ответ

Как я указал в самом вопросе, мне посоветовали попробовать ExecutorService, и он действительно решил все проблемы.
Логика решения была примерно такой: простая итерация по списку полученных событий времени не занимает, а вот обработка некоторых (сообщений) занимает, и порой немало.
Значит, нужно пробегаться по массиву событий, и, если текущее событие оповещает о сообщении, отдавать его обработчику в новом потоке.
Можно было бы использовать FixedThreadPool, но тогда мне было не очень понятно, сколько потоков было бы необходимо создать и сколько их вообще нужно.
Как подсказала данная статья, есть ещё один хороший вариант — использовать CachedThreadPool. Он создаёт новые потоки, только если текущие заняты, и затем сам их подчищает. Таким образом, большое количество потоков будет создано только в случае большого количества сообщений, которые не успевали бы обрабатываться, но по окончании их обработки, все новые потоки будут "убиты".
Конкретная реализация:
Используем: ExecutorService service = Executors.newCachedThreadPool(); Отдаём наше "задание", которое требует обработки в новом потоке:
service.submit(() -> { // код, который будет выполнен в новом потоке });
Всё, вот так просто всё оказалось. Проверил на практике, среднее количество запущенных потоков стало равно 7-8, а в максимуме доходило до 30, а это вполне приемлемо.

Две строки в элементе меню

Необходимо динамически добавлять информацию о пользователе (имя + email) в элемент меню. Информация должна быть отображена в две строки в одном элементе. Уже пытался реализовать так:
android:lines="2" android:inputType="textMultiLine" android:maxLines="4" android:singleLine="false" android:text="First line
Second line"?
И так:
System.getProperty ("line.separator")
,
string = string.replace("\\
", System.getProperty("line.separator")); (Html.fromHtml("text before break
text after break").toString());
не все безрезультатно. Буду оченьблагодарен за любую подсказку или помощь!


Ответ

После нескольких дней поисков решения, пришел к выводу, что в options menu это сделать не представляется возможным. И лучшей (для меня) альтернативой было использование Popup Window. Конечно придется повозиться с разметкой layout'a, но если есть крайняя необходимость (как в моем случае), то это отличное решение.

Обеспечение строгой гарантии безопасности

Прохожу курс по C++ и пытаюсь ответить на такой вопрос:
Если у класса, объекты которого хранятся в стеке, есть небросающие перемещающие методы, то можно реализовать такой возвращающий pop со строгой гарантией безопасности исключений (отметьте все верные утверждения)
Обеспечить строгую гарантию безопасности для метода pop, который возвращает выталкиваемый элемент, не получается из-за возможного исключения при копировании возвращаемого объекта. Если у класса, объекты которого хранятся в стеке, есть перемещающий конструктор, то исходная реализация (см. степ 10) обеспечивает строгую гарантию безопасности исключений. Если у класса, объекты которого хранятся в стеке, есть небросающий перемещающий конструктор, то исходная реализация (см. степ 10) обеспечивает строгую гарантию безопасности исключений. Если у класса, объекты которого хранятся в стеке, есть небросающий перемещающий конструктор, то исходная реализация (см. степ 10) не обеспечивает строгую гарантию безопасности исключений, но этого можно добиться добавив std::move в строке с return. Проблема обеспечения строгой гарантии безопасности возвращающего метода pop возникает только для стеков, которые реализованы на массиве. Проблема обеспечения строгой гарантии безопасности возвращающего метода pop актуальна для произвольных контейнеров.
Исходная реализация вот такая:
template struct Stack { void Push(const T& t) { data_.push_back(t) }
T pop() { T tmp = data_.back(); data_.pop_back(); return tmp; }
std::vector data_; };
Как мне кажется, если в return добавить std::move, то мы явно вызовем перемещающий метод и все будет хорошо. Ну и проблема обеспечения СГБ вполне актуальна не только для стеков на массиве, не вижу тут совсем никакой связи с массивом, т.к. проблема в гарантии возникает из за возможной ошибки при копировании, потому что объект возвращается по значению. Поэтому отвечаю 1, 4 и 6, но это неверно. Подскажите пожалуйста где ошибка в моем мышлении?
UPD
Правильно: 1, 3, 6. Объяснение см. в комментариях к правильному ответу на вопрос ну и сам правильный ответ :)


Ответ

Как мне кажется, если в return добавить std::move, то мы явно вызовем перемещающий метод и все будет хорошо.
Так как после изменения контейнера у нас идет только return, то необходимо, чтобы этот участок был безопасен относительно исключений, всё что было до изменения контейнера нам не важно. Также примем за правду тот факт, что деструктор T не выбрасывает исключений, в ином случае обеспечить строгую гарантию нельзя. Поэтому для начала разберемся с копированием возвращаемого значения. Компилятор постарается применить оптимизации RVO/NRVO, чтобы избежать как копирования, так и перемещения возвращаемого значения. В случае, если такая оптимизация не удастся, то в return по-возможности будет перемещение tmp, а не его копирование, согласно стандарту:
When the criteria for elision of a copy/move operation are met, but not for an exception-declaration , and the object to be copied is designated by an lvalue, or when the expression in a return statement is a (possibly parenthesized) id-expression that names an object with automatic storage duration declared in the body or parameter-declaration-clause of the innermost enclosing function or lambda-expression, overload resolution to select the constructor for the copy is first performed as if the object were designated by an rvalue. If the first overload resolution fails or was not performed, or if the type of the first parameter of the selected constructor is not an rvalue reference to the object’s type (possibly cv-qualified), overload resolution is performed again, considering the object as an lvalue.

Обеспечить строгую гарантию безопасности для метода pop, который возвращает выталкиваемый элемент, не получается из-за возможного исключения при копировании возвращаемого объекта.
Объект по-возможности будет перемещен, так что этот пункт отпадает, ведь у нас явно задано в условии, что объект может перемещаться не выбрасывая исключений. Если же объект может выбросить исключение при перемещении, или если перемещение невозможно и при копировании может вылететь исключение, то строгую безопасность такой pop не обеспечивает.
Если у класса, объекты которого хранятся в стеке, есть перемещающий конструктор, то исходная реализация (см. степ 10) обеспечивает строгую гарантию безопасности исключений.
Нет, ведь перемещающий конструктор может бросить исключение, а объект из стека уже удален.
Если у класса, объекты которого хранятся в стеке, есть небросающий перемещающий конструктор, то исходная реализация (см. степ 10) обеспечивает строгую гарантию безопасности исключений.
Это верно.
Если у класса, объекты которого хранятся в стеке, есть небросающий перемещающий конструктор, то исходная реализация (см. степ 10) не обеспечивает строгую гарантию безопасности исключений, но этого можно добиться добавив std::move в строке с return.
Нет, исходная реализация уже обеспечивает такую гарантию. Как сказано выше, сначала компилятор захочет вообще убрать, и копирование, и перемещение. Если не удастся, то tmp будет рассматриваться как rvalue. А вот если применить std::move, то это может поломать компилятору возможность сделать RVO/NRVO, т.е. лучше оно не сделает, но может сделать хуже в плане оптимизаций.
Проблема обеспечения строгой гарантии безопасности возвращающего метода pop возникает только для стеков, которые реализованы на массиве.
Имеется ввиду просто массив вида T[N] или любой контейнер, представляющий структуру данных массив? В первом случае удаление из массива одного элемента невозможно, разве что вручную управлять временем жизни объектов, но тогда массив должен быть не T[N], а, например, char[N * sizeof(T)] с соответствующим выравниванием. При таком подходе вся безопасность ложиться на обертку, которая управляет этим массивом. Во втором же, всё должно быть в порядке, если сам pop_back не бросает исключений. Поэтому в общем случае, с массивом будет всё нормально.
Проблема обеспечения строгой гарантии безопасности возвращающего метода pop актуальна для произвольных контейнеров.
Разве что произвольный контейнер сам по себе не обеспечивает эту безопасность при удалении последнего элемента, поэтому всё будет зависеть от реализации контейнера.
Возможно, я в чем-то не прав и меня поправят.

Почему имена встроенных функций в Go набраны строчными буквами?

Функции и переменные с именами начинающимися со сточной буквы не видны снаружи пакета, так почему встроенные типы и функции начинаются со строчной буквы? Они описаны в пакете main?


Ответ

Имена встроенных функций не входят ни в один пакет, и в Go нет перегрузки функций, по этому если существует встроенная функция foo, то ни в одном пакете нельзя объявить функцию с именем foo
Если бы встроенные функции начинались с большой буквы, например Append или Close, то нельзя было бы сделать метод с таким именем, что весьма неудобно.
Более того, если в будущем появится новая встроенная функция, например Move, то надо будет переписывать все пакеты, имеющие такую функцию. Более того, т.к. это публичное API, надо будет переписать все пакеты, в которых эта функция вызывается.
По этому встроенные функции начинаются с маленькой буквы. Это не мешает использованию этих функций в публичном API, и при добавлении новых встроенных функций будет сломано меньше кода.

Общее количество недель в месяце

Как определить общее количество (неполные + полные) недель в произвольном месяце? Исходим из того что неделя начинается с понедельника и заканчивается воскресеньем. Другими словами, сколько строк календаря занимает тот или иной месяц.
Ноя.2015 - 6 недель (01.11 - Вс, 30.11 - Пн) Дек.2015 - 5 недель (01.12 - Вт, 31.12 - Чт)


Ответ

На основании дня недели последнего числа месяца находим число дней прошедших до последнего понедельника. Делим это число на 7 и округляем вверх, получаем кол-во недель прошедших до последней недели, прибавляем 1.
function getWeeks(year, month) // Внимание: Месяцы нумеруются с 0, как принято в JS { var l=new Date(year, month+1, 0); return Math.ceil( (l.getDate()- (l.getDay()?l.getDay():7))/7 )+1; }

Не работает сериализация объекта из xml в string C#

Рутовый класс:
[Serializable] [XmlRoot("Main")] public class MainClass { public MainClass() { Parents = new List(); }
[XmlElement("Parent")] public List Parents { get; set; } }
Есть родительский класс:
[Serializable] [XmlRoot("Parent")] public class MyParent { public enum myEnum : int { One, Two, Three };
[XmlAttribute("Id")] public string Id { get; set; }
[XmlAttribute("Name")] public string Name { get; set; } }
Есть класс, расширяющий его:
[Serializable] [XmlRoot("Parent")] public class MyChild : MyParent { public MyChild() { Child = new List(); }
[XmlElement("Child")] public List Child { get; set; } }
Этот класс раскрывает, что внутри тега Child
[Serializable] [XmlRoot("Child")] public class InnerParameter { public InnerParameter() {
}
[XmlText] public string Value { get; set; } }
Затем, пытаюсь сериализовать:
MainClass newMainXml = new MainClass(); newMainXml.Parents = new List(); MyChild ch = new MyChild ();
ch.Id = "123"; ch.Name = "Stefani";
InnerParameter parameter = new InnerParameter(); parameter.Value = "MyText"; ch.Child.Add(parameter);
newMainXml.Parents.Add(ch);
XmlSerialization.TrySerializeObjectToXmlString(newMainXml, out result);
Хочу на выходе такую XML:

MyText

Получаю ошибку сериализации:
There was an error generating the XML document. The type MyChild was not expected. Use the XmlInclude or SoapInclude attribute to specify types that are not known statically.
Подскажите пожалуйста, что я делаю не так в формировании xml? Почему сериализация не работает? (Есть большое подозрение, что дело тут в наследовании и зависимости классов между собой. Наверно, надо сказать, что MyChild не единственный класс, который расширяет MyParent...)
P.S.: похоже, что тут ситуация как у меня почти: XML-сериализация объекта с полем типа Object C#


Ответ

Проблема решается действительно добавлением [XmlInclude(typeof(MyChild))] таким образом:
[Serializable] [XmlRoot("Parent")] [XmlInclude(typeof(MyChild))] public class MyParent { public enum myEnum : int { One, Two, Three };
[XmlAttribute("Id")] public string Id { get; set; }
[XmlAttribute("Name")] public string Name { get; set; } }
Моя ошибка была в том, что (для сравнения) у меня было написано так:
[Serializable] [XmlRoot("Parent")] [XmlInclude(typeof(MyChild)), XmlInclude(typeof(HisChild)), XmlInclude(typeof(HerChild))] public class MyParent { public enum myEnum : int { One, Two, Three };
[XmlAttribute("Id")] public string Id { get; set; }
[XmlAttribute("Name")] public string Name { get; set; } }
Однако, как я уже говорил, MyChild - не единственный класс, реализующий MyParent, и эти наследники так же необходимо сериализовать. Вот тут хорошая статья: Xml Сериализация и наследование

Как узнать имя пользователя или email в onedrive sdk?

Как, используя OneDriveClientExtensions, узнать имя отображаемое пользователя или email.
string[] scopes = { "onedrive.readwrite" }; IOneDriveClient OneDriveClient = OneDriveClientExtensions.GetUniversalClient(scopes); await OneDriveClient.AuthenticateAsync();


Ответ

Напрямую, используя данную библиотеку, не получить данные пользователя. Но из неё можно получить AccessToken, а затем, используя его, получить данные пользователя при помощи REST API. Имя пользователя можно получить примерно так:
string[] scopes = new string[] { "onedrive.readwrite" }; var client = OneDriveClientExtensions.GetUniversalClient(scopes) as OneDriveClient; await client.AuthenticateAsync(); // забираем access_token var AccessToken = client.AuthenticationProvider.CurrentAccountSession.AccessToken; // запрашиваем данные авторизованного пользователя REST API var uri = new Uri($"https://apis.live.net/v5.0/me?access_token={AccessToken}");
var httpClient = new System.Net.Http.HttpClient(); var result = await httpClient.GetAsync(uri); // получаем данные пользователя в JSON и разбираем его string jsonUserInfo = await result.Content.ReadAsStringAsync(); if (jsonUserInfo != null) { var json = Newtonsoft.Json.Linq.JObject.Parse(jsonUserInfo); string username = json["name"].ToString(); }
Email можно получить примерно так (не забудьте добавить wl.emails в список разрешений scopes)
string[] scopes = new string[] { "onedrive.readwrite", "wl.emails" }; var client = OneDriveClientExtensions.GetUniversalClient(scopes) as OneDriveClient; await client.AuthenticateAsync(); var AccessToken = client.AuthenticationProvider.CurrentAccountSession.AccessToken; var uri = new Uri($"https://apis.live.net/v5.0/me?access_token={AccessToken}");
var httpClient = new System.Net.Http.HttpClient(); var result = await httpClient.GetAsync(uri); string jsonUserInfo = await result.Content.ReadAsStringAsync(); if (jsonUserInfo != null) { var json = Newtonsoft.Json.Linq.JObject.Parse(jsonUserInfo); string username = json["name"].ToString(); string email = json["emails"]["account"].ToString(); }

Помогите разобраться с лицензиями к программам на Qt

Так как я практически написал библиотеку и программу на бесплатной версии Qt, пришло время задуматься о лицезии.
С библиотекой я думаю использовать Apache License 2.0.
Apache даёт пользователю право использовать программное обеспечение для любых целей, свободно распространять, изменять, и распространять изменённые копии, за исключением названия"
Мне это подходит, я все выложу в открытый доступ на Git-hub, я правильно думаю?
Теперь о программе. Вот с программой под десктоп, которая будет бесплатной, выкладывать открытый исходный код на git-hub мне не хочется, я так понял с этого выражения
The Qt Company предоставляет возможность использования открытой лицензии, в рамках которой некоторые модули доступны под LGPLv2.1, LGPLv3/GPLv3, другие – только под LGPLv3.
что исходный код можно не показывать. Какую лицензию для бесплатной проги взять чтобы исходный код все же был закрыт? В принципе на крайний случай я готов выложить исходный код, если так, то надо принять GPL 3.0? Спс если кто то поможет разобраться.


Ответ

По опыту использовать Qt в разных компаниях, обычно использование Qt не требует от вас выкладывать исходники, если вы используете динамическую линкову с Qt (Qt в dll). Лицензию при этом надо брать LGPL. Но вы не можете модифицировать исходники Qt. Если вы статически линкуетесь с Qt то исходный код надо вроде бы выкладывать. Можете посмотреть вот этот сайт по лицензиям: https://tldrlegal.com/license/gnu-lesser-general-public-license-v3-(lgpl-3)
Тут могут быть всякие трюки например: Основаную бизнес-логику помещаете в dll и делаете её закрытой, при этом exe файл открыт и использует Qt для вывода информации.
Также я встречал OpenSource проекты, которые имели открытый исходный код, но продавали платные модули для своих программ.
Ссылка, которая может помочь в выборе под какой лицензией использовать Qt:https://www.qt.io/download-guide/

почему Java stack memory быстрее heap

почему доступ к объектам в стэке быстрее чем к объектам вне стэка ?


Ответ

Управление памятью для стека тривиально: машина просто увеличивает или уменьшает одно значение, так называемый «указатель стека» (stack pointer). Управление памятью для кучи сложнее: память, выделенная в куче, освобождается в произвольные моменты, а каждая область выделенной в куче памяти может быть произвольного размера. Распределителю памяти, как правило, требуется приложить гораздо больше усилий для определения областей, которые можно использовать заново.

Как найти все матрицы состоящие из заданного числа элементов, с ненулевым определителем?

Имеется матрица 7×7. Элементами матрицы может быть любой из 33 элементов. Если мы перебором всевозможные матрицы, то количество комбинаций будет равно 33^49. Это очень долго для компьютера. Но мне не нужны все матрицы, мне нужны лишь те, которые имеет ненулевой определитель. Какие свойства матриц можно использовать, чтобы ускорить поиск неворожденных матриц? Ведь количество таких матриц ограничено в сравнении с общим количеством комбинаций. Какие будет предложения?


Ответ

В постановке ОП задача нерешаема.
Если 33 ненулевых элемента не равны попарно, то существует 33^7 = 42618442977 вариантов их размещения на главной диагонали, . Если это не смущает, можно разместить те же элементы в любой из 7! = 5040 "ладейных" расстановок (по одному на строку и по одному на столбец). И такие коэффициенты можно добавлять многократно (например, использовать свойства блочных матриц).
UPD.
Если задать элементы ниже главной диагонали нулевыми, элементы главной диагонали - ненулевыми, а элементы выше главной диагонали - произвольными, то определитель будет ненулевым (и равным произведению элементов главной диагонали). Если нуль использовать нельзя, то можно заполнить матрицу ниже главной диагонали одним и тем же элементом, главную диагональ - неравными ему элементами, а матрицу выше главной диагонали - произвольно. В этом случае определитель будет произведением разностей элементов главной диагонали и "нижнего" элемента. Перестановка строк и столбцов матрицы может изменить только знак её определителя. Таким образом, количество ненулевых определителей не меньше, чем 33 * 327 * 3321 * 7! = 5040 * 3322 * 327 ≈ 4 * 1047

Как добавить в visual studio 2015 вывод дебажной информации в самом редакторе?

Как добавить показ дебажной информации в Visual Studio 2015 непосредственно в редакторе кода а не в отдельной панели. Хотелось бы как например а Intelij Idea или в chrome:


Ответ

В ReSharper 2017.3.1 как раз появился такой функционал как у вас на скрине. Ну и всегда вы можете навести мышку на переменную в режиме отладки и увидеть значение.

Почему GitHub считает, что другой человек запушил коммит, который на самом деле запушил я?

Создал пустой GitHub репозиторий. Запушил в него свой единственный локальный коммит. Обновил страницу репозитория на GitHub'е, а там написано, что закоммитил какой-то неизвестный мне человек. Почему?

Это однозначно не я, это аккаунт какого-то совершенно незнакомого мне человека (на самом деле знакомого, потому как подобное уже происходило ранее, но я не придавал этому значения).
Тем более, что при пуше я указывал данные от своего аккаунта - свой username и свой пароль. Как такое возможно, почему так происходит, и что теперь делать?

Интересно также, что GitHub считает, что этот репозиторий, созданный собственноручно мной 20 минут назад, создан тем самым matthewferderber'ом:

А также тот коммит, который якобы он сделал в мой репозиторий, не отображается в истории его активности


Ответ

Скорее всего неверно указан пользователь в конфигурации git. Чтобы проверить, нужно в консоли набрать git config --list
В списке проверить, что поле user.email присутствует один раз и заполнено правильно.
Как настроить конфиги верно:
Видео: на youtube Статья в книге: Первоначальная-настройка-Git

Разница между VARCHAR и VARCHAR2

Все кто имел дело с БД Oracle, знают, что объявлять символьные поля и переменные надо как VARCHAR2, а не VARCHAR
Но эти типы данных вроде синонимы:
CREATE TABLE table1 ( id number, text VARCHAR(10), text2 VARCHAR2(10) );
Name Null Type ----- ---- ------------ ID NUMBER TEXT VARCHAR2(10) TEXT2 VARCHAR2(10)
Так какая все таки между ними разница?


Ответ

На данный момент они синонимы.
VARCHAR зарезервирован Oracle для поддержки, в будущем, различия между NULL и пустой строкой, как описано в стандарте ANSI
VARCHAR2 не делает различий между NULL и пустой строкой, и никогда не будет делать.
Если вы полагаетесь на то, что пустая строка и NULL - одно и то же, используйте VARCHAR2
перевод ответа @Quassnoi

Ускорение генерации excel файла на c#

Ситуация следующая:
Необходимо из набора данных (порядка 7000 значений) сгенерировать отчет. Структура отчета задается шаблоном (набор стандартных блоков и порядок значений в строках блока, количество строк в блоке и количество заполняемых ячеек варьируется), ячейки в которые необходимо вставить данные ищутся поиском по ключевому тексту для значения. После вставки данных незаполненные ячейки очищаются (тот же поиск, но вставка пустого текста, а не значений). Вставка занимает в среднем 87с., удаление 33с. Есть варианты ускорить работу приложения?
Код примерно следующий:
foreach(значения) { rngFind = rngReport.Find(Ключевой текст, Type.Missing, Excel.XlFindLookIn.xlValues, Excel.XlLookAt.xlWhole, Excel.XlSearchOrder.xlByColumns, Excel.XlSearchDirection.xlNext, false, Type.Missing, false); if (rngFind != null) { rngFind.HorizontalAlignment = Excel.XlHAlign.xlHAlignRight; rngFind.Value2 = Значение; } }
while (flag) { rngFind = rngReport.Find(Элемент ключа, rngReport.Cells[1, 1], Excel.XlFindLookIn.xlValues, Excel.XlLookAt.xlPart, Excel.XlSearchOrder.xlByRows, Excel.XlSearchDirection.xlNext, false, Type.Missing, false); if (rngFind != null) { rngFind.HorizontalAlignment = Excel.XlHAlign.xlHAlignRight; rngFind.Value2 = ""; } }
Дополнение:
Ускорит ли работу вариант со сбором значений в массив по шаблону, а потом уже вставкой массива в excel без поиска, просто по тем же индексам что в массиве?
Дополнение2: Добавление в код следующих строк ускорило работу на 13%:
xlApp.Calculation = Excel.XlCalculation.xlCalculationManual; xlApp.ScreenUpdating = false;
Заметки: Calculation можно изменить только при открытом документе и ручной расчет влияет на доступность буфера обмена (при дефолтном значении во время генерации документа нельзя было что-то скопировать в windows).
Для ускорения работы с данными можно работать с массивом вместо Range (Object[,] vals = (System.Object[,])wsReport.Cells.get_Range(from, to)), но так не получается сохранять форматирование из шаблона, как обойтись без Range, но сохранить форматирование?
Похожее обсуждение на stackoverflow.com. Статья на microsoft.com по работе с массивом вместо range.
Дополнение 3: Удалось сравнить Interop и EPPlus (3.0.0.2) на генерации одинаковых документов: Interop медленнее в ~20 раз.


Ответ

Делай как я: Object[,] vals = (System.Object[,])wsReport.get_Range(from, to).get_Value(Type.Missing); В этом массиве набираются и изменяются итоговые данные. Excel.Range format = wsReport.Cells.get_Range(from, to); Этот range используется для вставки форматов из шаблонов. При получении блока из шаблона (rng) делаю так: rng.Copy(Type.Missing);//скопировали в буфер ((Excel.Range)format[fromRow, 1]).PasteSpecial(Microsoft.Office.Interop.Excel.XlPasteType.xlPasteFormats, Microsoft.Office.Interop.Excel.XlPasteSpecialOperation.xlPasteSpecialOperationNone, Type.Missing, Type.Missing);//скопировали форматирование из шаблона на итоговый лист
Object[,] tmpvals = (Object[,])rng.get_Value(Type.Missing);//взяли значения из шаблона, теперь надо вставить в итоговый массив данных for (int i = 1; i <= rng.Rows.Count; i++) { if (fromRow > vals.GetUpperBound(0)) break; for (int j = 1; j<= rng.Columns.Count; j++) { vals[fromRow, j] = tmpvals[i,j]; } fromRow++;//курсор на последнюю строчку } Вместо Find приходится делать так: foreach (значения) { for (int i = nRows - dx; i <= nRows; i++) { for (int j = 2; j < vals.GetUpperBound(1); j++) { if (vals[i,j]!=null && vals[i, j].ToString() == ключевой текст) { vals[i, j] = значение; goto Found; } } } Found: ; } В итоге время формирования документа со 150с. (вставка строк 0.6с, вставка значений 86с., удаление 32с.) уменьшилось до 45с (вставка строк 13с. вставка данных в массив 0.13с., удаление 0.32с, итоговая вставка 0.16).

Лицензия GNU AGPLv3 и play market

Допустим, есть опенсорсная библиотека, распространяемая под лицензией GNU AGPLv3. Я хочу использовать ее в своем приложении (исходники править не буду).
Могу ли я использовать свое приложение в коммерческих целях (выложить на маркет как free app)? Если я нарушаю лицензию, могут ли меня вычислить и покарать? Если я писал приложение под заказ, может ли заказчик выложить его на маркет?


Ответ

Удивительно читать упражнения отдельных деятелей даже без намека на ссылку на оригинал собственно самой лицензии. Радуют опусы типа: Раз либа - опенсорс, да еще и распространяется по свободной лицензии - вы ничего не нарушаете и можете свободно использовать её в своих целях. Это распространенное заблуждение. Есть варианты, напрямую запрещающие их использование в коммерческих целях, например Creative Common Non-Commercial Что касается лицензии AGPL v.3, то условия по поводу либ довольно простые: При распространении не-исходных форм необходимо: опубликовать на каждой копии уведомление об авторском праве, сохранить все уведомления об авторских правах, сохранить все уведомления об отсутствии гарантий, приложить копию данной лицензии, приложить заметное уведомление об изменении кода программы, указать дату внесения изменений, распространять на условиях данной лицензии все внесенные изменения, В конкретном случае, указанном ТС, необходимо в пункте About программы сделать сноску на копирайты авторов либы, URL с локацией либы и привести текст лицензии (да-да, сделать не ссылку, а именно приложить прямо сам текст лицензии - см. приложить копию данной лицензии). Update Прозвучал вопрос: карают ли за нарушение лицензий? Карают-карают, так покарают, что мало не покажется. У меня удалили из маркета аппу со 100 тыс. закачек только за то, что в About не привел ссылку на лицензию на использование иконки, скачанной в WikiMedia. Update2 Прозвучал еще 1 вопрос: может ли заказчик сам выложить его на маркет? Это довольно сложный вопрос. Исходя из общей логики и здравого смысла, если вы разработали аппу под заказ, то вы лишаетесь авторских прав, и авторские права на публикацию и извлечение дохода от публикации переходят к заказчику. В реальной жизни, если у вас с заказчиком отсутствует договор об отчуждении/переходе авторских от автора к заказчику, то заказчик не имеет прав на продукт. Уже были таковые прецеденты судебных дел. Чтобы защититься, заказчику необходимо заключить письменный договор с вами, ну или на худой конец акт передачи/отчуждения авторских прав. Форма договора обсуждалась на Хабре То есть если, не имея такого договора, заказчик опубликовал работу на маркете, то это прямое нарушение действующего законодательства, хотя с точки зрения здравого смысла он прав. Как поступить, решайте сами. И по совести, и по закону будет правильно составить бумажку, в которой говорится о переходе авторских прав от вас к заказчику.

Абстрактный класс-Singleton на java

Некоторое время назад писал на юнити. Использовал на шарпе такой класс:
public abstract class MySingleton { private static T _instance;
public static T Instance { get { if (_instance == null) _instance = (T)Activator.CreateInstance(typeof(T));
return _instance; } }
public static void ForgetInstance() { _instance = default(T); }
protected MySingleton() {
}
Можно ли как-то извернуться, чтобы написать похоже на java?
В лоб то, говорит: "Cannot make a static reference to the non-static type T"


Ответ

Как я понимаю, хочется хранить по одному объекту для каждого запрошенного класса, созданному с помощью конструктора по умолчанию. Для этой цели можно завести ConcurrentHashMap, Object>
import java.util.concurrent.ConcurrentHashMap;
public class MySingleton { private static ConcurrentHashMap, Object> map = new ConcurrentHashMap<>();
@SuppressWarnings("unchecked") public static T getInstance(Class clazz) { T object = (T) map.get(clazz); if(object == null) { try { object = clazz.newInstance(); } catch (IllegalAccessException | InstantiationException | SecurityException ex) { throw new RuntimeException(ex); } T oldObject = (T) map.putIfAbsent(clazz, object); if(oldObject != null) // ой, другой поток вперёд успел return oldObject; } return object; }
public static void forgetInstance(Class clazz) { map.remove(clazz); } }
Соответственно использовать как-то так:
public static void main(String[] args) { String str = MySingleton.getInstance(String.class); String str2 = MySingleton.getInstance(String.class); System.out.println(str == str2); // true MySingleton.forgetInstance(String.class); }

Автогенерация классов в C++

Имеется структура данных, подобная следующей:
Group_1 Type_1_1 Variable_1_1 . . . Type_1_N Variable_1_N Group_1_1 Name_1_1 Type_1_1_1 Variable_1_1_1 . . . Type_1_1_N1 Variable_1_1_N1 . . . Group_1_M Name_1_M Type_1_M_1 Variable_1_M_1 . . . Type_1_M_N2 Variable_1_M_N2
Важно, что число групп и число уровней вложения может быть произвольным. Внутри каждой группы могут быть как другие группы, так и переменные. В общем случае на типы переменных никакие ограничения не накладываются.
Для указанной структуры необходимо построить C++-код следующего вида:
class Group_1_1 { public : Type_1_1_1 Variable_1_1_1; // . . . Type_1_1_N1 Variable_1_1_N1; void f(Worker& worker) { worker.begin("Group_1_1"); worker.work("Variable_1_1_1", Variable_1_1_1); // . . . worker.work("Variable_1_1_N1", Variable_1_1_N1); worker.end(); } } // . . .
class Group_1 { public : Type_1_1 Variable_1_1; // . . . Type_1_N Variable_1_N; Group_1_1 Name_1_1; // . . . Group_1_M Name_1_M; void f(Worker& worker) { worker.begin("Group_1"); worker.work("Variable_1_1", Variable_1_1_1); // . . . worker.work("Variable_1_N", Variable_1_1_N1); Name_1_1.f(worker); // . . . Name_1_M.f(worker); worker.end(); } }
Здесь Worker - некоторый внешний класс.
Построить код необходимо без использования каких-либо внешних инструментов, можно использовать только стандартные возможности компилятора и препроцессора. То есть можно использовать макросы и шаблоны, но нельзя запускать какие-либо программы, не входящие в состав компилятора. Можно при этом ограничиться стандартом c++11, совместимость с предыдущими стандартами не требуется. Решение должно работать по меньшей мере со следующими компиляторами: GCC 4.9+ (Linux), MinGW 4.9+ (Windows), MSVC 2015.
Собственно вопрос: можно ли это реализовать и если можно, то как?


Ответ

Предлагаю решение с ипользованием boost::preprocessor. Пока что это решение только половины задачи, генерации отдельных классов.
Входные данные должны быть представлены примерно так:
#define GEN_GROUP 1 #define GEN_VAR 0
#define MY_STRUCT \ (GEN_GROUP, AType, A, (GEN_VAR, int, AInt) \ , (GEN_GROUP, BType, B, (GEN_VAR, int, BInt) \ , (GEN_VAR, float, BFloat) \ ) \ , (GEN_VAR, char, AChar) \ )
В понятиях boost::preprocessor это кортежи (tuple). А точнее кортежи которые содержат другие кортежи и таким образом образуют дерево. Струтура это дерева такая: элемент 0 - тип узла (группа или переменная), элемент 1 - тип данных, 2 - имя узла, и в случае группы остальные элементы начиная с 3 это поля или подгруппы:
группа = (1, тип, имя, [переменная | группа]+ ) переменная = (0, тип, имя)
Использовать эту структуру надо следующим образом, приведу просто код с коментариями:
#include
#define GEN_NODE_TYPE(node) BOOST_PP_TUPLE_ELEM(0, node) #define GEN_TYPE(node) BOOST_PP_TUPLE_ELEM(1, node) #define GEN_NAME(node) BOOST_PP_TUPLE_ELEM(2, node) #define GEN_CHILDS(node) \ // конвертируем в список, выкусываем хвост начиная с элемента 3 BOOST_PP_LIST_REST_N(3, BOOST_PP_TUPLE_TO_LIST(node))
// функтор для генерации полей класа вида "тип имя;" #define GENERATE_MEMBER_PRED(r, data, member) \ GEN_TYPE(member) GEN_NAME(member);
// функтор для генерации вызовов worker внутри фнукции f #define GENERATE_WORK_CALL_PRED(r, data, member) \ BOOST_PP_IF(GEN_NODE_TYPE(member) \ // если тип "группа" , GEN_NAME(member).f(worker) \ // то "имя.f(worker)" , worker.work(BOOST_PP_STRINGAZE(GEN_NAME(member)) \ // заковычиваем имя ,GEN_NAME(2, member)) \ // иначе worker.work("имя", имя) ); // точка с запятой для всех
#define GENERATE_CLASS(node) \ class GEN_TYPE(node) \ // "class тип" { \ public: \ \ // генерируем поля BOOST_PP_LIST_FOR_EACH( \ GENERATE_MEMBER_PRED \ // выполняем для каждого элемента , _ \ // здесь можно пусто , GEN_CHILDS(node) \ // список детей ) \ \ void f(Worker & worker) \ { \ worker.begin(BOOST_PP_STRINGAZE(GEN_TYPE(member))); \ // заковычивам тип \ \ // генерируем вызовы worker для полей BOOST_PP_LIST_FOR_EACH( \ GENERATE_WORK_CALL_PRED \ // выполняем для каждого ребенка , _ \ // здесь можно пусто , GEN_CHILDS(node) \ // список детей ) \ \ worker.end(); \ } \ };
Результат для GENERATE_CLASS(MY_STRUCT)
class AType { public: int AInt; BType B; char AChar;
void f(Worker & worker) { worker.begin("AType"); worker.work("AInt", AInt); B.f(worker); worker.work("AChar", AChar); worker.end(); } };
Как видите, классы для вложенных групп не генерируются, но это уже хоть что-то. Чтобы сделать вложенные группы необходимо реализовать обход входного дерева, при котором для каждого узла, если он группа, вызывать GENERATE_CLASS. Так как препроцессор не поддерживает рекурсию макросов, обход надо делать нерекурсивный со стеком. На boost::preprocessor по идее это возможно, но сложно, предлагаю это для самостоятельной проработки.
Внимание! Самое большое ограничение такого подхода в том, что бесконечную структуру обработать не получится. Так как количество итераций в любом циклическом операторе boost::preprocessor ограничено, равно как и в любом другом подобном инструменте основанном на препроцессоре Си. Сейчас, например, максимальное количество итераций для FOR_* составляет 256. Поэтому для больших циклов вам нужно править библиотеку boost::preprocessor либо делать свой клон с каким вам надо количеством итераций.

Определить перекрыт ли элемент другим элементом

Возможно ли определить перекрытие элемента другим элементом, при условии что не известен элемент который будет перекрывать?
var e = document.getElementById('e'); var eR = e.getBoundingClientRect(); var all = document.getElementsByTagName('*'); for (var i=0, max=all.length; i < max; i++) { var cR = all[i].getBoundingClientRect(); if(all[i] != e) { if (((cR.top <= eR.top) && (eR.top <= cR.bottom)) && ((cR.top <= eR.bottom) && (eR.bottom <= cR.bottom)) && ((cR.left <= eR.left) && (eR.left <= cR.right)) && ((cR.left <= eR.right) && (eR.right <= cR.right))) { //var inside = true; var style = window.getComputedStyle ? getComputedStyle(all[i]) : all[i].currentStyle; if(style.zIndex >= 1) { var overlap = true; console.log(style.zIndex); } } } } .wrap { width:700px; overflow:hidden; height:600px; background-color:yellow; z-index:9999; } .behind { position: absolute; left:8px; top:0px; width:468px; height:160px; background-color:rgba(255, 0, 0, 0.71); z-index: 999; } .container { background-color:black; left:20px; top:50px; width:468px; height:60px; color:#fff; }

ad

Пример на JSFiddle UPD
Вот что получилось, проблема в том, что если у родителя (.wrap) стоит z-index >0 он не перекрывает потомка (.container), а скрипт проверяет это условие и получается ложное перекрытие. Я правильно понимаю что родитель может перекрыть потомка только если у потомка отрицательный z-index?


Ответ

Как вариант, можно получить х,у координаты нашего "container" элемента и его х,у + его размер. Далее, пройтись циклом по всему DOM (по всем элементам, исключая наш "container") и проверять их координаты учитывая размеры и параметр z-index, который должен быть меньше чем у "container" в случае если есть совпадения координат. Это все можно или на Javascript сделать, или с JQuery.
Например, с JQuery: http://www.sitepoint.com/jquery-coordinates-element/
var ctrxCoord = $(".container").offset().left; var ctryCoord = $(".container").offset().top; var ctrwidthCoord = ctrxCoord + $(".container").width(); var ctrheightCoord = ctryCoord + $(".container").height();
Нужно проверить, возможно, для добавления, нужно делать с parseInt(ctryCoord) ...
Больше информации (англ.): https://stackoverflow.com/a/21274679/873481 и http://www.dyn-web.com/javascript/element-location/
Думаю, комментарий cyadvert может также быть верным.
Еще вариант - document.elementFromPoint https://stackoverflow.com/a/3942852/873481