О чём статья
Почему 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();
}
2) Ввод: тап, текст, свайп, «назад»
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");
3) Снятие UI без зависаний
C#:
string DumpUI()
{
RunAdb($"-s {serial} shell uiautomator dump /sdcard/ui.xml");
return RunAdb($"-s {serial} shell cat /sdcard/ui.xml");
}
Как «читать» экран: парсим UIAutomator 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('@')}");
Если Telegram уже открыт поверх — система часто пишет:
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.
Приложил видео работы проекта
Последнее редактирование:



