DEV Community

Anton Krylov
Anton Krylov

Posted on

Redroid

What is ReDroid?

ReDroid is a lightweight alternative to the standard Android emulator that runs as a Docker container. ReDroid provides a full Android system in a container, significantly reducing startup time and resource consumption compared to traditional emulators.

ReDroid uses the host system's Linux kernel and is based on the anbox module project, enabling Android to run without CPU virtualization. This makes it an ideal solution for automated Android app testing in CI/CD environments where speed and efficiency are critical.

Comparison with Android Studio Emulator:

ReDroid is perfect for automated UI testing in CI/CD, especially when speed and resource efficiency are important. The standard Android Studio emulator is better suited for local development, debugging, and testing requiring full device emulation.

Manual Testing

Start the ReDroid container (Docker) and wait about a minute for it to load:

docker run -d --name redroid --privileged -p 5555:5555 redroid/redroid:15.0.0_64only-latest
sleep 30
Enter fullscreen mode Exit fullscreen mode

Connect to the running container via ADB:

adb connect localhost:5555
adb devices
Enter fullscreen mode Exit fullscreen mode

Install both APK files on ReDroid:

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
Enter fullscreen mode Exit fullscreen mode

Run your Espresso/UI tests with ADB:

adb -s localhost:5555 shell am instrument -w your.package.name.test/androidx.test.runner.AndroidJUnitRunner
Enter fullscreen mode Exit fullscreen mode

Export logs after testing:

adb -s localhost:5555 logcat -d > android-log.txt
Enter fullscreen mode Exit fullscreen mode

After completing the tests, remove the container:

docker stop redroid && docker rm redroid
Enter fullscreen mode Exit fullscreen mode

Preparing an Android Project for UI Testing

Step 1: Configuring Dependencies in app/build.gradle

gradle:

android {
    defaultConfig {
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }
}

dependencies {
    // Core components for UI testing
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
    androidTestImplementation 'androidx.test:runner:1.5.2'
    androidTestImplementation 'androidx.test:rules:1.5.0'

    // Additional Espresso libraries for advanced testing
    androidTestImplementation 'androidx.test.espresso:espresso-contrib:3.5.1'
    androidTestImplementation 'androidx.test.espresso:espresso-intents:3.5.1'

    // JUnit for test environment integration
    androidTestImplementation 'androidx.test.ext:junit:1.1.5'
}
Enter fullscreen mode Exit fullscreen mode

Step 2: Creating an Example UI Test with Espresso

Create the file 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() {
        // Check that the welcome text field is displayed
        onView(withId(R.id.welcome_text))
            .check(matches(isDisplayed()))
            .check(matches(withText("Hello World!")));
    }

    @Test
    public void buttonClickChangesText() {
        // Click the button
        onView(withId(R.id.action_button))
            .perform(click());

        // Check that the text has changed
        onView(withId(R.id.welcome_text))
            .check(matches(withText("Button Clicked!")));
    }
}
Enter fullscreen mode Exit fullscreen mode

Configuring Jenkins for UI Test Automation with ReDroid

Step 1: Installing Required Jenkins Plugins

In the Jenkins interface, go to "Configure Jenkins" > "Manage Plugins" and install:

Docker Pipeline
Gradle Plugin
HTML Publisher Plugin
JUnit Plugin
Allure Jenkins Plugin (optional for enhanced reporting)
Step 2: Creating a Jenkinsfile for the Pipeline

Create a Jenkinsfile in the project root:

groovy:

