Страницы

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

понедельник, 1 октября 2018 г.

Почему асинхронная функция внутри цикла выполняет последнюю итерацию много раз?

Есть setTimeout внутри цикла for
for (var i = 1; i <= 5; i++) { setTimeout(function() { console.log(i); }, i * 1000); }
Я хочу показывать числа 1, 2, 3, 4, 5, но показывает 6, 6, 6, 6, 6. Почему?


Ответ

Дело в том, что функция выполняется после того, что цикл закончится. Поэтому, i уже равно 6, когда console.log(i) выполняется первый раз.
Если еще непонятно, вот похожий пример в псевдокоде:
У меня 1 камень. Через минуту скажи, сколько у меня камней. Дай мне камень сейчас. Через 2 минуты скажи, сколько у меня камней. Дай мне камень сейчас.
Получится, что сейчас мне даст 2 камня, в итоге у меня будут 3. Через минуту скажет, сколько у меня камней (т.е. 3), и через две минуты опять скажет, что у меня 3 камня.
Как это решить?
Вставить асинхронную функцию в анонимную функцию, чтобы передать другое значение i функции вывода на каждой итерации. Это самое обычное решение.
for(var i = 1; i <= 5; i++) { (function(i) { setTimeout(function() { console.log(i); }, i * 1000); })(i); }
Создавать много одинаковых функций - не очень хорошо, можно поднять эту функцию выше:
function pass(i) { return function () { console.log(i); } } for(var i = 1; i <= 5; i++) { setTimeout(pass(i), i * 1000); }
Использовать рекурсию вместо цикла. Это тоже обычное решение.
(function f(i) { if (i > 5) return; setTimeout(function() { console.log(i); f(i + 1); }, 1000); })(1);
Использовать Function.prototype.bind(), чтобы создать новую функцию на каждой итерации. Это короче первого варианта, но IE8 и ниже не поддерживают .bind
for (var i = 1; i <= 5; i++) { setTimeout(function(i) { console.log(i); }.bind(null, i), i * 1000); }
Передавать аргументы через setTimeout Это поддерживают все современные браузеры, но если речь идёт о старых, то стоит проверить.
for(var i = 1; i <= 5; i++) { setTimeout(function (i) { console.log(i); }, i * 1000, i); }
Замечу, что теперь функции в цикле ничем не отличаются друг от друга, поэтому можно сделать одну функцию:
function doSmth(i) { console.log(i); } for(var i = 1; i <= 5; i++) { setTimeout(doSmth, i * 1000, i); }
Использовать let . Это удобный вариант, но это новая возможность в ECMAScript 2015, так что еще не работает в большинстве браузеров. Если хотите использовать ECMAScript 2015 до того, что браузеры его поддерживают, рекомендую попробовать Babel
Внимание: некоторые браузеры (например, IE 11) поддерживают let, но не поддерживают его в цикле for
for (let i = 1; i <= 5; i++) { setTimeout(function() { console.log(i); }, i * 1000); }

На какие уязвимости, помимо SQL-инъекций и XSS, стоит проверить сайт?

Давно не интересовался методами взлома, так что солидно отстал от жизни. Кто с чем сталкивался?


Ответ

Я обычно анализирую следующие варианты:
XSS SQL injection HTML Injection HTTP Response splitting
Загляните на OWASP Top Ten
UPD
Попробуйте пройтись по своему творению этой системой

В чем разница между git add ., add -A, add -u и add *?

Есть разные способы добавить все измененные файлы в индекс репозитория Git. В чем разница и зачем столько способов?
git add . git add * git add -u git add -A git add --all git add --no-all


Ответ

