О чём статья
Почему AVD (Android Studio) + ZennoPoster
AVD — официальный эмулятор Android. Он:
Итог: минимальная связка, высокая скорость, гибкость и полная управляемость.
Быстрый старт (чек-лист)
Базовые операции: готовые сниппеты
1) Быстрый вызов adb из C# блока ZennoPoster
2) Ввод: тап, текст, свайп, «назад»
3) Снятие UI без зависаний
Как «читать» экран: парсим UIAutomator XML
В DumpUI() приходит XML со структурой элементов. Можно искать:
Примечание: структура у разных приложений может отличаться — смотрите реальный XML (project.Variables["test"] = DumpUI()
и корректируйте XPath.
Практика на примере Telegram
Переход в канал/группу по схеме «deeplink + intent»
Если Telegram уже открыт поверх — система часто пишет:
Activity not started, intent has been delivered to currently running top-most instance.
Это норм: приложение получило интент и само переключило экран.
«Прыгнуть к последним» (если есть FAB)
Подходы:
«Анти-ловушки» и лайфхаки стабильности
Когда нужен Appium / UIAutomator2
Готовый «скелет» сценария (свайп + сбор)
Диагностика и логирование (чек-лист)
Безопасность и этика
Почему иногда пусто в списке сообщений?
Чаще всего — из-за XPath (у вашего приложения другой класс/расположение текста). Сохраните DumpUI() в переменную, откройте XML и подстройте SelectNodes().
Почему срабатывает «Поделиться/реакция»?
Короткий/правый свайп похож на tap/long-press. Свайпайте слева и держите длительность ~140–200 мс.
uiautomator dump зависает.
Используйте «через файл»: dump /sdcard/ui.xml → cat /sdcard/ui.xml.
Нужно ли Appium?
Если нужен надёжный поиск по resource-id, сложные ожидания внутри активности или кросс-девайс сценарии — да. Для «читать/листать/нажимать» чаще достаточно ADB.
Итоги
- Как напрямую управлять Android-эмулятором Android Studio AVD из ZennoPoster (через adb).
- Как нажимать, свайпить, печатать и снимать UI без Appium и сторонних «прокладок».
- Как стабильно работать с «прожорливыми» мессенджерами (пример: Telegram).
- Готовые сниппеты C# для вставки в ZennoPoster, чек-листы стабильности и отладки.
Почему AVD (Android Studio) + ZennoPoster
AVD — официальный эмулятор Android. Он:
- максимально совместим с новыми API/SDK;
- не тащит лишний софт/рекламу;
- предсказуем по координатам и поведению ввода;
- поддерживает «родной» инструментарий (ADB, UIAutomator).
- легко миксует HTTP/API-шаги и мобильную автоматизацию;
- хранит состояния (Таблицы/Списки/Переменные);
- позволяет писать «тонкие» C#-обработчики для adb/UI.
Итог: минимальная связка, высокая скорость, гибкость и полная управляемость.
Архитектура решения:
ZennoPoster (C# блоки)
│
├─ запускает adb.exe с нужными аргументами
│
├─ отправляет команды ввода:
│ input tap / text / swipe / keyevent
│
├─ снимает разметку экрана:
│ uiautomator dump → /sdcard/ui.xml → cat
│
└─ парсит XML (bounds, class, text, content-desc)
- Поставьте Android Studio → SDK Platform-Tools (там adb.exe).
Пример пути:
C:\Users\User\AppData\Local\Android\Sdk\platform-tools\adb.exe - Создайте AVD (напр., Pixel 7 / Android 14–15). Запустите эмулятор.
В adb devices -l увидите что-то вроде emulator-5554. - Подготовьте ZennoPoster: блок C# + ваш путь к adb и serial AVD.
- Проверьте команду:
cmd:
adb -s emulator-5554 shell getprop ro.product.model
1) Быстрый вызов adb из C# блока ZennoPoster
C#:
string adb = @"C:\Users\User\AppData\Local\Android\Sdk\platform-tools\adb.exe";
string serial = "emulator-5554";
string RunAdb(string args, int timeoutMs = 1500)
{
var psi = new System.Diagnostics.ProcessStartInfo {
FileName = adb, Arguments = args,
UseShellExecute = false, CreateNoWindow = true,
RedirectStandardOutput = true, RedirectStandardError = true,
StandardOutputEncoding = System.Text.Encoding.UTF8,
StandardErrorEncoding = System.Text.Encoding.UTF8
};
using var p = new System.Diagnostics.Process { StartInfo = psi };
p.Start();
if (!p.WaitForExit(timeoutMs)) { try { p.Kill(); } catch {} }
var err = p.StandardError.ReadToEnd();
if (!string.IsNullOrWhiteSpace(err) && !err.Contains("dumped to"))
project.SendWarningToLog("[ADB] " + err);
return p.StandardOutput.ReadToEnd();
}
C#:
void Tap(int x, int y) =>
RunAdb($"-s {serial} shell input tap {x} {y}");
void TypeText(string text) =>
RunAdb($"-s {serial} shell input text {text}");
void Swipe(int x1,int y1,int x2,int y2,int ms=150) =>
RunAdb($"-s {serial} shell input swipe {x1} {y1} {x2} {y2} {ms}");
void Back() => RunAdb($"-s {serial} shell input keyevent KEYCODE_BACK");
C#:
string DumpUI()
{
RunAdb($"-s {serial} shell uiautomator dump /sdcard/ui.xml");
return RunAdb($"-s {serial} shell cat /sdcard/ui.xml");
}
В DumpUI() приходит XML со структурой элементов. Можно искать:
- @class (например, androidx.recyclerview.widget.RecyclerView);
- @text — текст у виджета (часто у «листовых» сообщений);
- @Content-desc — подпись для доступности (иконки, FAB);
- @bounds — координаты [x1,y1][x2,y2].
C#:
List<string> ExtractMessages(string xml)
{
var res = new List<string>();
var m = System.Text.RegularExpressions.Regex.Match(xml ?? "", "(<hierarchy[\\s\\S]*?</hierarchy>)");
if (!m.Success) return res;
var doc = new System.Xml.XmlDocument();
doc.LoadXml(m.Groups[1].Value);
// Многие мессенджеры кладут «текст сообщения» прямо в text узлов ViewGroup
var nodes = doc.SelectNodes("//node[@class='androidx.recyclerview.widget.RecyclerView']/node[@class='android.view.ViewGroup' and @text!='']");
if (nodes != null)
foreach (System.Xml.XmlNode n in nodes)
res.Add(n.Attributes?["text"]?.Value ?? "");
return res;
}
и корректируйте XPath.Практика на примере Telegram
Переход в канал/группу по схеме «deeplink + intent»
C#:
// Жёстко: Telegram уже установлен в профиле AVD с Play Services.
string tgGroup = "@gruz"; // или t.me/...
RunAdb($"-s {serial} shell am start -a android.intent.action.VIEW -d https://t.me/{tgGroup.TrimStart('@')}");
Activity not started, intent has been delivered to currently running top-most instance.
Это норм: приложение получило интент и само переключило экран.
«Прыгнуть к последним» (если есть FAB)
- снимаем XML → ищем content-desc/text с ключами типа «в конец», «последним», «jump», «new message»;
- тапаем в центр найденной кнопки (по bounds);
- если не нашли — делаем пару свайпов вверх по левому краю (чтобы избежать случайного «поделиться/реакции»).
Подходы:
- Скроллить и парсить каждую «порцию» UI (быстро, но зависит от верстки).
- Не скроллить, а ждать 30 сек и каждые N секунд снимать UI (полезно для «живых» каналов).
- свайпать по левому краю,
- не делать слишком короткие свайпы (иначе эмулятор считает их tap),
- избегать долгих свайпов (>500 мс), чтобы не сработал long-press.
«Анти-ловушки» и лайфхаки стабильности
- Шторка «Поделиться». При слишком длинном/правом свайпе может выпадать системный «chooser».
Решение: свайпать по левому краю, при детекте «resolver/chooser/share» делать KEYCODE_BACK. - Залипания uiautomator. Команда exec-out uiautomator dump иногда «виснет».
Решение: использовать «надёжную» схему
uiautomator dump /sdcard/ui.xml → cat /sdcard/ui.xml. - Тайминги. Избыточные Thread.Sleep(1000+) убивают скорость.
На практике хватает 30–120 мс между жестами, 20–50 мс перед дампом. - Координаты. Нормируйте от размеров экрана (wm size), а не хардкод.
- Кодировка. Если видите «кракозябры», попробуйте «перекинуть» как 1251/1252→UTF-8 (простые фиксы в коде).
Когда нужен Appium / UIAutomator2
- Нужны локаторы по ресурсу/иерархии и стабильные клики без координат.
- Сложные сценарии с ожиданиями и взаимодействием изнутри Android (Accessibility).
- Мульти-эмуляторы и тонкий контроль времени анимаций.
Готовый «скелет» сценария (свайп + сбор)
C#:
// 1) уже перешли в группу (интентом) и «провалились в самый низ» другой подзадачей
// 2) здесь: делаем 10 свайпов «в историю» и собираем тексты как есть
IZennoList outList = project.Lists["Список 1"];
outList.Clear();
string adb = @"C:\Users\User\AppData\Local\Android\Sdk\platform-tools\adb.exe";
string serial = "emulator-5554";
string RunAdb(string args, int t=900) { /* из блока выше */ return ""; }
void Swipe(int x1,int y1,int x2,int y2,int ms=160) => RunAdb($"-s {serial} shell input swipe {x1} {y1} {x2} {y2} {ms}");
string DumpUI(){ RunAdb($"-s {serial} shell uiautomator dump /sdcard/ui.xml"); return RunAdb($"-s {serial} shell cat /sdcard/ui.xml"); }
(int W,int H) GetWH(){
var m = System.Text.RegularExpressions.Regex.Match(RunAdb($"-s {serial} shell wm size"), @"(\d+)\s*x\s*(\d+)");
return m.Success ? (int.Parse(m.Groups[1].Value), int.Parse(m.Groups[2].Value)) : (1080,2400);
}
var (W,H) = GetWH();
int xL = (int)(W*0.08);
int y1 = (int)(H*0.12), y2 = (int)(H*0.90);
System.Collections.Generic.List<string> Extract(string xml){
var res = new System.Collections.Generic.List<string>();
var mm = System.Text.RegularExpressions.Regex.Match(xml ?? "", "(<hierarchy[\\s\\S]*?</hierarchy>)");
if (!mm.Success) return res;
var doc = new System.Xml.XmlDocument(); doc.LoadXml(mm.Groups[1].Value);
var nodes = doc.SelectNodes("//node[@class='androidx.recyclerview.widget.RecyclerView']/node[@class='android.view.ViewGroup' and @text!='']");
if (nodes!=null) foreach(System.Xml.XmlNode n in nodes) res.Add(n.Attributes?["text"]?.Value ?? "");
return res;
}
// текущий кадр + 10 свайпов
var seen = new System.Collections.Generic.HashSet<string>();
for (int i=0;i<11;i++){
var xml = DumpUI();
foreach (var t in Extract(xml))
if (!string.IsNullOrWhiteSpace(t) && seen.Add(t)) outList.Add(t);
if (i==10) break; // дефолт: 10 свайпов
Swipe(xL, y1, xL, y2); // вниз (в историю)
}
project.SendInfoToLog($"[OK] Собрано: {outList.Count}");
return 0;
- Писать в лог путь к adb, serial, размеры экрана.
- Хранить сырой XML последнего DumpUI() в переменной test для быстрой отладки XPath.
- Фиксировать «подозрительные» stderr из adb (но не ругаться на «dumped to /sdcard/ui.xml»).
- Если что-то «случайно нажимается» — перенести жесты ещё левее и чуть удлинить ms.
Безопасность и этика
- Уважайте правила площадок и приватность пользователей.
- Не автоматизируйте действий, запрещённых ToS/законами.
- Работайте в тестовых/демо-окружениях и своих аккаунтах.
Почему иногда пусто в списке сообщений?
Чаще всего — из-за XPath (у вашего приложения другой класс/расположение текста). Сохраните DumpUI() в переменную, откройте XML и подстройте SelectNodes().
Почему срабатывает «Поделиться/реакция»?
Короткий/правый свайп похож на tap/long-press. Свайпайте слева и держите длительность ~140–200 мс.
uiautomator dump зависает.
Используйте «через файл»: dump /sdcard/ui.xml → cat /sdcard/ui.xml.
Нужно ли Appium?
Если нужен надёжный поиск по resource-id, сложные ожидания внутри активности или кросс-девайс сценарии — да. Для «читать/листать/нажимать» чаще достаточно ADB.
Итоги
- ZennoPoster + AVD даёт нативный, быстрый и прозрачный способ автоматизировать Android без посредников.
- Базовых кирпичиков (tap/text/swipe/dump + XPath) достаточно для 80% задач: от чтения лент до аккуратных действий в приложениях.
- Стабильность обеспечивают левый край свайпов, корректные тайминги и «быстрый» дамп UI.
Приложил видео работы проекта
Для запуска проектов требуется программа ZennoPoster.
Это основное приложение, предназначенное для выполнения автоматизированных шаблонов действий (ботов).
Подробнее...
Для того чтобы запустить шаблон, откройте программу ZennoPoster. Нажмите кнопку «Добавить», и выберите файл проекта, который хотите запустить.
Подробнее о том, где и как выполняется проект.
Последнее редактирование:



