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
Connect to the running container via ADB:
adb connect localhost:5555
adb devices
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
Run your Espresso/UI tests with ADB:
adb -s localhost:5555 shell am instrument -w your.package.name.test/androidx.test.runner.AndroidJUnitRunner
Export logs after testing:
adb -s localhost:5555 logcat -d > android-log.txt
After completing the tests, remove the container:
docker stop redroid && docker rm redroid
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'
}
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!")));
}
}
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
}
}
}
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
Connect via Wi-Fi (knowing the device's IP address):
adb connect 192.168.1.5:5555
scrcpy -s 192.168.1.5:5555
Change video resolution:
scrcpy -m 1024
Record device video:
scrcpy --record file.mp4
Control the device without displaying the screen (keyboard and mouse input only):
scrcpy -N
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"
}
}
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
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
Configure traffic capture in Wireshark, filtering for the specific ReDroid container traffic:
ip.addr == [container IP address]
For HTTPS traffic, configure a proxy in ReDroid via ADB:
adb connect localhost:5555
adb shell settings put global http_proxy "localhost:8080"
Start mitmproxy for HTTPS decryption:
mitmproxy --listen-port 8080
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.