Давайте обозначим категории файлов, которые вообще можно добавлять. Будем использовать те же обозначения, что и в выводе команды git status -s
M - (modified) отслеживаемые, изменились с прошлого коммита, еще не добавлены D - (deleted) отслеживаемые, удалены после прошлого коммита, еще не добавлены ? - (untracked) неотслеживаемые, не запрещены к добавлению ! - (ignored) неотслеживаемые, запрещены к добавлению (например, в .gitignore)
Параметры и аргументы
Первое различие  — в том, что — это путь (аргумент), а всё остальное — параметры. Те и другие не исключают друг друга и возможны их сочетания.
Использование абсолютных :/ и относительных путей с командой add
Путь обозначает текущую директорию, т.е. ту, в которой была запущена команда.
Начиная с Git версии 2.0, поведение команды add приведено в соответствие с поведением commit и других комманд. Теперь обозначает не всю рабочую область (working tree), а текущий путь в этой области.
Таким образом, если вы выполняете команду add не в корневой директории проекта (той, где лежит .git/), то будет обработано содержимое только текущей директории.
Чтобы явным образом дать указание Git работать со всей рабочей областью, используйте :/
# работает одинаково из любой директории, добавляет всю рабочую область git add :/
# путь относительно корневой директории git add :/path/to/files/
# работает только в текущей директории cd test git add . # эквивалентно этому: git add :/test
# путь относительно текущей директории cd test git add ./path # эквивалентно этому: git add :/test/path
Если не указан никакой путь к добавляемым файлам, то большинство команд работает во всей рабочей области, а git add и git add --no-all просто не работают.
Сводная таблица

О функционале команд подробно

git add . git add '*'
Git версии 2.0+ просматривает текущую папку и добавляет файлы M, D, ? Git версии 1.х просматривает всю рабочую область и добавляет файлы M, D
Если '*' дается в кавычках, то обрабатывать его будет Git и это эквивалентно git add .. Исключение: из-под cmd.exe git add '*' не сработает, используйте git add . или git add *

git add --no-all :/ git add --ignore-removal :/
Эта команда в Git v. 2.0+ работает как git add . в Git v. 1.x, то есть добавляет измененные и новые файлы M, ? во всей рабочей области. Для этой команды обязательно указывать путь.
git add --no-all . #добавляет измененные и новые файлы в *текущей директории* git add --no-all path1/ path2/ # добавляет измененные и новые файлы в путях *относительно текущей директории*

git add -u git add -update
Git обновляет (update) статус уже отслеживаемых файлов т.е. M, D

git add -A git add --all git add --no-ignore-removal
Эти варианты эквивалентны и добавляют M, D, ?
Без точки — из всей рабочей области:
git add -A = git add -A :/ = git add :/ + git add -u
С точкой — только текущий путь:
git add -A . = git add . + git add -u .

git add *
Этот синтаксис лучше не использовать, и вот почему:
При этой команде shell (или bash или другая командная оболочка) просматривает рабочую область и отдает Git список файлов на добавление. Система сработает таким образом, что будут найдены абсолютно все не-скрытые файлы, находящиеся в заданном корне. Вы можете посмотреть на этот список, выполнив echo *. ( Исключение: из-под cmd.exe git add * работает так же как git add '*' на shell/bash. )
Произойдет следующее (здесь мы видим сразу несколько причин не использовать add *):
Добавятся не изменившиеся с прошлого коммита файлы. Git спокойно и молча "прожует" этот запрос, не влияющий на индекс. Будут добавлены в индекс файлы в не-скрытых папках M? Не будут добавлены файлы в скрытых папках. .M, .? Не будут добавлены удаленные файлы D Если будут захвачены игнорируемые файлы !, то будет попытка их добавить. Git отменит всю операцию и покажет сообщение об ошибке.
Зачем столько способов
Разнообразие параметров (-u, -A, --no-all) нужно для того, чтобы можно было добавлять разные группы файлов. Конкретно --no-all . было добавлено для того, чтобы реализовывать старое поведение add . в версиях 1.х.
Похоже, что несмотря на это, Git не позволяет добавлять конкретные группы файлов одной командой (см. сводную таблицу в начале).
Тонкости в использовании и :/ нужны для того, чтобы каждую команду можно было выполнять как на всю рабочую область, так и на конкретную подпапку.

