How to Capture and Gracefully Stop Android Logcat in UiAutomator
This guide explains how to use Java's Runtime API within a UiAutomator test to start, filter, and safely terminate an adb logcat process, including a complete Logcat thread implementation and tips for preventing oversized log files.
When using UiAutomator to automate Android testing, developers often need to collect device logs automatically. Directly invoking adb logcat via Runtime.exec works, but stopping the spawned process is tricky; common suggestions like killing the PID are cumbersome, and calling Thread.destroy() throws NoSuchMethodError because the method destroys the Java thread, not the underlying OS process.
Solution Overview
The author introduces a custom Logcat class that extends Thread. It starts a logcat process, optionally filters output, writes matching lines to a file, and provides a flag‑based mechanism to stop the process cleanly by calling Process.destroy().
Key Implementation Details
package monkeytest;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.text.SimpleDateFormat;
import java.util.Date;
import source.Common;
public class Logcat extends Thread {
private static boolean LogKey = false;
@Override
public void run() {
execCmdAdb("adb logcat -c");
execCmdAdb("adb logcat", "logcat" + getNow() + ".log", "happyjuzi", true);
}
/** Execute adb command with optional filtering */
private void execCmdAdb(String cmd, String fileName, String filter, boolean key) {
System.out.println(cmd);
String OSname = System.getProperty("os.name");
try {
Process p = null;
if (OSname.contains("Mac")) {
p = Runtime.getRuntime().exec(Common.ADB_PATH + cmd);
} else {
p = Runtime.getRuntime().exec("cmd /c " + cmd);
}
// Standard output handling
InputStream input = p.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(input));
String line;
while ((line = reader.readLine()) != null) {
if (LogKey) {
p.destroy();
reader.close();
input.close();
return;
}
if (key) {
if (line.contains(filter)) {
Common.getInstance().saveToFile(line, fileName, false);
}
} else {
if (!line.contains(filter)) {
Common.getInstance().saveToFile(line, fileName, false);
}
}
}
reader.close();
input.close();
// Error output handling
InputStream errorInput = p.getErrorStream();
BufferedReader errorReader = new BufferedReader(new InputStreamReader(errorInput));
String eline;
while ((eline = errorReader.readLine()) != null) {
if (LogKey) {
p.destroy();
errorReader.close();
errorInput.close();
return;
}
if (key) {
if (eline.contains(filter)) {
Common.getInstance().saveToFile(line, fileName, false);
}
} else {
if (!eline.contains(filter)) {
Common.getInstance().saveToFile(line, fileName, false);
}
}
}
errorReader.close();
errorInput.close();
} catch (IOException e) {
Common.getInstance().output("执行" + cmd + "失败!");
e.printStackTrace();
}
}
/** Overload for commands without filtering */
private void execCmdAdb(String cmd) {
System.out.println(cmd);
String OSname = System.getProperty("os.name");
try {
if (OSname.contains("Mac")) {
Runtime.getRuntime().exec(Common.ADB_PATH + cmd);
} else {
Runtime.getRuntime().exec("cmd /c " + cmd);
}
} catch (IOException e) {
Common.getInstance().output("执行" + cmd + "失败!");
e.printStackTrace();
}
}
/** Stop the logcat thread */
public void stopLoacat() {
Logcat.LogKey = true;
}
/** Get current timestamp for file naming */
private String getNow() {
Date time = new Date();
SimpleDateFormat now = new SimpleDateFormat("MMdd-HHmm");
return now.format(time);
}
}Usage Tips
Call new Logcat().start() from your test script to begin logging. When you need to stop, invoke stopLoacat(), which sets the static flag LogKey to true; the next read loop iteration will destroy the process and close streams.
Be careful not to let the thread run indefinitely, as log files can grow large and consume resources. You can embed the Logcat thread inside a larger test loop or manage its lifecycle externally, as demonstrated by the author.
The provided implementation works on both macOS and Windows environments by checking System.getProperty("os.name") and adjusting the command invocation accordingly.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
