Что такое ReDroid?
ReDroid — это легковесная альтернатива стандартному эмулятору Android, работающая как Docker-контейнер. ReDroid предоставляет полноценную Android-систему в контейнере, позволяя значительно сократить время запуска и потребление ресурсов по сравнению с традиционными эмуляторами.
ReDroid использует ядро Linux хост-системы и работает на базе проекта anbox-модуля, что обеспечивает запуск Android без виртуализации процессора. Это делает его идеальным решением для автоматизированного тестирования Android-приложений в среде CI/CD, где скорость и эффективность имеют решающее значение.
Сравнение с эмулятором Android Studio:
ReDroid идеально подходит для автоматизированного UI-тестирования в CI/CD, особенно когда важна скорость и эффективность использования ресурсов. Стандартный эмулятор Android Studio более подходит для локальной разработки, отладки и тестирования, требующего полной эмуляции возможностей устройства.
Ручное тестирование
Запустите контейнер ReDroid (Docker) и подождите около минуты для его загрузки:
docker run -d --name redroid --privileged -p 5555:5555 redroid/redroid:15.0.0_64only-latest
sleep 30
Подключитесь к запущенному контейнеру через ADB:
adb connect localhost:5555
adb devices
Установите на ReDroid оба APK-файла:
adb -s localhost:5555 install ./app/build/outputs/apk/debug/app-debug.apk
adb -s localhost:5555 install ./app/build/outputs/apk/androidTest/debug/app-debug-androidTest.apk
Запустите ваши Espresso/UI-тесты с ADB:
adb -s localhost:5555 shell am instrument -w your.package.name.test/androidx.test.runner.AndroidJUnitRunner
Выгрузите логи после тестирования:
adb -s localhost:5555 logcat -d > android-log.txt
После завершения тестов удалите контейнер:
docker stop redroid && docker rm redroid

Подготовка Android-проекта для UI-тестирования
Шаг 1: Настройка зависимостей в app/build.gradle
gradle
android {
defaultConfig {
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
}
dependencies {
// Основные компоненты для UI-тестирования
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
androidTestImplementation 'androidx.test:runner:1.5.2'
androidTestImplementation 'androidx.test:rules:1.5.0'
// Дополнительные библиотеки Espresso для расширенного тестирования
androidTestImplementation 'androidx.test.espresso:espresso-contrib:3.5.1'
androidTestImplementation 'androidx.test.espresso:espresso-intents:3.5.1'
// JUnit для интеграции с тестовым окружением
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
}
Шаг 2: Создание примера UI-теста с Espresso
Создайте файл app/src/androidTest/java/com/example/app/MainActivityTest.java
:
java
package com.example.app;
import androidx.test.espresso.matcher.ViewMatchers;
import androidx.test.ext.junit.rules.ActivityScenarioRule;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.LargeTest;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.action.ViewActions.click;
import static androidx.test.espresso.assertion.ViewAssertions.matches;
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
import static androidx.test.espresso.matcher.ViewMatchers.withId;
import static androidx.test.espresso.matcher.ViewMatchers.withText;
@RunWith(AndroidJUnit4.class)
@LargeTest
public class MainActivityTest {
@Rule
public ActivityScenarioRule<MainActivity> activityRule =
new ActivityScenarioRule<>(MainActivity.class);
@Test
public void welcomeTextIsDisplayed() {
// Проверка, что текстовое поле с приветствием отображается
onView(withId(R.id.welcome_text))
.check(matches(isDisplayed()))
.check(matches(withText("Hello World!")));
}
@Test
public void buttonClickChangesText() {
// Нажатие на кнопку
onView(withId(R.id.action_button))
.perform(click());
// Проверка, что текст изменился
onView(withId(R.id.welcome_text))
.check(matches(withText("Button Clicked!")));
}
}
Настройка Jenkins для автоматизации UI-тестов с ReDroid
Шаг 1: Установка необходимых плагинов в Jenkins
В интерфейсе Jenkins перейдите в раздел "Настроить Jenkins" > "Управление плагинами" и установите:
Docker Pipeline
Gradle Plugin
HTML Publisher Plugin
JUnit Plugin
Allure Jenkins Plugin (опционально для улучшенной отчетности)
Шаг 2: Создание Jenkinsfile для пайплайна
Создайте файл Jenkinsfile
в корне проекта:
groovy
pipeline {
agent any
environment {
// Переменные окружения
ANDROID_HOME = '/opt/android-sdk'
REDROID_IMAGE = 'redroid/redroid:15.0.0_64only-latest'
REDROID_CONTAINER = 'android-ui-test-container'
ADB_PORT = '5555'
TEST_RESULTS_DIR = 'app/build/outputs/androidTest-results'
TEST_REPORT_DIR = 'app/build/reports/androidTests'
}
stages {
stage('Checkout') {
steps {
checkout scm
}
}
stage('Build APKs') {
steps {
sh './gradlew clean assembleDebug assembleDebugAndroidTest'
}
}
stage('Start ReDroid Container') {
steps {
script {
// Остановка и удаление контейнера, если он существует
sh "docker stop ${REDROID_CONTAINER} || true"
sh "docker rm ${REDROID_CONTAINER} || true"
// Запуск ReDroid в Docker
sh """
docker run -d --name ${REDROID_CONTAINER} \
--privileged \
-p ${ADB_PORT}:${ADB_PORT} \
-e REDROID_PROP_ro.debuggable=1 \
-e REDROID_PROP_service.adb.tcp.port=${ADB_PORT} \
${REDROID_IMAGE}
"""
// Ожидание запуска ReDroid
sh "sleep 30"
}
}
}
stage('Connect to ReDroid and Install APKs') {
steps {
script {
// Подключение к устройству через ADB
sh "adb connect localhost:${ADB_PORT}"
sh "adb devices"
// Ожидание полной загрузки системы
sh "adb wait-for-device"
// Установка APK приложения и тестов
sh "adb -s localhost:${ADB_PORT} install -r app/build/outputs/apk/debug/app-debug.apk"
sh "adb -s localhost:${ADB_PORT} install -r app/build/outputs/apk/androidTest/debug/app-debug-androidTest.apk"
}
}
}
stage('Run UI Tests') {
steps {
script {
// Создание директорий для результатов
sh "mkdir -p ${TEST_RESULTS_DIR}"
// Получение имени пакета приложения
def packageName = sh(
script: "aapt dump badging app/build/outputs/apk/debug/app-debug.apk | grep package | awk '{print \$2}' | sed s/name=//g | sed s/\\'//g",
returnStdout: true
).trim()
// Запуск UI-тестов
sh """
adb -s localhost:${ADB_PORT} shell am instrument -w \
-e debug false \
-e junit.output.format=xml \
-e additionalTestOutputDir=/sdcard/test-results \
${packageName}.test/androidx.test.runner.AndroidJUnitRunner
"""
// Копирование результатов с устройства
sh "adb -s localhost:${ADB_PORT} pull /sdcard/test-results ${TEST_RESULTS_DIR}"
// Сбор логов
sh "adb -s localhost:${ADB_PORT} logcat -d > ${TEST_RESULTS_DIR}/device-log.txt"
// Сделать скриншот для диагностики (опционально)
sh "adb -s localhost:${ADB_PORT} shell screencap -p /sdcard/screen.png"
sh "adb -s localhost:${ADB_PORT} pull /sdcard/screen.png ${TEST_RESULTS_DIR}/"
}
}
}
}
post {
always {
// Публикация JUnit-результатов
junit "${TEST_RESULTS_DIR}/**/*.xml"
// Публикация отчетов тестирования
publishHTML([
allowMissing: true,
alwaysLinkToLastBuild: true,
keepAll: true,
reportDir: "${TEST_REPORT_DIR}/connected",
reportName: 'Espresso Test Report',
reportFiles: 'index.html'
])
// Остановка и удаление контейнера
script {
sh "docker stop ${REDROID_CONTAINER} || true"
sh "docker rm ${REDROID_CONTAINER} || true"
}
// Очистка ADB
sh "adb disconnect localhost:${ADB_PORT} || true"
// Архивация артефактов
archiveArtifacts artifacts: "${TEST_RESULTS_DIR}/**/*", allowEmptyArchive: true
}
}
}
Scrcpy
Scrcpy — это легковесная бесплатная утилита с открытым исходным кодом для отображения и управления экраном Android-устройств на компьютере. Работает по USB (ADB) или по Wi-Fi без каких-либо мобильных приложений на стороне устройства.
Примеры использования:
Подключение и запуск (устройство подключено по USB):
scrcpy
Подключение через Wi-Fi (зная IP-адрес устройства):
adb connect 192.168.1.5:5555
scrcpy -s 192.168.1.5:5555
Изменение разрешения видео:
scrcpy -m 1024
Запись видео с устройства:
scrcpy --record file.mp4
Управление устройством без отображения экрана (только ввод с клавиатуры и мыши):
scrcpy -N
Если вам нужен визуальный контроль за процессом выполнения тестов, можно интегрировать Scrcpy в пайплайн:
groovy
stage('Visual Debug with Scrcpy') {
when {
expression { return params.ENABLE_VISUAL_DEBUG }
}
steps {
script {
// Установка Scrcpy, если еще не установлена
sh '''
if ! command -v scrcpy &> /dev/null; then
apt-get update && apt-get install -y scrcpy
fi
'''
// Запуск Scrcpy для записи видео процесса тестирования
sh "mkdir -p ${TEST_RESULTS_DIR}/recordings"
for (int i = 0; i < DEVICE_COUNT; i++) {
def adbPort = BASE_ADB_PORT + i
sh """
nohup scrcpy --no-display --record=${TEST_RESULTS_DIR}/recordings/device-${i}-recording.mp4 \
--serial=localhost:${adbPort} &
"""
}
// Ограничение длительности записи
sh "sleep 60" // Запись первой минуты тестирования
sh "pkill scrcpy || true"
}
}
Kubernetes
Зачем использовать ReDroid в Kubernetes
ReDroid в Kubernetes упрощает тестирование Android-приложений. Вот главные плюсы:
Параллельные тесты — запускаешь сразу много виртуальных Android-устройств и экономишь время на тестировании.
Меньше расходов на ресурсы — ReDroid легкий, а Kubernetes грамотно распределяет нагрузку между серверами.
Автоматическое управление — система сама запускает, перезапускает и останавливает Android-контейнеры без твоего участия.
Простая связь с CI/CD — легко встраивается в существующие пайплайны Jenkins или GitLab CI.
Изоляция тестов — каждое тестовое окружение работает независимо, без помех другим тестам.
Централизованное управление — все контролируется через один интерфейс.
Экономия бюджета — виртуальные устройства вместо дорогих реальных телефонов.
Одинаковые условия — все тесты в одинаковой среде, что повышает надежность результатов.
Доступ к аппаратному ускорению — можно настроить использование GPU для лучшей производительности.
Это решение подходит командам, которым нужно регулярно тестировать Android-приложения в разных конфигурациях быстро и предсказуемо.
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: redroid-farm
namespace: android-testing
spec:
serviceName: "redroid"
replicas: 3 # Количество параллельных экземпляров устройств
selector:
matchLabels:
app: redroid-device
template:
metadata:
labels:
app: redroid-device
spec:
securityContext:
runAsUser: 0 # Запуск от имени root
containers:
- name: redroid
image: redroid/redroid:15.0.0_64only-latest
securityContext:
privileged: true # Требуется для redroid
ports:
- containerPort: 5555
name: adb
env:
- name: REDROID_PROP_ro.debuggable
value: "1"
- name: REDROID_PROP_service.adb.tcp.port
value: "5555"
- name: ANDROID_ARCH
value: "x86_64"
resources:
requests:
cpu: 2
memory: 3Gi
limits:
cpu: 4
memory: 6Gi
volumeMounts:
- name: dri
mountPath: /dev/dri
- name: kvm
mountPath: /dev/kvm
lifecycle:
preStop:
exec:
command: ["/system/bin/reboot", "-p"]
volumes:
- name: dri
hostPath:
path: /dev/dri
- name: kvm
hostPath:
path: /dev/kvm
nodeSelector:
hardware-acceleration: "true" # Выбор узлов с GPU/аппаратным ускорением
Wireshark и ReDroid: перехват и анализ сетевого трафика Android-приложений
Wireshark в сочетании с ReDroid предоставляет инструментарий для тестирования и отладки сетевого взаимодействия Android-приложений. Эта комбинация особенно полезна для анализа API-запросов, проверки безопасности соединений и отладки проблем с сетью.
Основные возможности:
Наблюдение за реальным трафиком — Wireshark позволяет видеть все сетевые пакеты, которыми обменивается Android-приложение в ReDroid с внешними сервисами.
Простота настройки — поскольку ReDroid работает как Docker-контейнер, его сетевой трафик легко перехватывается через Docker-сеть без дополнительных инструментов на самом устройстве.
Анализ защищенного трафика — при правильной настройке можно исследовать даже HTTPS-трафик (с использованием инструментов типа mitmproxy для ReDroid).
Поиск проблем производительности — легко выявлять медленные запросы и задержки в работе приложения по сетевым данным.
Автоматизированное тестирование API — можно записывать и затем воспроизводить сценарии сетевого взаимодействия для тестирования.
Настройка сети для тестирования:
Запустите ReDroid с особыми сетевыми параметрами:
docker run --name redroid-test --network host redroid/redroid:11.0.0-latest
Настройте перехват трафика в Wireshark, используя фильтр для выделения трафика конкретного контейнера ReDroid:
ip.addr == [IP-адрес контейнера]
Для HTTPS-трафика настройте прокси в ReDroid через ADB:
adb connect localhost:5555
adb shell settings put global http_proxy "localhost:8080"
Запустите mitmproxy для расшифровки HTTPS:
mitmproxy --listen-port 8080
Это решение помогает разработчикам и тестировщикам глубже понимать, как их Android-приложения взаимодействуют с сетью, выявлять проблемы безопасности и оптимизировать сетевые взаимодействия без необходимости использования физических устройств или сложных настроек эмуляторов.
Margutoop
Хорошая статья! Можно добавить только краткое упоминание про безопасность контейнеров и ограничения ReDroid для полноты картины
antonkryloff Автор
Я вам скажу что с Хабра практически не бывает звезд на GitHub. То есть выкладывая здесь статьи, я делюсь знаниями и опытом, накручиваю просмотры Хабру, чтобы ребята могли зарабатывать на рекламе, но в остальном - смысла в этом нет никакого.