Обязательно ли использовать else?

Сегодня был на собеседовании по Java и завалился на операторе if Задача была в том, чтобы написать метод, который возвращает второе по величине число из входного массива. Завалился на операторе if при проверке данных. Моя реализация была такой:
public int getSecondMaxNumber(int[] numbers) { if (numbers == null || numbers.length < 2) { throw new IllegalArgumentException(); } // далее логика получения второго макс. числа }
К моему великому удивлению, оказалось, что я допустил грубейшую ошибку. Логику программы следует заключать в блок else, то есть так:
public int getSecondMaxNumber(int[] numbers) { if (numbers == null || numbers.length < 2) { throw new IllegalArgumentException(); } else { // именно здесь логика получения второго макс. числа } }
Зачем весь остальной код писать в блоке else, если при генерировании исключения поток выполнения этого метода прервется?
Аналогично, как выяснилось, и в случае, когда вместо генерации исключения просто указывается return. И вообще, после оператора if всегда должен быть else
Насколько я понял из слов технического менеджера, в случае с else JVM выполняет некую оптимизацию. Хотелось бы более подробнее узнать об этом моменте - что за оптимизация и где можно подробнее об этом почитать?


Ответ

В дополнение к ответу @VladD: я скомпилировал следующий класс с помощью java 8:
public class IfElse { public int getSecondMaxNumber1(int[] numbers) { if (numbers == null || numbers.length < 2) { throw new IllegalArgumentException(); } return numbers[1]; }
public int getSecondMaxNumber2(int[] numbers) { if (numbers == null || numbers.length < 2) { throw new IllegalArgumentException(); } else { return numbers[1]; } } }
Видно, что байткод методов абсолютно одинаковый:
$ javap -c IfElse.class Compiled from "IfElse.java" public class IfElse { public IfElse(); Code: 0: aload_0 1: invokespecial #1 // Method java/lang/Object."":()V 4: return
public int getSecondMaxNumber1(int[]); Code: 0: aload_1 1: ifnull 10 4: aload_1 5: arraylength 6: iconst_2 7: if_icmpge 18 10: new #2 // class java/lang/IllegalArgumentException 13: dup 14: invokespecial #3 // Method java/lang/IllegalArgumentException."":()V 17: athrow 18: aload_1 19: iconst_1 20: iaload 21: ireturn
public int getSecondMaxNumber2(int[]); Code: 0: aload_1 1: ifnull 10 4: aload_1 5: arraylength 6: iconst_2 7: if_icmpge 18 10: new #2 // class java/lang/IllegalArgumentException 13: dup 14: invokespecial #3 // Method java/lang/IllegalArgumentException."":()V 17: athrow 18: aload_1 19: iconst_1 20: iaload 21: ireturn }

воскресенье, 30 сентября 2018 г.

Работа с контролами из фонового потока

Ситуация следующая:
имеется окно с кнопкой button1 и меткой label1 по кнопке запускается какая-то долгая операция, в отдельном потоке. по завершению операции нужно вывести результат label1
При попытке поменять значение label1.Text код падает с исключением InvalidOperationException:
WinForms:
Cross-thread operation not valid: Control 'label1' accessed from a thread other than the thread it was created on. Недопустимая операция в нескольких потоках: попытка доступа к элементу управления 'label1' не из того потока, в котором он был создан.
WPF:
The calling thread cannot access this object because a different thread owns it. Вызывающий поток не может получить доступ к данному объекту, так как владельцем этого объекта является другой поток.
Пример кода:
private void button1_Click(object sender, EventArgs e) { (new Thread((s) => { var result = Worker.SomeLongOperation();
// следующая строчка падает c InvalidOperationException: this.label1.Text = result; })).Start(); }
class Worker { public static string SomeLongOperation() { Thread.Sleep(1000); return "результат"; } }


Ответ

Решение для .NET 4.0 и более поздних версий
Использовать Асинхронную модель на основе задач (TAP) и ключевые слова async-await
private async void button1_Click(object sender, EventArgs e) { string result = await Task.Factory.StartNew( () => Worker.SomeLongOperation(), TaskCreationOptions.LongRunning);
this.label1.Text = result; }
Преимущества:
Код значительно короче других вариантов, вызовы записаны в той последовательности, в которой они выполняются. Никаких коллбеков и ручной работы с потоками. async не дает обработчику события завершиться, но при этом не блокирует UI. TaskCreationOptions.LongRunning подсказывает планировщику, что операция будет действительно долгой, и для ее выполнения не стоит использовать пул потоков.
Встроенная поддержка ключевых слова async/await появились в .NET 4.5 и Visual Studio 2013.
Данное решение также может быть использовано для .NET 4.0 и Silverlight 5, если используется версия Visual Studio не ниже 2012. Для этого нужно установить пакет Microsoft.Bcl.Async из NuGet.
Решение с отображением прогресса выполнения
Если в процессе выполнения нужно отображать прогресс или промежуточные результаты из второго потока, то можно использовать класс Progress
private async void button1_Click(object sender, EventArgs e) { var progress = new Progress(s => label1.Text = s);
string result = await Task.Factory.StartNew( () => Worker.SomeLongOperation(progress), TaskCreationOptions.LongRunning);
this.label1.Text = result; }
class Worker { public static string SomeLongOperation(IProgress progress) { // Perform a long running work... for (var i = 0; i < 10; i++) { Task.Delay(500).Wait(); progress.Report(i.ToString()); } return "результат"; } }
Progress захватывает SynchronizationContext в момент создания, и использует его для выполнения операций, избавляя от ручных вызовов Invoke.
Решение для .NET 3.5 и более ранних версий
Использовать Invoke/BeginInvoke
// WinForms: this.label1.BeginInvoke((MethodInvoker)(() => this.label1.Text = result));
// WPF: Dispatcher.BeginInvoke((Action)(() => this.label1.Content = result));
Для .NET 2.0, в котором еще не было лямбд, эквивалентный код записывается с помощью анонимных делегатов:
// WinForms: this.label1.BeginInvoke((MethodInvoker)(delegate { this.label1.Text = result; });
// WPF: Dispatcher.BeginInvoke((Action)(delegate { this.label1.Content = result; });
Полный код:
private void button1_Click(object sender, EventArgs e) { (new Thread((s) => { var result = Worker.SomeLongOperation();
this.label1.BeginInvoke((MethodInvoker)(() => this.label1.Text = result));
})).Start(); }
BeginInvoke поставит код на выполнение в тот поток, в котором был создан label1 и продолжит выполнение фонового потока. При использовании Invoke вместо BeginInvoke фоновый поток будет приостановлен до завершения выполнения кода в UI потоке.

Как работает оператор ==

Как работает оператор проверки на равенство ("=="), имеются ли ему альтернативы и в каких ситуациях следует его применять?


Ответ

Оператор "=="
В Java оператор "==" возвращает значение типа boolean - результат сравнения ссылок на объекты (кроме примитивов), т.е. данный оператор не сравнивает на равенство внутреннее содержимое объектов, а просто проверяет указывают ли ссылки на один и тот-же объект. Таким образом, при сравнении объектов в Java оператор "==" вернет true лишь в том случае, когда ссылки указывают на один и тот-же объект.

Сравнение примитивов
Для примитивных типов (byte, short, int, long, char, float и double) концепция равенства/неравенства достаточно тривиальна: сначала операнды распространяются (т.е. происходит арифметическое распространение (promotion) операндов) до наибольшего если это необходимо, а затем происходит непосредственное побитовое сравнение (если все биты равны, то возвращаемое значение будет true, иначе - false).
Например, значение 48.0f (типа float, соответственно) равно значению '0' (код которого равен 48 согласно таблице символов Unicode) типа char, значение которого неявно распространяется до типа float
Контрольный пример:
char char1 = '0'; int int1 = 48; float float1 = 48.0F; System.out.println(char1 == int1); // true System.out.println(char1 == float1); // true System.out.println(int1 == float1); // true
// ---------------------------------------------
char char2 = 'A'; int int2 = 65; float float2 = 65.0F; System.out.println(char2 == int2); // true System.out.println(char2 == float2); // true System.out.println(int2 == float2); // true

Сравнение вещественных примитивов
Для вещественных примитивов (float и double) существуют некоторые особенности, которые необходимо учитывать. Представление дробной части осуществляется с помощью конечного числового ряда 2^(-n) (где значение числа n зависит от размера мантисы, который в свою очередь зависит от конкретного типа: float - 24 бита – это 2^(-24) ≈ 6*10^(-8), т.е. 6E-8F, либо double - 53 бита – это 2^(-53) ≈ 10^(-16), т.е. 1E-16, что фактически является шагом с которым реально идут значения в представлении данных типов), а потому о точном представлении произвольно взятого числа говорить не приходится.
Само собой разумеется, что две вещественные переменные, проинициализированные одним и тем же вещественным литералом, будут равны (ведь они проинициализировались одной и той же последовательностью бит):
float float1 = 0.7F; float float2 = 0.7F;
System.out.println(float1 == float2); // true
Но стоит начать выполнять арифметические операции с переменными данного типа, как, скорее всего, начнет накапливаться погрешность вычислений (из-за указанных выше особенностей представления экземпляров данного типа):
float float1 = 0.7F; float float2 = 0.3F + 0.4F;
System.out.println(float1 == float2); // false System.out.println(float1); // 0.7 System.out.println(float2); // 0.70000005
Как следует сравнивать вещественные примитивы
Вещественные примитивы (float и double) стоит сравнивать с определенной точностью. Например, округлять их до 6-го знака после запятой (1E-6 для double, либо 1E-6F для float), либо, что предпочтительнее, проверять абсолютное значение разницы между ними.
Контрольный пример:
float float1 = 0.7F; float float2 = 0.3F + 0.4F; final float EPS = 1E-6F;
System.out.println(Math.abs(float1 - float2) < EPS); // true

Использование Pool'ов
Как уже было сказано, оператор "==" проверяет указывают ли ссылки "на один и тот же объект", но иногда под этим простым выражением скрывается нечто большее чем может показаться на первый взгляд.
Для более эффективного использования памяти, в Java используются так называемые пулы: Integer pool, String pool и некотоыре другие. Когда мы создаем объект не используя операцию new, объект помещается в пул, и в последствии, если мы захотим создать такой же объект (опять не используя new), то новый объект создан не будет, а мы просто получим ссылку на наш объект из пула.
Стоит заметить, что использование Integer Pool'а будет осуществляться при любой автоупаковке (autoboxing), если значение соответсвует указанному диапазону, в отличие от строк (String Pool), для которых интернирование (intern) работает для литералов.

Integer pool
Особенность Integer pool'а в том, что он хранит только числа, которые помещаются в тип данных byte: от -128 до 127 (который начиная с Java 7 (раньше это было захардкожено внутри java.lang.Integer) можно расширить с помощью опции JVM: -Djava.lang.Integer.IntegerCache.high=size или -XX:AutoBoxCacheMax=size). Для остальных чисел из Integer'а пул не работает.
Контрольный пример:
Integer valueFromPool1 = 127; Integer valueFromPool2 = 127; Integer valueNotFromPool1 = 128; Integer valueNotFromPool2 = 128;
System.out.println(valueFromPool1 == valueFromPool2); // true System.out.println(valueNotFromPool1 == valueNotFromPool2); // false
System.out.println(127 == valueFromPool1); // true System.out.println(128 == valueNotFromPool1); // true System.out.println((Integer)127 == valueFromPool1); // true System.out.println((Integer)128 == valueNotFromPool1); // false

String pool
Выделим основные ньюансы строкового пула в Java
Строковые литералы (в одном/разных классе(ах) и в одном/разных пакете(ах)) представляют собой ссылки на один и тот же объект. Строки, получающиеся сложением констант, вычисляются во время компиляции и далее смотри пункт первый. Строки, создаваемые во время выполнения НЕ ссылаются на один и тот же объект. Метод intern в любом случае возвращает объект из пула, вне зависимости от того, когда создается строка, на этапе компиляции или выполнения.
В контексте данных пунктов речь шла об "одинаковых" строковых литералах.
Контрольный пример:
String hello = "Hello", hello2 = "Hello"; String hel = "Hel", lo = "lo";
System.out.println("Hello" == "Hello"); // true System.out.println("Hello" == "hello"); // false System.out.println(hello == hello2); // true System.out.println(hello == ("Hel" + "lo")); // true System.out.println(hello == (hel + lo)); // false System.out.println(hello == (hel + lo).intern()); // true
Подробнее про interning можно прочитать здесь

Альтерантивы оператору "=="
Для сравнения двух объектов в Java также существуют такие методы как: equals() и hashCode(). Методы hashCode() и equals() определены в классе Object, который является родительским классом для объектов Java. Поэтому все Java объекты наследуют от этих методов реализацию по умолчанию.
Использование equals() и hashCode()
Метод equals(), как и следует из его названия, используется для простой проверки равенства двух объектов. Реализация этого метода по умолчанию просто проверяет по ссылкам два объекта на предмет их эквивалентности, т.е. просто сравнивает ссылки.
Метод hashCode() обычно используется для получения уникального целого числа, что на самом деле не является правдой, т.к. результат работы данного метода - это целое число примитивного типа int (называемое хеш-кодом), полученное посредством работы хэш-функции входным параметром которой является объект, вызывающий данный метод, но множество возможных хеш-кодов ограничено примитивным типом int, а множество объектов ограничено только нашей фантазией. В итоге, имеем следующее:
если хеш-коды разные, то и объекты гарантированно разные если хеш-коды равны, то входные объекты не всегда равны (это обусловленно тем, что множество объектов мощнее множества хеш-кодод, т.к. множество возможных хеш-кодов ограничено примитивным типом int)
Также про данные методы можно прочитать здесь
Переопределение поведения по умолчанию.
Так как в Java нельзя переопределять операторы (т.е. поведеие оператора "==" не может быть изменено) как, например, в C++ или C#, то для сравнения двух объектов одного класса по необходимому для нас алгоритму, можно переопределить (@Override) методы equals() и hashCode() внутри нашего класса и использовать их.
Таким обарзом, создавая пользовательский класс следует переопределять методы hashCode() и equals(), чтобы они корректно работали и учитывали данные объекта. Кроме того, если оставить реализацию из Object, то, например, при использовании java.util.HashMap возникнут проблемы, поскольку HashMap активно используют hashCode() и equals() в своей работе.

Особенности при работе с вещественными типами Float и Double
В Java NaN'ы несравнимы между собой, но есть два исключения в работе классов Float и Double, рассмотрим на примере класса Float
Если float1 и float2 оба представляют Float.NaN, тогда метод equals возвращает true, в то время как Float.NaN == Float.NaN принимает значение false Если float1 содержит +0.0f в то время как float2 содержит -0.0f, метод equals возвращает false, в то время как 0.0f == -0.0f возвращает true
Контрольный пример 1:
Float float1 = new Float(Float.NaN); Float float2 = new Float(Float.NaN);
System.out.println(float1 == float2); // false System.out.println(float1.equals(float2)); // true System.out.println(Float.NaN == Float.NaN); // false
// --------------------------------------------------
Double double1 = new Double(Double.NaN); Double double2 = new Double(Double.NaN);
System.out.println(double1 == double2); // false System.out.println(double1.equals(double2)); // true System.out.println(Double.NaN == Double.NaN); // false
Контрольный пример 2:
Float float1 = new Float(0.0F); Float float2 = new Float(-0.0F);
System.out.println(float1 == float2); // false System.out.println(float1.equals(float2)); // false System.out.println(0.0F == -0.0F); // true
// --------------------------------------------------
Double double1 = new Double(0.0); Double double2 = new Double(-0.0);
System.out.println(double1 == double2); // false System.out.println(double1.equals(double2)); // false System.out.println(0.0 == -0.0); // true

Подведем итоги
Если вам необходимо проверить не ссылаются ли две переменных на один и тот же объект, либо сравнить на равенство два примитива (но стоит иметь в виду то, что вещественные числа следует сравнить лишь с определенной точностью), то вам определенно стоит воспользоваться оператором "==", но если же вам необходимо сравнить именно внутреннее содержимое объектов (либо для пользовательского типа сравнить по еще какому-то особому алгоритму), на которые ссылаются данные переменные, то вам стоит воспользоваться методом соответствующего класса equals() (причем если это пользовательский класс, то вам стоит переопределить данный метод и при этом не забыть про метод hashCode()).

Регистрация аккаунта разработчика Google Play и вывод денег в России

Мне нужно создать аккаунт разработчика в Google Play для того, чтобы выкладывать свои приложения и получать денежные средства с рекламы в этих приложениях. Какой банковский счёт для этого нужен? Где нужно регистрироваться? Какие есть нюансы при выводе денег в России? Я хотел бы поподробнее узнать про регистрацию аккаунта разработчика в Google Play, про то, какая кредитная карта для этого нужна, и где её получить, и про вывод денег в России.


Ответ

Нужна карта с возможностью платить в Интернете для первоначального взноса в 25 долларов в Google Play (заплатить надо всего один раз, в отличие от яблочного акка разработчика, где платить надо раз в год). Лично я пользовался виртуальной QIWI-картой. Для её получения нужна SIM-карта (для регистрации аккаунта в QIWI). Далее, на сайте или в приложении создаёте карту с любыми данными о владельце и платите. Нужен долларовый счёт с возможностью получать доллары из-за границы. Мне помог в этом Сбербанк (счёт открывают за $5, которые оказываются неснимаемой суммой на счету. Счёт называется "Универсальный"). Дали бумажную книжку по паспорту, реквизиты. Ввёл это в аккаунте - деньги пришли. Далее, если хочется удобств - можно оформить сбербанковскую же карту (за 780 рублей в виде страховки единовременной. Далее можно и не платить её. Нужен паспорт). С ней можно в онлайн-банке доллары в рубли перевести на карту и с карты уже в банкомате наличные снять. Либо по старинке отстоять очередь в отделении и снять наличные по паспорту и книжке. Для рекламы нужен отдельный аккаунт. В моём случае - AdMob. Там всё так же, за исключением первоначального взноса. Но есть один нюанс: после достижения некой пороговой суммы доходов за рекламу будет плашка в аккаунте повешена с предложением вбить адрес для высылки бумажного (sic!) письма с пин-кодом, который должен (после его получения и вбития в аккаунте) убедить AdMob в том, что вы реальный человек. Идёт оно из Калифорнии, готовьтесь к его длительному ожиданию. Сама регистрация не сложная. Просто вбиваем запрашиваемые данные.
Итог: Нужны SIM-карта, банковская карта, паспорт РФ, 30 долларов и чуть-чуть прямых рук.
P.S. Вот тут ещё чуть-чуть информации: Вывод денег из аккаунтов Google Play и AdMob