pipeline {
    agent any

    environment {
        // Environment variables
        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 {
                    // Stop and remove the container if it exists
                    sh "docker stop ${REDROID_CONTAINER} || true"
                    sh "docker rm ${REDROID_CONTAINER} || true"

                    // Run ReDroid in 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}
                    """

                    // Wait for ReDroid to start
                    sh "sleep 30"
                }
            }
        }

        stage('Connect to ReDroid and Install APKs') {
            steps {
                script {
                    // Connect to the device via ADB
                    sh "adb connect localhost:${ADB_PORT}"
                    sh "adb devices"

                    // Wait for the system to fully load
                    sh "adb wait-for-device"

                    // Install the app APK and tests
                    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 {
                    // Create directories for results
                    sh "mkdir -p ${TEST_RESULTS_DIR}"

                    // Get the app package name
                    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()

                    // Run UI tests
                    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
                    """

                    // Copy results from the device
                    sh "adb -s localhost:${ADB_PORT} pull /sdcard/test-results ${TEST_RESULTS_DIR}"

                    // Collect logs
                    sh "adb -s localhost:${ADB_PORT} logcat -d > ${TEST_RESULTS_DIR}/device-log.txt"

                    // Take a screenshot for diagnostics (optional)
                    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 {
            // Publish JUnit results
            junit "${TEST_RESULTS_DIR}/**/*.xml"

            // Publish test reports
            publishHTML([
                allowMissing: true,
                alwaysLinkToLastBuild: true,
                keepAll: true,
                reportDir: "${TEST_REPORT_DIR}/connected",
                reportName: 'Espresso Test Report',
                reportFiles: 'index.html'
            ])

            // Stop and remove the container
            script {
                sh "docker stop ${REDROID_CONTAINER} || true"
                sh "docker rm ${REDROID_CONTAINER} || true"
            }

            // Clean up ADB
            sh "adb disconnect localhost:${ADB_PORT} || true"

            // Archive artifacts
            archiveArtifacts artifacts: "${TEST_RESULTS_DIR}/**/*", allowEmptyArchive: true
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Scrcpy

Scrcpy is a lightweight, free, open-source utility for displaying and controlling Android device screens on a computer. It works over USB (ADB) or Wi-Fi without requiring any mobile apps on the device.

Usage Examples:

Connect and start (device connected via USB):

scrcpy
Enter fullscreen mode Exit fullscreen mode

Connect via Wi-Fi (knowing the device's IP address):

adb connect 192.168.1.5:5555
scrcpy -s 192.168.1.5:5555
Enter fullscreen mode Exit fullscreen mode

Change video resolution:

scrcpy -m 1024
Enter fullscreen mode Exit fullscreen mode

Record device video:

scrcpy --record file.mp4
Enter fullscreen mode Exit fullscreen mode

Control the device without displaying the screen (keyboard and mouse input only):

scrcpy -N
Enter fullscreen mode Exit fullscreen mode

If you need visual monitoring of test execution, you can integrate Scrcpy into the pipeline:

stage('Visual Debug with Scrcpy') {
    when {
        expression { return params.ENABLE_VISUAL_DEBUG }
    }
    steps {
        script {
            // Install Scrcpy if not already installed
            sh '''
                if ! command -v scrcpy &> /dev/null; then
                    apt-get update && apt-get install -y scrcpy
                fi
            '''

            // Start Scrcpy to record test execution
            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} &
                """
            }

            // Limit recording duration
            sh "sleep 60"  // Record the first minute of testing
            sh "pkill scrcpy || true"
        }
    }
Enter fullscreen mode Exit fullscreen mode

Kubernetes

Why Use ReDroid in Kubernetes

ReDroid in Kubernetes simplifies Android app testing. Here are the key benefits:

Parallel Tests — Run multiple virtual Android devices simultaneously, saving testing time.

Lower Resource Costs — ReDroid is lightweight, and Kubernetes efficiently distributes the load across servers.

Automatic Management — The system automatically starts, restarts, and stops Android containers without manual intervention.

Easy CI/CD Integration — Seamlessly integrates with existing Jenkins or GitLab CI pipelines.

Test Isolation — Each test environment operates independently without interfering with others.

Centralized Management — Everything is controlled through a single interface.

Budget Savings — Virtual devices replace expensive physical phones.

Consistent Conditions — All tests run in the same environment, improving result reliability.

Hardware Acceleration Access — Can be configured to use GPU for better performance.

This solution is ideal for teams needing to regularly test Android apps in different configurations quickly and predictably.

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: redroid-farm
  namespace: android-testing
spec:
  serviceName: "redroid"
  replicas: 3  # Number of parallel device instances
  selector:
    matchLabels:
      app: redroid-device
  template:
    metadata:
      labels:
        app: redroid-device
    spec:
      securityContext:
        runAsUser: 0  # Run as root
      containers:
      - name: redroid
        image: redroid/redroid:15.0.0_64only-latest
        securityContext:
          privileged: true  # Required for 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"  # Select nodes with GPU/hardware acceleration
Enter fullscreen mode Exit fullscreen mode

Wireshark and ReDroid: Capturing and Analyzing Android App Network Traffic

Wireshark combined with ReDroid provides powerful tools for testing and debugging Android app network interactions. This combination is particularly useful for analyzing API requests, checking connection security, and debugging network issues.

Key Features:

  • Real Traffic Monitoring — Wireshark lets you see all network packets exchanged between the Android app in ReDroid and external services.

  • Easy Setup — Since ReDroid runs as a Docker container, its network traffic is easily captured through Docker networking without additional tools on the device itself.

  • Secure Traffic Analysis — With proper setup, you can even inspect HTTPS traffic (using tools like mitmproxy with ReDroid).

  • Performance Issue Detection — Easily identify slow requests and app performance delays from network data.

  • Automated API Testing — Record and replay network interaction scenarios for testing.

Network Setup for Testing:

Run ReDroid with special network parameters:

docker run --name redroid-test --network host redroid/redroid:11.0.0-latest
Enter fullscreen mode Exit fullscreen mode

Configure traffic capture in Wireshark, filtering for the specific ReDroid container traffic:

ip.addr == [container IP address]
Enter fullscreen mode Exit fullscreen mode

For HTTPS traffic, configure a proxy in ReDroid via ADB:

adb connect localhost:5555
adb shell settings put global http_proxy "localhost:8080"
Enter fullscreen mode Exit fullscreen mode

Start mitmproxy for HTTPS decryption:

mitmproxy --listen-port 8080
Enter fullscreen mode Exit fullscreen mode

This solution helps developers and testers better understand how their Android apps interact with the network, identify security issues, and optimize network interactions without needing physical devices or complex emulator setups.

Top comments (0)

Some comments may only be visible to logged-in visitors. Sign in to view all comments.