安卓开发工具Android Studio中的日志窗口无内容显示

364

我昨天安装了Android Studio,然后尝试使用LogCat查看日志。但是LogCat中没有任何内容。我使用终端运行./adb logcat命令可以正常工作。

请问有人能够解释如何在Android Studio中使用LogCat吗?


6
只需重新启动Android Studio。 - Amit Vaghela
“./adb” 是什么意思?我不知道那个目录在哪里。 - Nerdy Bunz
打开 AVD 管理器并冷启动模拟器,这对我起到了作用!更改模拟器名称也可能起到作用! - beginner
现在是2022年2月,我正在运行Android Studio Bumblebee | 2021.1.1版本。我所做的只是重新启动了Android Studio。@AmitVaghela的答案帮助了我。 - Donzaala
我尝试了这些答案中几乎所有的建议,但是没有一个对我起作用。最后,我不得不求助于使用Android Studio的另一个logcat查看器插件:https://josesamuel.com/logviewer [它自己也有一些bug,例如,进程过滤对我来说失败了,但至少日志和文本过滤功能正常工作!] - Venryx
69个回答

12

确保您在模拟器开发者菜单选项中设置了适当的日志缓冲区大小。

在此输入图片描述


2
此外,如果有“选择日志级别”选项,则应启用它。不确定,但猜测它是在Android 10上添加的。 - Zakir Shikhli
1
谢谢,医生!这正是我的情况所在! - userVadim

9

对我来说,问题是我有两个同名的模拟器(我创建了它,然后删除了它,再次使用相同的名称创建它)。日志下拉菜单中有两个模拟器条目,而它连接到错误的模拟器。我所要做的就是切换到另一个模拟器。通过重命名模拟器,我永久地解决了这个问题。

enter image description here


8
尝试关闭项目并重新打开。这对我有用。日志将重新出现。

这对我有效...谷歌(或IntelliJ)应该检查为什么会发生这种情况:S,因为这只有在AS3上发生过,AS2上没有。 - Asfo

8

如果您仍然遇到logcat为空的问题,请阅读以下内容。

  • 如果您还被困在空白的logcat中,请阅读此处

我经历了数月的烦恼和麻烦,终于解决了这个问题。
没有任何帮助,设备监视器在调试期间工作正常,但标准的logcat视图总是为空的。

原因很简单又让人烦恼:
logcat视图存在,但宽度被更新后变为0!

您在“ALT 6”选项卡中,看到两个选项卡:“ADB logs”和“Devices | logcat”
“Devices | logcat”实际上意味着它由设备和logcat组成,并通过竖直边框进行分割。
竖直边框可以移动,并且在更新期间似乎已经向右移动了100%。

这导致logcat被收集但未显示,将鼠标指针移到工具窗口的右侧,然后将logcat拖回视图中。

这个解决方案不会帮助所有人,但我发现有很多人与工作正常的ADB连接,但仍然没有logcat输出,这些人可能会遇到相同的问题。


7

在此输入图片描述

在我的情况中,我从右侧的下拉菜单中删除了“image”。之后它就正常显示了。那是因为它将在搜索框中搜索关键字的日志,所以如果没有找到任何匹配项,它就会返回空白。


7
即使在最新版本的Android Studio上仍然遇到这个问题,感觉很奇怪。我阅读了一长串解决方案,但它们对我没有用。早期版本的Android Studio(我猜是v2.3)的被接受的答案对我有用。
我按照以下步骤重新启用了Logcat:
1. Logcat > 仅显示选定的应用程序 > 没有过滤器

enter image description here

  1. Logcat > 无筛选条件 > 仅显示所选应用程序

enter image description here

我本以为重置logcat应该会产生相同的效果,但实际上并没有。手动切换过滤器是唯一有效的方法。

这个问题出现在Android Studio 3.0.1(稳定版)上(在完成当前项目之前我不能更新它)。当我早上启动Android Studio继续昨晚离开的工作时,出现了这个问题。我希望开发人员能够解决这个问题。从stackoverflow尝试了超过15种解决方案仍然没有结果,真是让人痛苦。对于未来遇到这个问题的人来说,找到另一个解决方案甚至更加恼人。


