- Регистрация
- 09.07.2015
- Сообщения
- 357
- Благодарностей
- 566
- Баллы
- 93
Привет, дорогие зенщики!
Небольшое предисловие.
Обычно я стараюсь писать ботов на запросах, так как это не ресурсозатратно и в целом не сложно, если приложение слабозащищено.
Но бывают приложения, которые достаточно трудоёмко воспроизвести так, чтобы бот не умирал каждую неделю. Поэтому я решил, что некоторые вещи, например, регистраторы и фолловеры стоит делать используя автоматизацию эмуляторов.
В этой статье я поделюсь с вами своими наработками по автоматизации Android эмуляторов. И покажу как всё работает на примере эмулятора Nox и приложения Youtube.
Приступим.
Ранее я автоматизировал приложения через Appium, но меня не устраивали батники, запущенные окна cmd и невысокая гибкость всей системы в целом.
Поэтому я решил сделать автоматизацию напрямую, используя только ADB и UIAutomator. Для этого я использовал библиотеку SharpAdbClient.
Разделим статью на несколько частей для удобства:
- Установка и настройка необходимого софта
- Принцип работы. Разбор методов. XPath.
- Определение адреса (ip:port) эмулятора, организация многопоточного режима и обработка зависаний.
1. Установка и настройка необходимого софта.
Установка UIAutomator для поиска элементов:
- Скачиваем архив под статьёй и распаковываем.
- Устанавливаем AndroidSDK. Eсли просит установить Java, то устанавливаем сначала её (jre-8u241-windows-x64.exe).
- Запускаем SDKManager и устанавливаем необходимые пакеты.
- Прописываем пути до SDK (если их нет) в переменные среды:
- Заходим в "Систему", через поиск Windows, либо комбинация клавиш Win + Pause/Break.
- Дополнительные параметры системы
- Переменные среды
Создаём переменную ANDROID_HOME и прописываем путь до папки с SDK.
В системной переменной Path прописываем пути до папки tools и platform-tools
В системной переменной Path прописываем пути до папки tools и platform-tools
- Создаём ярлык для uiautomatorviewer.bat на рабочем столе, сам файл можно найти по пути "android-sdk/tools/uiautomatorviewer.bat", там куда вы установили AndroidSDK.
- Запускаем uiautomatorviewer.bat для теста. Если появилось окно под названием UI Automator Viewer, значит всё хорошо.
Установка и настройка эмулятора и ZennoPoster:
- Перемещаем библиотеки (.dll) из ExternalAssemblies в директорию ZennoPoster
- Скачиваем и устанавливаем Nox.
- Запускаем MultiDrive и создаём 3 эмулятора для теста (для удобства в настройках можно выставить телефонную ориентацию)
- Добавляем шаблон в ZennoPoster. Прописываем необходимые пути в настройках (nox_adb.exe и nox.exe в папке bin).
- Запускаем последовательно 3 эмулятора, затем устанавливаем 3 потока в ZennoPoster, и запускаем для теста. Должна пойти автоматизация в Youtube.
2. Принцип работы. Разбор методов. XPath.
Как получается связать adb с эмулятором без посредника (например Appium)?
У adb есть множество команд для управления Android. Например, команда
cmd.exe:
adb shell input tap x y
Соответственно, чтобы тапнуть по нужному элементу, нам надо узнать координаты этого элемента.
На эмуляторе по умолчанию предустановлен UIAutomator, поэтому вы можете получать данные видимых элементов с помощью команды:
cmd.exe:
adb shell uiautomator dump
Теперь команда будет выглядеть так:
cmd.exe:
adb shell uiautomator dump /dev/tty
Приведу небольшой список часто используемых команд, которые поддерживает adb:
cmd.exe:
adb help //вывести весь список комманд
adb start-server //запустить adb сервер
adb kill-server //закрыть adb сервер
adb connect <ip:port> //подключиться к устройству
adb devices //показать список подключенных устройств
adb reboot //перезагрузить устройство
adb install <path to .apk> //установить .apk файл
adb shell // Запускать команды через терминал Shell
adb shell input x y //тапнуть по координатам
adb shell input swipe x1 y1 x2 y2 sss //свайпнуть по координатам, где sss - скорость в миллисекундах
adb shell input text <string> //отправить текст
adb shell input keyevent <event_code> //отправить event (полный список event'ов будет ниже)
adb shell pm list packages //показать установленные пакеты
adb shell pm uninstall <com.your.app> //удалить пакет
adb shell dumpsys window windows | grep -E 'mCurrentFocus|mFocusedApp' //показать название текущей активности
Теперь перейдем к шаблону.
В GAC уже добавлены необходимые .dll, а в директиве using и общем коде добавлены необходимые пространства имён.
Директивы using и общий код:
using SharpAdbClient;
using System.Net;
using System.Xml;
using System.Windows.Forms;
using System.Diagnostics;
using System.Management;
В общем коде я написал методы для удобства, обзор которых будет на видео в конце статьи.
Пройдемся по готовым кубикам.
Начнём с проверки запущен ли adb сервер по пути из переменной settings_adb:
C#:
if (!AdbServer.Instance.GetStatus().IsRunning) {
AdbServer server = new AdbServer();
var result = server.StartServer(project.Variables["settings_adb"].Value, restartServerIfNewer: false);
throw new Exception("Restart.");
}
Подключаемся к ip:port эмулятора. Обычно он автоматически подключается, но подстрахуемся командой:
C#:
AdbClient.Instance.Connect(new DnsEndPoint("127.0.0.1", int.Parse(Regex.Match(project.Variables["device"].Value, "(?<=:).*").ToString())));
Cоздаём объект ADB и сохраняем его в контексте, чтобы использовать далее в проекте:
C#:
ADB a = new ADB(project);
project.Context["ADB"] = a;
Создаём объект PackageManager, который позволит нам Удалять/Устанавливать приложение в этом же сниппете:
C#:
var a = project.Context["ADB"];
var device = a.Device();
SharpAdbClient.DeviceCommands.PackageManager manager = new SharpAdbClient.DeviceCommands.PackageManager(device);
try {
manager.UninstallPackage("com.google.android.youtube"); //удаление пакета
} catch (Exception e) {}
manager.InstallPackage(project.Directory + @"\youtube.apk", reinstall: false); //установка apk
Осуществляем запуск приложения:
C#:
var a = project.Context["ADB"];
a.StartApp("com.google.android.youtube/com.google.android.apps.youtube.app.WatchWhileActivity"); //запуск приложения
// в CMD получить наименование текущего окна, которое нужно вставить в этот запрос
// adb shell dumpsys window windows | grep -E 'mCurrentFocus|mFocusedApp'
Ожидаем появление элемента:
C#:
var a = project.Context["ADB"];
a.Wait("//node[@resource-id='' and @class='android.widget.ImageView']", 0, 10); //XPath, Index, Секунды
Тапаем на элемент:
C#:
var a = project.Context["ADB"];
a.Click("//node[@resource-id='com.google.android.youtube:id/menu_search' and @class='android.widget.TextView']", 0, 10); //XPath, Index, Секунды
Учимся составлять свой XPath (это несложно):
Для начала запустим Nox и убедимся, что он подключен к adb.
Если подключенных устройств нет, тогда придется подключить эмулятор самостоятельно с помощью команды:
Теперь установим и запустим youtube.apk из архива, а затем запустим uiautomatorviewer.bat и сделаем dump окна.
Вы можете поводить курсором по элементам и заметите, что в правой части окна выделяются соответствующие узлы (node). Соответственно, чтобы составить XPath мы должны выделить нужный нам элемент и взять атрибуты, которые могут идентифицировать элемент в дереве.
Составим XPath для лупы (поиск), взяв атрибуты resource-id и class:
Если случается, что элемент невозможно идентифицировать (не указаны атрибуты), то в таком случае можно составить длинный XPath, например до элемента Home слева от лупы:
Весь синтаксис XPath можно подсмотреть здесь.
cmd.exe:
adb devices
Вы должны увидеть своё устройство в списке.
cmd.exe:
adb connect 127.0.0.1:62001
Вы должны получить сообщение о подключении. Обычно порт первого эмулятора 62001, но в моём случае 62025.
Если не получается поймать порт, то пробуйте перебор от 62001 до 62100.
Если не получается поймать порт, то пробуйте перебор от 62001 до 62100.
Составим XPath для лупы (поиск), взяв атрибуты resource-id и class:
XPath:
//node[@resource-id='com.google.android.youtube:id/menu_search' and @class='android.widget.TextView']
Если случается, что элемент невозможно идентифицировать (не указаны атрибуты), то в таком случае можно составить длинный XPath, например до элемента Home слева от лупы:
XPath:
//node[@resource-id='com.google.android.youtube:id/toolbar' and @class='android.view.View']/node[@class='android.widget.TextView']
Ввод текста и нажатие event'а (Enter):
C#:
var a = project.Context["ADB"];
a.Text("ZennoLab"); //ввод текста
a.KeyEvent("66"); //Enter
0 --> "KEYCODE_UNKNOWN"
1 --> "KEYCODE_MENU"
2 --> "KEYCODE_SOFT_RIGHT"
3 --> "KEYCODE_HOME"
4 --> "KEYCODE_BACK"
5 --> "KEYCODE_CALL"
6 --> "KEYCODE_ENDCALL"
7 --> "KEYCODE_0"
8 --> "KEYCODE_1"
9 --> "KEYCODE_2"
10 --> "KEYCODE_3"
11 --> "KEYCODE_4"
12 --> "KEYCODE_5"
13 --> "KEYCODE_6"
14 --> "KEYCODE_7"
15 --> "KEYCODE_8"
16 --> "KEYCODE_9"
17 --> "KEYCODE_STAR"
18 --> "KEYCODE_POUND"
19 --> "KEYCODE_DPAD_UP"
20 --> "KEYCODE_DPAD_DOWN"
21 --> "KEYCODE_DPAD_LEFT"
22 --> "KEYCODE_DPAD_RIGHT"
23 --> "KEYCODE_DPAD_CENTER"
24 --> "KEYCODE_VOLUME_UP"
25 --> "KEYCODE_VOLUME_DOWN"
26 --> "KEYCODE_POWER"
27 --> "KEYCODE_CAMERA"
28 --> "KEYCODE_CLEAR"
29 --> "KEYCODE_A"
30 --> "KEYCODE_B"
31 --> "KEYCODE_C"
32 --> "KEYCODE_D"
33 --> "KEYCODE_E"
34 --> "KEYCODE_F"
35 --> "KEYCODE_G"
36 --> "KEYCODE_H"
37 --> "KEYCODE_I"
38 --> "KEYCODE_J"
39 --> "KEYCODE_K"
40 --> "KEYCODE_L"
41 --> "KEYCODE_M"
42 --> "KEYCODE_N"
43 --> "KEYCODE_O"
44 --> "KEYCODE_P"
45 --> "KEYCODE_Q"
46 --> "KEYCODE_R"
47 --> "KEYCODE_S"
48 --> "KEYCODE_T"
49 --> "KEYCODE_U"
50 --> "KEYCODE_V"
51 --> "KEYCODE_W"
52 --> "KEYCODE_X"
53 --> "KEYCODE_Y"
54 --> "KEYCODE_Z"
55 --> "KEYCODE_COMMA"
56 --> "KEYCODE_PERIOD"
57 --> "KEYCODE_ALT_LEFT"
58 --> "KEYCODE_ALT_RIGHT"
59 --> "KEYCODE_SHIFT_LEFT"
60 --> "KEYCODE_SHIFT_RIGHT"
61 --> "KEYCODE_TAB"
62 --> "KEYCODE_SPACE"
63 --> "KEYCODE_SYM"
64 --> "KEYCODE_EXPLORER"
65 --> "KEYCODE_ENVELOPE"
66 --> "KEYCODE_ENTER"
67 --> "KEYCODE_DEL"
68 --> "KEYCODE_GRAVE"
69 --> "KEYCODE_MINUS"
70 --> "KEYCODE_EQUALS"
71 --> "KEYCODE_LEFT_BRACKET"
72 --> "KEYCODE_RIGHT_BRACKET"
73 --> "KEYCODE_BACKSLASH"
74 --> "KEYCODE_SEMICOLON"
75 --> "KEYCODE_APOSTROPHE"
76 --> "KEYCODE_SLASH"
77 --> "KEYCODE_AT"
78 --> "KEYCODE_NUM"
79 --> "KEYCODE_HEADSETHOOK"
80 --> "KEYCODE_FOCUS"
81 --> "KEYCODE_PLUS"
82 --> "KEYCODE_MENU"
83 --> "KEYCODE_NOTIFICATION"
84 --> "KEYCODE_SEARCH"
85 --> "TAG_LAST_KEYCODE"
1 --> "KEYCODE_MENU"
2 --> "KEYCODE_SOFT_RIGHT"
3 --> "KEYCODE_HOME"
4 --> "KEYCODE_BACK"
5 --> "KEYCODE_CALL"
6 --> "KEYCODE_ENDCALL"
7 --> "KEYCODE_0"
8 --> "KEYCODE_1"
9 --> "KEYCODE_2"
10 --> "KEYCODE_3"
11 --> "KEYCODE_4"
12 --> "KEYCODE_5"
13 --> "KEYCODE_6"
14 --> "KEYCODE_7"
15 --> "KEYCODE_8"
16 --> "KEYCODE_9"
17 --> "KEYCODE_STAR"
18 --> "KEYCODE_POUND"
19 --> "KEYCODE_DPAD_UP"
20 --> "KEYCODE_DPAD_DOWN"
21 --> "KEYCODE_DPAD_LEFT"
22 --> "KEYCODE_DPAD_RIGHT"
23 --> "KEYCODE_DPAD_CENTER"
24 --> "KEYCODE_VOLUME_UP"
25 --> "KEYCODE_VOLUME_DOWN"
26 --> "KEYCODE_POWER"
27 --> "KEYCODE_CAMERA"
28 --> "KEYCODE_CLEAR"
29 --> "KEYCODE_A"
30 --> "KEYCODE_B"
31 --> "KEYCODE_C"
32 --> "KEYCODE_D"
33 --> "KEYCODE_E"
34 --> "KEYCODE_F"
35 --> "KEYCODE_G"
36 --> "KEYCODE_H"
37 --> "KEYCODE_I"
38 --> "KEYCODE_J"
39 --> "KEYCODE_K"
40 --> "KEYCODE_L"
41 --> "KEYCODE_M"
42 --> "KEYCODE_N"
43 --> "KEYCODE_O"
44 --> "KEYCODE_P"
45 --> "KEYCODE_Q"
46 --> "KEYCODE_R"
47 --> "KEYCODE_S"
48 --> "KEYCODE_T"
49 --> "KEYCODE_U"
50 --> "KEYCODE_V"
51 --> "KEYCODE_W"
52 --> "KEYCODE_X"
53 --> "KEYCODE_Y"
54 --> "KEYCODE_Z"
55 --> "KEYCODE_COMMA"
56 --> "KEYCODE_PERIOD"
57 --> "KEYCODE_ALT_LEFT"
58 --> "KEYCODE_ALT_RIGHT"
59 --> "KEYCODE_SHIFT_LEFT"
60 --> "KEYCODE_SHIFT_RIGHT"
61 --> "KEYCODE_TAB"
62 --> "KEYCODE_SPACE"
63 --> "KEYCODE_SYM"
64 --> "KEYCODE_EXPLORER"
65 --> "KEYCODE_ENVELOPE"
66 --> "KEYCODE_ENTER"
67 --> "KEYCODE_DEL"
68 --> "KEYCODE_GRAVE"
69 --> "KEYCODE_MINUS"
70 --> "KEYCODE_EQUALS"
71 --> "KEYCODE_LEFT_BRACKET"
72 --> "KEYCODE_RIGHT_BRACKET"
73 --> "KEYCODE_BACKSLASH"
74 --> "KEYCODE_SEMICOLON"
75 --> "KEYCODE_APOSTROPHE"
76 --> "KEYCODE_SLASH"
77 --> "KEYCODE_AT"
78 --> "KEYCODE_NUM"
79 --> "KEYCODE_HEADSETHOOK"
80 --> "KEYCODE_FOCUS"
81 --> "KEYCODE_PLUS"
82 --> "KEYCODE_MENU"
83 --> "KEYCODE_NOTIFICATION"
84 --> "KEYCODE_SEARCH"
85 --> "TAG_LAST_KEYCODE"
Так же случаются ситуации, когда вам нужно сделать двойной тап или другие действия, которых нет в списке.
На помощь приходит команда sendevent:
cmd.exe:
adb shell sendevent /dev/input/event<x>
Получаем координаты элемента, с помощью метода получения координат.
Вы можете получать "чистые" координаты, либо уже высчитанную рандомную точку. В нашем случае используем сразу второй вариант:
C#:
var a = project.Context["ADB"];
string coord = a.GetCoord("//node[@resource-id='com.google.android.youtube:id/channel_avatar' and @class='android.widget.ImageView']", 0, 10, true); //XPath, Index, Секунды, рандомная точка между x1,y1 и x2,y2
string[] coords = coord.Split(new char[] {','});
project.Variables["x"].Value = coords[0];
project.Variables["y"].Value = coords[1];
Введем команду в cmd, кликнем на эмуляторе и запишем событие:
cmd.exe:
adb shell getevent
Получится:
Код:
/dev/input/event<N>: 1 330 1
/dev/input/event<N>: 3 58 1
/dev/input/event<N>: 3 53 <x>
/dev/input/event<N>: 3 54 <y>
/dev/input/event<N>: 0 2 0
/dev/input/event<N>: 0 0 0
/dev/input/event<N>: 0 2 0
/dev/input/event<N>: 0 0 0
/dev/input/event<N>: 1 330 0
/dev/input/event<N>: 3 58 0
/dev/input/event<N>: 3 53 0
/dev/input/event<N>: 3 54 38
/dev/input/event<N>: 0 2 0
/dev/input/event<N>: 0 0 0
C#:
var a = project.Context["ADB"];
a.Command("cat /proc/bus/input/devices", true);
project.Variables["event"].Value = Regex.Match(project.Variables["receiver"].Value, "(?<=mouse2 event).*").ToString().Trim(); //получение № ивэнта
Тапаем с помощью sendevent, подставив номер event'а и координаты (в нашем случае двойной тап не нужен, поэтому я закомментил вторую часть в шаблоне):
C#:
var a = project.Context["ADB"];
string evnt = project.Variables["event"].Value;
string x = project.Variables["x"].Value;
string y = project.Variables["y"].Value;
a.Command(String.Format("sendevent /dev/input/event{0} 1 330 1", evnt), false);
a.Command(String.Format("sendevent /dev/input/event{0} 3 58 1", evnt), false);
a.Command(String.Format("sendevent /dev/input/event{0} 3 53 {1}", evnt, x), false);
a.Command(String.Format("sendevent /dev/input/event{0} 3 54 {1}", evnt, y), false);
a.Command(String.Format("sendevent /dev/input/event{0} 0 2 0", evnt), false);
a.Command(String.Format("sendevent /dev/input/event{0} 0 0 0", evnt), false);
a.Command(String.Format("sendevent /dev/input/event{0} 0 2 0", evnt), false);
a.Command(String.Format("sendevent /dev/input/event{0} 0 0 0", evnt), false);
a.Command(String.Format("sendevent /dev/input/event{0} 1 330 0", evnt), false);
a.Command(String.Format("sendevent /dev/input/event{0} 3 58 0", evnt), false);
a.Command(String.Format("sendevent /dev/input/event{0} 3 53 0", evnt), false);
a.Command(String.Format("sendevent /dev/input/event{0} 3 54 38", evnt), false);
a.Command(String.Format("sendevent /dev/input/event{0} 0 2 0", evnt), false);
a.Command(String.Format("sendevent /dev/input/event{0} 0 0 0", evnt), false);
Свайпаем:
C#:
var a = project.Context["ADB"];
a.Swipe("200", "600", "200", "200", "900"); //координаты x1, y1, x2, y2, sss - скорость в милисекундах
Также можно спарсить что-нибудь. Спарсим длительность видимых роликов (результат появится в списке шаблона):
C#:
var a = project.Context["ADB"];
project.Lists["parse"].AddRange(a.Parse("//node[@resource-id='com.google.android.youtube:id/duration' and @class='android.widget.TextView']", "text", 0, 5)); //XPath, какой аттрибут парсить, Index, Секунды
Button Back:
C#:
var a = project.Context["ADB"];
a.Back();
Button Home и Kill Process:
C#:
var a = project.Context["ADB"];
a.Home(); //Свернуть все окна
a.Kill("com.google.android.youtube"); //Убить процесс
Перезагрузить Android:
C#:
var a = project.Context["ADB"];
a.Reboot();
Загрузка файла. Я написал 2 метода для загрузки файла: обычная загрузка
C#:
var a = project.Context["ADB"];
a.UploadFile("/storage/sdcard0/ZennoLab.txt", project.Directory + @"\ZennoLab.txt");
C#:
var a = project.Context["ADB"];
a.UploadFromVar("/storage/sdcard0/ZennoLab.txt", "ZennoLab TEST");
Скачивание файла:
C#:
var a = project.Context["ADB"];
a.DownloadFile("/storage/sdcard0/ZennoLab.txt", project.Directory + @"\Download_test.txt");
Удаление Файла:
C#:
var a = project.Context["ADB"];
a.Command("rm -rf /storage/sdcard0/ZennoLab.txt", false);
3. Определение IP+PORT эмулятора, организация многопоточного режима и обработка зависаний.
Для организации многопоточного режима я решил использовать залоченную глобальную переменную, в которой будут записаны id процессов (pid) окон эмуляторов через ";".
Когда шаблон запускается, то в глобальную переменную записывается pid первого по списку свободного эмулятора. Если в переменной не было этого pid, то другой поток не будет работать с этим pid.
Сам pid можно найти в Диспетчере задач (Ctrl+Shift+Esc).
Далее если ввести в cmd команду:
cmd.exe:
netstat -a -n -o
Зная, что порты для подключение adb у Nox начинаются с 620, мы можем взять адрес по pid'у с помощью команды:
cmd.exe:
netstat -a -n -o | find "PID" | find "127.0.0.1" | find "620"
Теперь сделаем тоже самое в зенке.
Инициализируем/проверяем существование глобальной переменной:
C#:
lock(SyncObject) {
try {
var gbVar = project.GlobalVariables["Zappium", "process"];
return null;
} catch (KeyNotFoundException ex) {
string defaultValue = String.Empty;
project.GlobalVariables.SetVariable("Zappium", "process", defaultValue);
}
}
Получаем свободный PID с помощью стандартного инструментария .Net:
C#:
lock(SyncObject) {
var gbVar = project.GlobalVariables["Zappium", "process"];
Process[] processes = Process.GetProcessesByName("NoxVMHandle");
var ids = processes.Select(p => p.Id);
string process = "";
foreach(int processId in ids){
project.SendInfoToLog(processId.ToString());
process = processId.ToString();
if (project.Variables["process"].Value == String.Empty && !project.GlobalVariables["Zappium", "process"].Value.ToString().Contains(process)){
project.Variables["process"].Value = process;
gbVar.Value = gbVar.Value + process + ";";
}
}
}
Получаем адрес устройства, подставив pid в ранее написанную команду:
C#:
Process cmd = new Process();
cmd.StartInfo.FileName = "cmd.exe";
cmd.StartInfo.RedirectStandardInput = true;
cmd.StartInfo.RedirectStandardOutput = true;
cmd.StartInfo.CreateNoWindow = true;
cmd.StartInfo.UseShellExecute = false;
cmd.Start();
cmd.StandardInput.WriteLine(String.Format("netstat -a -n -o | find \"{0}\" | find \"127.0.0.1\" | find \"620\"",project.Variables["process"].Value));
cmd.StandardInput.Flush();
cmd.StandardInput.Close();
cmd.WaitForExit();
return "127.0.0.1:" + Regex.Match(cmd.StandardOutput.ReadToEnd(), "(?<=127.0.0.1:)62.*?(?= )");
По завершению работы шаблона удаляем из глобальной переменной pid, чтобы освободить окно для следующих потоков:
C#:
lock(SyncObject) {
var gbVar = project.GlobalVariables["Zappium", "process"];
gbVar.Value = gbVar.Value.ToString().Replace(project.Variables["process"].Value + ";", "");
}
Порой случается, что эмулятор зависает, для этого я использую метод CommandLineUtilities, прописанный в общем коде, чтобы записать все процессы связанные с эмулятором, закрыть и открыть их заново.
Закрываем процессы:
C#:
Process[] processes = Process.GetProcessesByName("Nox");
var ids = processes.Select(p => p.Id);
var process = Process.GetProcessById(int.Parse(project.Variables["process"].Value));
string name = Regex.Match(CommandLineUtilities.getCommandLines(process), "(?<=--comment ).*(?= --startvm)").ToString();
project.SendInfoToLog(CommandLineUtilities.getCommandLines(process));
foreach(int processId in ids){
var nox = Process.GetProcessById(processId);
if (Regex.Match(CommandLineUtilities.getCommandLines(nox),"(?<=-clone:).*").ToString().Contains(name)){
project.Variables["start_cmd"].Value = CommandLineUtilities.getCommandLines(nox);
nox.Kill();
break;
}
}
process.Kill();
Открываем эмулятор заново:
C#:
var proc = System.Diagnostics.Process.Start(project.Variables["settings_nox"].Value, Regex.Match(project.Variables["start_cmd"].Value, "-clone:.*").ToString());
Заключение.
Надеюсь моя статья облегчит вам работу с эмуляторами и поможет создать более совершенную схему взаимодействия с ними. Это лишь небольшая часть того, как можно взаимодействовать с эмуляторами.
Каждый ваш голос за статью будет мотивировать меня делиться многими полезными фишками в следующих частях.
Спасибо за внимание, друзья!
- Тема статьи
- Нестандартные хаки
- Номер конкурса статей
- Тринадцатый конкурс статей
Вложения
-
17,8 МБ Просмотры: 2 720
Для запуска проектов требуется программа ZennoPoster или ZennoDroid.
Это основное приложение, предназначенное для выполнения автоматизированных шаблонов действий (ботов).
Подробнее...
Для того чтобы запустить шаблон, откройте нужную программу. Нажмите кнопку «Добавить», и выберите файл проекта, который хотите запустить.
Подробнее о том, где и как выполняется проект.
Последнее редактирование: