Что такое 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
Redroid
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 — можно записывать и затем воспроизводить сценарии сетевого взаимодействия для тестирования.

Настройка сети для тестирования:

  1. Запустите ReDroid с особыми сетевыми параметрами:

docker run --name redroid-test --network host redroid/redroid:11.0.0-latest
  1. Настройте перехват трафика в Wireshark, используя фильтр для выделения трафика конкретного контейнера ReDroid:

ip.addr == [IP-адрес контейнера]
  1. Для HTTPS-трафика настройте прокси в ReDroid через ADB:

adb connect localhost:5555
adb shell settings put global http_proxy "localhost:8080"
  1. Запустите mitmproxy для расшифровки HTTPS:

mitmproxy --listen-port 8080

Это решение помогает разработчикам и тестировщикам глубже понимать, как их Android-приложения взаимодействуют с сетью, выявлять проблемы безопасности и оптимизировать сетевые взаимодействия без необходимости использования физических устройств или сложных настроек эмуляторов.


https://t.me/akrylof

https://github.com/avkcode/resume/blob/main/resume.pdf

Комментарии (2)


  1. Margutoop
    05.06.2025 22:41

    Хорошая статья! Можно добавить только краткое упоминание про безопасность контейнеров и ограничения ReDroid для полноты картины


    1. antonkryloff Автор
      05.06.2025 22:41

      Я вам скажу что с Хабра практически не бывает звезд на GitHub. То есть выкладывая здесь статьи, я делюсь знаниями и опытом, накручиваю просмотры Хабру, чтобы ребята могли зарабатывать на рекламе, но в остальном - смысла в этом нет никакого.