7
当其他方法都无效时,这是我所做的。由于 adb logcat 运行良好,我决定依赖它。在 Android Studio 的内置终端中运行 adb logcat -v color 可以产生类似于正常 logcat 的输出,并且允许代码链接正常工作:

Running adb logcat -v color in the embedded console

但是这带来了一些问题:

  • 你无法指定要监视的包。使用--pid=<your PID>选项,可以监视单个进程的输出。但由于每次重新启动应用程序时PID都会更改,因此您必须在每次重新启动时重新运行此命令。
  • 颜色很烦人(我个人认为)。
  • 输出字段与先前消息不对齐,整个格式不正确,这使得跟随logcat比应该更难(虽然内置的logcat也是如此)。

所以我决定制作自己的工具来自动监视我的包PID并美化logcat输出:

import java.awt.AWTException;
import java.io.*;
import java.util.ArrayList;
import java.awt.Robot;
import java.awt.event.KeyEvent;

public class Logcat {

    private static final String ADB_FILE_PATH = "adb";

    // Customizations,
    private static final Color     V_COLOR = Color.RESET;
    private static final Color     D_COLOR = Color.RESET;
    private static final Color     I_COLOR = Color.RESET;
    private static final Color     W_COLOR = Color.BLUE;
    private static final Color     E_COLOR = Color.RED_BRIGHT;
    private static final Color  HINT_COLOR = Color.MAGENTA_BOLD_BRIGHT;
    private static final Color OTHER_COLOR = Color.GREEN_BOLD_BRIGHT;

    private static final int       DATE_LENGTH =   5;
    private static final int       TIME_LENGTH =  12;
    private static final int PROCESS_ID_LENGTH =   5;
    private static final int  THREAD_ID_LENGTH =   5;
    private static final int  LOG_LEVEL_LENGTH =   1;
    private static final int        TAG_LENGTH =  20;
    private static final int    MESSAGE_LENGTH = 110;

    private static final String SEPARATOR = " | ";
    private static final String CONTINUATION = "→";
    private static final String INDENTATION = "  ";

    private static final int PROCESS_IDS_UPDATE_INTERVAL_MILLIS = 1224;

    private static final int HISTORY_LENGTH = 1000;

    // State,
    private static boolean skipProcessIDCheck;
    private static ArrayList<String> processIDs = new ArrayList<String>();

    private static String logLevelToShow="V";  // All.

    private static Process logcatProcess;
    private static boolean appClosed;
    private static boolean stopEverything;

    private static String[] history = new String[HISTORY_LENGTH];
    private static int currentLocationInHistory, historyLength;

    public static void main(final String args[]) {

        clearAndroidStudioConsole();
        System.out.println("besm Allah");

        // Get processes ids of the provided package,
        if (args.length==0) {
            skipProcessIDCheck = true;
        } else {
            skipProcessIDCheck = false;
            getProcessIDs    (args[0]);    // Do it once before we start.
            monitorProcessIDs(args[0]);    // Do it periodically from now on.
        }

        // Start capturing and prettifying logcat,
        if (!monitorLogcat()) {
            stopEverything = true;
            return;
        }

        // Handle user input,
        handleUserInput();
    }

    private static void watch(final Process process, final ProcessListener listener) {

        // Read process standard output and send it to the listener line by line,
        new Thread() {
            public void run() {
                BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(process.getInputStream()));
                String line = "";
                try {
                    do {
                        if (bufferedReader.ready()) {
                            line = bufferedReader.readLine();
                            if (line!=null && !line.isEmpty()) listener.onNewLine(line);
                        } else {
                            Thread.sleep(100);
                        }
                    } while (line!=null && !stopEverything);
                } catch (Exception e) { e.printStackTrace(); }
            }
        }.start();
    }

    private static void monitorProcessIDs(String packageName) {

        // Continuously monitor the process IDs of this package and update when changed,
        new Thread() {
            public void run() {
                do {
                    try { Thread.sleep(PROCESS_IDS_UPDATE_INTERVAL_MILLIS); } catch (InterruptedException e) {}
                    getProcessIDs(packageName);
                } while (!stopEverything);
            }
        }.start();
    }

    private static void getProcessIDs(String packageName) {

        // Get the process IDs associated with this package once,
        ArrayList<String> newProcessIDs = new ArrayList<String>();
        Runtime runtime = Runtime.getRuntime();
        try {
            Process getPIDProcess = runtime.exec(ADB_FILE_PATH + " shell ps");
            watch(getPIDProcess, (line) -> {
                if (line.contains(packageName)) {
                    newProcessIDs.add(removeRedundantSpaces(line).split(" ")[1]);
                }
            });
            getPIDProcess.waitFor();
            Thread.sleep(500);  // Make sure we've already handled all the input from the process.
        } catch (Exception e) { e.printStackTrace(); }

        // Return immediately if program is closed,
        if (stopEverything) return ;

        // Some action upon getting the pid(s),
        boolean shouldRepeatHistory = false;
        if (newProcessIDs.isEmpty()) {

            // Just closed,
            if (!appClosed) {
                appClosed = true;
                prettify("----- App closed -----");
            }
        } else if (appClosed) {

            // Just opened, clear,
            appClosed = false;
            clearAndroidStudioConsole();
            prettify("----- App opened -----");
            shouldRepeatHistory = true;
        } else {

            // Detect changes in processes,
            for (String pid : newProcessIDs) {
                if (!processIDs.contains(pid)) {
                    clearAndroidStudioConsole();
                    prettify("----- Process(es) changed (or app restarted - some logs could have been missed) -----");
                    shouldRepeatHistory = true;
                    break ;
                }
            }
        }

        // Set the new PID(s),
        processIDs = newProcessIDs;
        if (shouldRepeatHistory) repeatHistory();
    }

    private static boolean monitorLogcat() {

        Runtime runtime = Runtime.getRuntime();
        try {
            logcatProcess = runtime.exec(ADB_FILE_PATH + " logcat -v threadtime");
            watch(logcatProcess, (line) -> {

                // Learn history, in case we need to repeat it,
                if (appClosed || processLogcatLine(line)) {
                    history[currentLocationInHistory] = line;
                    currentLocationInHistory = (currentLocationInHistory + 1) % history.length;
                    if (historyLength<history.length) historyLength++;
                }
            });
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }

        return true;
    }

    private static boolean processLogcatLine(String line) {
        try {
            return prettify(line);
        } catch (Exception e) {
            print(line, OTHER_COLOR);
            System.out.println();

            // Debug,
            e.printStackTrace();
            return true;
        }
    }

    // Returns true if line should be kept in history,
    private static synchronized boolean prettify(String line) {

        if (line.startsWith("-")) {
            // It's a "beginning of <something>" line,
            print(line, HINT_COLOR);
            System.out.println();
            return true;
        }

        // Get the individual fields,
        String      date = line.substring(0, line.indexOf(' ')); line = line.substring(line.indexOf(' ')+1); line = line.trim();
        String      time = line.substring(0, line.indexOf(' ')); line = line.substring(line.indexOf(' ')+1); line = line.trim();
        String processID = line.substring(0, line.indexOf(' ')); line = line.substring(line.indexOf(' ')+1); line = line.trim();

        // Break early if possible,
        if (!skipProcessIDCheck && !processIDs.contains(processID.trim())) return false;

        // Continue parsing,
        String  threadID = line.substring(0, line.indexOf(' ')); line = line.substring(line.indexOf(' ')+1); line = line.trim();
        String  logLevel = line.substring(0, line.indexOf(' ')); line = line.substring(line.indexOf(' ')+1); line = line.trim();

        // Break early if possible,
        switch (logLevel) {
            case "V": if (!"V"    .contains(logLevelToShow)) return true; break;
            case "D": if (!"VD"   .contains(logLevelToShow)) return true; break;
            case "I": if (!"VDI"  .contains(logLevelToShow)) return true; break;
            case "W": if (!"VDIW" .contains(logLevelToShow)) return true; break;
            case "E": if (!"VDIWE".contains(logLevelToShow)) return true; break;
        }

        // Continue parsing,
        String       tag = line.substring(0, line.indexOf(':')); line = line.substring(line.indexOf(':')+1); line = line.trim();

        // Because some tags have a trailing ":",
        if (line.startsWith(":")) {
            tag += ":";
            line = line.substring(1);
        }

        // Indent lines starting by "at",
        String indentation = "";
        if (line.startsWith("at ")) {
            indentation = "   " + INDENTATION;
            line = " " + INDENTATION + line;
        }

        // Print the prettified log,
        Color color;
        switch (logLevel) {
            case "V": color = V_COLOR; break;
            case "D": color = D_COLOR; break;
            case "I": color = I_COLOR; break;
            case "W": color = W_COLOR; break;
            case "E": color = E_COLOR; break;
            default:
                color = Color.RESET;
        }

        String fields = adjustLength(     date,       DATE_LENGTH) + SEPARATOR +
                        adjustLength(     time,       TIME_LENGTH) + SEPARATOR +
                        adjustLength(processID, PROCESS_ID_LENGTH) + SEPARATOR +
                        adjustLength( threadID,  THREAD_ID_LENGTH) + SEPARATOR +
                        adjustLength( logLevel,  LOG_LEVEL_LENGTH) + SEPARATOR +
                        adjustLength(      tag,        TAG_LENGTH) + SEPARATOR;

        // Split the message onto multiple lines if needed,
        String message = chunkPreservingParentheses(line, MESSAGE_LENGTH, 2);
        print(fields + message, color);
        System.out.println();

        while (line.length() > message.length()) {

            // Debug,
            //print(line, OTHER_COLOR);
            //System.out.println("Line: " + line.length() + "length: " + message.length() + ", cont: " + CONTINUATION.length() + "dent: " + indentation.length());
            //System.out.println();

            // Remove the already printed part.
            line = line.substring(message.length()-CONTINUATION.length());

            // Add a dot to make links work,
            boolean shouldAddDot=false;
            if (line.matches("^[^\\.]*\\(.*:[123456789][1234567890]*\\).*")) shouldAddDot = true;

            // Indent,
            line = (shouldAddDot ? "." : (indentation.isEmpty() ? "" : " ")) + indentation + line;

            // Take another chunk,
            message = chunkPreservingParentheses(line, MESSAGE_LENGTH, 2+indentation.length());

            // Front pad to align this part with the message body,
            String paddedMessage = message;
            for (int i=0; i<fields.length(); i++) paddedMessage = ' ' + paddedMessage;

            // Print,
            print(paddedMessage, color);
            System.out.println();
        }

        return true;  // Keep in local buffer.
    }

    private static String adjustLength(String text, int length) {
        while (text.length() < length) text += ' ';
        if (text.length() > length) {
            text = text.substring(0, length-CONTINUATION.length());
            text += CONTINUATION;
        }
        return text;
    }

    private static String chunkPreservingParentheses(String text, int length, int minChunckLength) {

        if (text.length() <= length) return text;

        // Take a chunk out of the text,
        String chunk = text.substring(0, length-CONTINUATION.length()) + CONTINUATION;

        // Check if a paranthesis was opened and not closed,
        int lastOpenParanthesisIndex = chunk.lastIndexOf('(');
        int lastCloseParanthesisIndex = chunk.lastIndexOf(')');
        if (lastCloseParanthesisIndex <= lastOpenParanthesisIndex) {  // Also works when either is not found.
            if (minChunckLength<1) minChunckLength = 1;
            if (lastOpenParanthesisIndex > minChunckLength+CONTINUATION.length()) { // Avoid endless loops.
                int includeParenthesisSize = (CONTINUATION.length()>0) ? 1 : 0;
                chunk = text.substring(0, lastOpenParanthesisIndex+includeParenthesisSize-CONTINUATION.length()) + CONTINUATION;
            }
        }

        return chunk;
    }

    private static void repeatHistory() {
        int index = currentLocationInHistory-historyLength;
        if (index < 0) index += history.length;
        for (int i=0; i<historyLength; i++) {
            processLogcatLine(history[index]);
            index = (index + 1) % history.length;
        }
    }

    private static void print(String text, Color color) {
        System.out.print(color);
        System.out.print(text);
        System.out.print(Color.RESET);
    }

    private static String removeRedundantSpaces(String text) {
        String newText = text.replace("  ", " ");
        while (!text.equals(newText)) {
            text = newText;
            newText = text.replace("  ", " ");
        }
        return text;
    }

    private static void clearAndroidStudioConsole() {

        // Couldn't find a reliable way to clear Intellij terminal scrollback, so we just print
        // a LOT of newlines,
        StringBuilder bunchOfNewLines = new StringBuilder();
        for (int i=0; i<124; i++) bunchOfNewLines.append(System.lineSeparator());
        System.out.print(bunchOfNewLines);

        // Scroll the current line to the top of the window,
        try {
            // If we are on Windows,
            new ProcessBuilder("cmd", "/c", "cls").inheritIO().start().waitFor();
        } catch (Exception e) {

            // We are not on Windows,
            bunchOfNewLines = new StringBuilder();
            for (int i=0; i<124; i++) bunchOfNewLines.append("\b\r");
            System.out.print(bunchOfNewLines);
        }
    }

    private static void handleUserInput() {

        // Line read. Unfortunately, java doesn't provide character by character reading out of the box.
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
        String input = "";
        do {
            try {
                if (bufferedReader.ready()) {
                    input = input = bufferedReader.readLine().toUpperCase();

                    // Set log level,
                    if (input.equals("V")||input.equals("D")||input.equals("I")||input.equals("W")||input.equals("E")) {

                        if (!logLevelToShow.equals(input)) {
                            logLevelToShow = input;
                            clearAndroidStudioConsole();
                            repeatHistory();
                        }

                        prettify("----- Log level set to " + logLevelToShow + " -----");

                    } else if (input.equals("C")) {

                        // Clear screen and history,
                        clearAndroidStudioConsole();
                        historyLength = 0;
                    }
                } else {
                    Thread.sleep(100);
                }
            } catch (Exception e) { e.printStackTrace(); }

            // Check if the logcat process is still alive,
            if (!logcatProcess.isAlive()) {
                prettify("----- adb logcat process terminated -----");
                stopEverything = true;
            }

        } while (!stopEverything && !input.equals("Q"));

        // Allow all monitoring threads to exit,
        stopEverything = true;
    }

    interface ProcessListener {
        void onNewLine(String line);
    }

    enum Color {

        // Thanks to this answer: https://dev59.com/hW025IYBdhLWcg3w25uy#51944613

        //Color end string, color reset
        RESET("\033[0m"),

        // Regular Colors. Normal color, no bold, background color etc.
        BLACK  ("\033[0;30m"),
        RED    ("\033[0;31m"),
        GREEN  ("\033[0;32m"),
        YELLOW ("\033[0;33m"),
        BLUE   ("\033[0;34m"),
        MAGENTA("\033[0;35m"),
        CYAN   ("\033[0;36m"),
        WHITE  ("\033[0;37m"),

        // Bold
        BLACK_BOLD  ("\033[1;30m"),
        RED_BOLD    ("\033[1;31m"),
        GREEN_BOLD  ("\033[1;32m"),
        YELLOW_BOLD ("\033[1;33m"),
        BLUE_BOLD   ("\033[1;34m"),
        MAGENTA_BOLD("\033[1;35m"),
        CYAN_BOLD   ("\033[1;36m"),
        WHITE_BOLD  ("\033[1;37m"),

        // Underline
        BLACK_UNDERLINED  ("\033[4;30m"),
        RED_UNDERLINED    ("\033[4;31m"),
        GREEN_UNDERLINED  ("\033[4;32m"),
        YELLOW_UNDERLINED ("\033[4;33m"),
        BLUE_UNDERLINED   ("\033[4;34m"),
        MAGENTA_UNDERLINED("\033[4;35m"),
        CYAN_UNDERLINED   ("\033[4;36m"),
        WHITE_UNDERLINED  ("\033[4;37m"),

        // Background
        BLACK_BACKGROUND  ("\033[40m"),
        RED_BACKGROUND    ("\033[41m"),
        GREEN_BACKGROUND  ("\033[42m"),
        YELLOW_BACKGROUND ("\033[43m"),
        BLUE_BACKGROUND   ("\033[44m"),
        MAGENTA_BACKGROUND("\033[45m"),
        CYAN_BACKGROUND   ("\033[46m"),
        WHITE_BACKGROUND  ("\033[47m"),

        // High Intensity
        BLACK_BRIGHT  ("\033[0;90m"),
        RED_BRIGHT    ("\033[0;91m"),
        GREEN_BRIGHT  ("\033[0;92m"),
        YELLOW_BRIGHT ("\033[0;93m"),
        BLUE_BRIGHT   ("\033[0;94m"),
        MAGENTA_BRIGHT("\033[0;95m"),
        CYAN_BRIGHT   ("\033[0;96m"),
        WHITE_BRIGHT  ("\033[0;97m"),

        // Bold High Intensity
        BLACK_BOLD_BRIGHT  ("\033[1;90m"),
        RED_BOLD_BRIGHT    ("\033[1;91m"),
        GREEN_BOLD_BRIGHT  ("\033[1;92m"),
        YELLOW_BOLD_BRIGHT ("\033[1;93m"),
        BLUE_BOLD_BRIGHT   ("\033[1;94m"),
        MAGENTA_BOLD_BRIGHT("\033[1;95m"),
        CYAN_BOLD_BRIGHT   ("\033[1;96m"),
        WHITE_BOLD_BRIGHT  ("\033[1;97m"),

        // High Intensity backgrounds
        BLACK_BACKGROUND_BRIGHT  ("\033[0;100m"),
        RED_BACKGROUND_BRIGHT    ("\033[0;101m"),
        GREEN_BACKGROUND_BRIGHT  ("\033[0;102m"),
        YELLOW_BACKGROUND_BRIGHT ("\033[0;103m"),
        BLUE_BACKGROUND_BRIGHT   ("\033[0;104m"),
        MAGENTA_BACKGROUND_BRIGHT("\033[0;105m"),
        CYAN_BACKGROUND_BRIGHT   ("\033[0;106m"),
        WHITE_BACKGROUND_BRIGHT  ("\033[0;107m");

        private final String code;

        Color(String code) { this.code = code; }
        @Override public String toString() { return code; }
    }
}

只需将此代码转储到 Logcat.java 中并使用以下命令进行编译:

javac Logcat.java

在Android Studio的嵌入式终端中运行:

java Logcat <your.package.name>

例如:

java Logcat com.nomone.vr_desktop

结果看起来像这样:

My own Logcat prettifier

这个应用非常可定制化,我把大部分选项都放在了应用的第一部分,所以你可以轻松地调整颜色和格式。如果adb工具不在你的PATH环境变量中,只需在编译之前在代码中设置它的完整路径到ADB_FILE_PATH变量中。

当应用程序运行时,你可以输入以下快捷键:

  • c清除屏幕和本地缓存。
  • vidwe更改logcat级别。
  • q优雅退出。也可以使用Ctrl+c

不幸的是,在按下这些键后,你必须按enter。似乎Java不允许控制台输入单个字符而不编写特定于系统的代码。抱歉!

免责声明

  • 如果使用adb连接了多个设备,则此方法无效。
  • 我没有进行全面测试。只是在一些设备上使用了一段时间。
  • 我没有在Windows或Mac上测试过,但我尽量避免使用任何特定于系统的内容,所以应该仍然有效。

希望这可以解决你的问题 :)


那么多的工作,却如此少的赞誉。在这里,送你一个网络点赞。(当然,如果这个问题在Android Studio中得到解决会更好) - Micha

6

在我的情况下,在开发者选项菜单中有一个名为

撤销USB调试授权的选项。

一旦您撤销了所有现有的授权,它将再次要求信任您正在使用的计算机,之后它就开始再次显示日志了。


5

在Android 3.6.1中,我需要:

  • 升级到最新的Android Studio版本(4.x.x)
  • 重启Logcat
  • 重启应用程序
  • 重启Android Studio
  • 重启Android测试设备

嘿,自从我最近升级以来就遇到了这个问题。我尝试了上面给出的步骤,但显然对我没有用。你能帮我解决一下吗? - Chandu
1
重新启动安卓设备解决了我的问题(比如手机、平板电脑)。 - Eric

4

步骤1: 打开Android开发人员选项和USB调试,将您的手机连接上。

步骤2: 进入View > Tools Window > Logcat

步骤3: 在运行应用程序之前,请确保您的手机已连接到Android Studio。然后运行应用程序。

注意:如果无法显示Logcat,请重新启动Android Studio:File > Invalid Caches/ restart


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接