434

根据 Google 的说法,在将我的 Android 应用程序发布到 Google Play 之前,我必须“停用对源代码中 Log 方法的任何调用”。摘自出版清单第 3 节:

确保在构建应用程序以供发布之前停用日志记录并禁用调试选项。您可以通过删除源文件中对 Log 方法的调用来停用日志记录。

我的开源项目很大,每次发布都手动做很痛苦。此外,删除 Log 行可能很棘手,例如:

if(condition)
  Log.d(LOG_TAG, "Something");
data.load();
data.show();

如果我注释 Log 行,则该条件适用于下一行,并且可能不会调用 load()。这种情况是否足够罕见以至于我可以决定它不应该存在?

那么,有没有更好的源代码级别的方法来做到这一点?或者也许一些巧妙的 ProGuard 语法可以有效但安全地删除所有日志行?

4

30 回答 30

526

我发现一个更简单的解决方案是忘记所有的if检查,当我们调用 Ant目标时,只使用ProGuard 去除任何Log.d()或方法调用。Log.v()release

这样,我们总是可以为常规构建输出调试信息,而不必为发布构建进行任何代码更改。ProGuard 还可以对字节码进行多次传递以删除其他不需要的语句、空块,并且可以在适当的情况下自动内联短方法。

例如,这是一个非常基本的 Android ProGuard 配置:

-dontskipnonpubliclibraryclasses
-dontobfuscate
-forceprocessing
-optimizationpasses 5

-keep class * extends android.app.Activity
-assumenosideeffects class android.util.Log {
    public static *** d(...);
    public static *** v(...);
}

所以你可以把它保存到一个文件中,然后从 Ant 调用 ProGuard,传入你刚刚编译的 JAR 和你正在使用的 Android 平台 JAR。

另请参阅ProGuard 手册中的示例


更新(4.5 年后):现在我使用Timber进行 Android 日志记录。

它不仅比默认Log实现好一点——日志标签是自动设置的,而且很容易记录格式化的字符串和异常——而且您还可以在运行时指定不同的日志记录行为。

在此示例中,日志语句将仅在我的应用程序的调试版本中写入 logcat:

木材是在我的Application onCreate()方法中设置的:

if (BuildConfig.DEBUG) {
  Timber.plant(new Timber.DebugTree());
}

然后在我的代码中的其他任何地方我都可以轻松记录:

Timber.d("Downloading URL: %s", url);
try {
  // ...
} catch (IOException ioe) {
  Timber.e(ioe, "Bad things happened!");
}

有关更高级的示例,请参阅Timber 示例应用程序,其中所有日志语句都在开发期间发送到 logcat,在生产中,不记录任何调试语句,但错误会以静默方式报告给 Crashlytics。

于 2010-03-17T23:30:02.247 回答
130

所有的答案都很好,但是当我完成开发时,我既不想在所有 Log 调用周围使用 if 语句,也不想使用外部工具。

所以我使用的解决方案是用我自己的 Log 类替换 android.util.Log 类:

public class Log {
    static final boolean LOG = BuildConfig.DEBUG;

    public static void i(String tag, String string) {
        if (LOG) android.util.Log.i(tag, string);
    }
    public static void e(String tag, String string) {
        if (LOG) android.util.Log.e(tag, string);
    }
    public static void d(String tag, String string) {
        if (LOG) android.util.Log.d(tag, string);
    }
    public static void v(String tag, String string) {
        if (LOG) android.util.Log.v(tag, string);
    }
    public static void w(String tag, String string) {
        if (LOG) android.util.Log.w(tag, string);
    }
}

在所有源文件中我唯一要做的就是用我自己的类替换 android.util.Log 的导入。

于 2011-01-04T11:12:21.070 回答
63

我建议在某处使用静态布尔值来指示是否记录:

类我的调试 {
  静态最终布尔 LOG = true;
}

然后,无论您想在哪里登录代码,只需执行以下操作:

如果(MyDebug.LOG){
  if (条件) Log.i(...);
}

现在,当您将 MyDebug.LOG 设置为 false 时,编译器将删除此类检查中的所有代码(因为它是静态最终代码,它在编译时知道代码未使用。)

对于较大的项目,您可能希望开始在单个文件中使用布尔值,以便能够根据需要轻松启用或禁用日志记录。例如,这些是我们在窗口管理器中拥有的各种日志记录常量:

static final String TAG = "WindowManager";
static final boolean DEBUG = false;
static final boolean DEBUG_FOCUS = false;
static final boolean DEBUG_ANIM = false;
static final boolean DEBUG_LAYOUT = false;
static final boolean DEBUG_RESIZE = false;
static final boolean DEBUG_LAYERS = false;
static final boolean DEBUG_INPUT = false;
static final boolean DEBUG_INPUT_METHOD = false;
static final boolean DEBUG_VISIBILITY = false;
static final boolean DEBUG_WINDOW_MOVEMENT = false;
static final boolean DEBUG_ORIENTATION = false;
static final boolean DEBUG_APP_TRANSITIONS = false;
static final boolean DEBUG_STARTING_WINDOW = false;
static final boolean DEBUG_REORDER = false;
static final boolean DEBUG_WALLPAPER = false;
static final boolean SHOW_TRANSACTIONS = false;
static final boolean HIDE_STACK_CRAWLS = true;
static final boolean MEASURE_LATENCY = false;

使用相应的代码,例如:

    if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT) Log.v(
        TAG, "Adding window " + window + " at "
        + (i+1) + " of " + mWindows.size() + " (after " + pos + ")");
于 2010-03-15T15:35:24.110 回答
31

Christopher 的 Proguard 解决方案是最好的,但如果出于任何原因您不喜欢 Proguard,这里有一个技术含量非常低的解决方案:

评论日志:

find . -name "*\.java" | xargs grep -l 'Log\.' | xargs sed -i 's/Log\./;\/\/ Log\./g'

取消注释日志:

find . -name "*\.java" | xargs grep -l 'Log\.' | xargs sed -i 's/;\/\/ Log\./Log\./g'

一个约束是您的日志记录指令不能跨越多行。

(在项目根目录的 UNIX shell 中执行这些行。如果使用 Windows,请获取 UNIX 层或使用等效的 Windows 命令)

于 2010-03-15T13:34:24.110 回答
20

我想添加一些关于将 Proguard 与 Android Studio 和 gradle 一起使用的精确度,因为我在从最终二进制文件中删除日志行时遇到了很多问题。

为了assumenosideeffects在 Proguard 作品中制作,有一个先决条件。

在您的 gradle 文件中,您必须指定proguard-android-optimize.txt作为默认文件的用法。

buildTypes {
    release {
        minifyEnabled true
        proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'

        // With the file below, it does not work!
        //proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
    }
}

实际上,在默认proguard-android.txt文件中,使用两个标志禁用优化:

-dontoptimize
-dontpreverify

proguard-android-optimize.txt文件没有添加这些行,所以现在assumenosideeffects可以工作了。

然后,就个人而言,我使用SLF4J,尤其是当我开发一些分发给其他人的库时。优点是默认没有输出。如果集成商想要一些日志输出,他可以使用 Android 版 Logback 并激活日志,因此可以将日志重定向到文件或 LogCat。

如果我真的需要从最终库中删除日志,然后添加到我的 Proguard 文件中(proguard-android-optimize.txt当然是在启用该文件之后):

-assumenosideeffects class * implements org.slf4j.Logger {
    public *** trace(...);
    public *** debug(...);
    public *** info(...);
    public *** warn(...);
    public *** error(...);
}
于 2015-09-09T15:08:47.953 回答
11

我强烈建议使用 Jake Wharton 的 Timber

https://github.com/JakeWharton/timber

它通过启用/禁用以及自动添加标签类解决了您的问题

只是

public class MyApp extends Application {

  public void onCreate() {
    super.onCreate();
    //Timber
    if (BuildConfig.DEBUG) {
      Timber.plant(new DebugTree());
    }
    ...

日志将仅在您的调试版本中使用,然后使用

Timber.d("lol");

或者

Timber.i("lol says %s","lol");

打印

“你的班级/味精”没有指定标签

于 2014-10-20T09:13:12.203 回答
8

我在 Google IO 示例应用程序中使用了LogUtils类。我将其修改为使用特定于应用程序的 DEBUG 常量而不是 BuildConfig.DEBUG,因为BuildConfig.DEBUG 是不可靠的。然后在我的课程中,我有以下内容。

import static my.app.util.LogUtils.makeLogTag;
import static my.app.util.LogUtils.LOGV;

public class MyActivity extends FragmentActivity {
  private static final String TAG = makeLogTag(MyActivity.class);

  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    LOGV(TAG, "my message");
  }
}
于 2013-03-30T04:52:41.363 回答
7

我会考虑使用 roboguice 的日志记录工具而不是内置的 android.util.Log

他们的设施会自动禁用发布版本的调试和详细日志。此外,您还可以免费获得一些漂亮的功能(例如,可自定义的日志记录行为、每个日志的附加数据等等)

使用 proguard 可能会很麻烦,除非您有充分的理由,否则我不会经历配置并使其与您的应用程序一起工作的麻烦禁用日志不是一个好理由)

于 2011-07-27T23:46:32.240 回答
7

我发布了这个专门适用于 Android Studio 用户的解决方案。我最近还发现了 Timber,并通过执行以下操作将其成功导入到我的应用程序中:

将最新版本的库放入您的 build.gradle:

compile 'com.jakewharton.timber:timber:4.1.1'

然后在 Android Studios 中,转到 Edit -> Find -> Replace in Path...

输入Log.e(TAG,或您已在"Text to find"文本框中定义日志消息。然后你只需将其替换为Timber.e(

在此处输入图像描述

单击查找,然后全部替换。

Android Studios 现在将检查您项目中的所有文件,并将所有日志替换为 Timbers。

我使用这种方法的唯一问题是 gradle 之后确实会出现一百万条错误消息,因为它无法在每个 java 文件的导入中找到“Timber”。只需单击错误,Android Studios 就会自动将“Timber”导入到您的 java.util 文件中。完成所有错误文件后,gradle 将再次编译。

您还需要将这段代码放入您的类onCreate方法中Application

    if (BuildConfig.DEBUG) {
        Timber.plant(new Timber.DebugTree());
    }

这将导致应用程序仅在您处于开发模式而非生产模式时进行日志记录。您还可以BuildConfig.RELEASE在发布模式下登录。

于 2016-03-21T17:29:43.633 回答
6

Per android.util.Log 提供了一种启用/禁用日志的方法:

public static native boolean isLoggable(String tag, int level);

默认情况下,方法 isLoggable(...) 仅在您在设备中设置属性后返回 false,如下所示:

adb shell setprop log.tag.MyAppTag DEBUG

这意味着可以打印出任何高于 DEBUG 级别的日志。参考安卓文档:

检查指定标签的日志是否可在指定级别记录。任何标签的默认级别都设置为 INFO。这意味着将记录任何高于并包括 INFO 的级别。在调用记录方法之前,您应该检查是否应该记录您的标签。您可以通过设置系统属性来更改默认级别:'setprop log.tag。' 其中 level 是 VERBOSE、DEBUG、INFO、WARN、ERROR、ASSERT 或 SUPPRESS。SUPPRESS 将关闭您的标签的所有日志记录。您还可以创建一个包含以下内容的 local.prop 文件:'log.tag.=' 并将其放在 /data/local.prop 中。

所以我们可以使用自定义日志工具:

public final class Dlog 
{
    public static void v(String tag, String msg)
    {
        if (Log.isLoggable(tag, Log.VERBOSE))
            Log.v(tag, msg);
    }

    public static void d(String tag, String msg)
    {
        if (Log.isLoggable(tag, Log.DEBUG))
            Log.d(tag, msg);
    }

    public static void i(String tag, String msg)
    {
        if (Log.isLoggable(tag, Log.INFO))
            Log.i(tag, msg);
    }

    public static void w(String tag, String msg)
    {
        if (Log.isLoggable(tag, Log.WARN))
            Log.w(tag, msg);
    }

    public static void e(String tag, String msg)
    {
        if (Log.isLoggable(tag, Log.ERROR))
            Log.e(tag, msg);
    }
}
于 2013-12-06T13:21:50.617 回答
6

如果您可以运行全局替换(一次),然后保留一些编码约定,则可以遵循 Android框架中经常使用的模式。

而不是写

Log.d(TAG, string1 + string2 + arg3.toString());

把它当作

if (BuildConfig.DEBUG) Log.d(TAG, string1 + String.format("%.2f", arg2) + arg3.toString());

现在 proguard 可以从优化发布的 DEX 中删除 StringBuilder 以及它在途中使用的所有字符串和方法。使用proguard-android-optimize.txt,您无需担心android.util.Log在您的proguard-rules.pro

android {
  …
  buildTypes {
    release {
      minifyEnabled true
      proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
    }
  }
}

使用 Android Studio 的 gradle 插件,非常可靠,因此您不需要额外的常量来控制剥离。BuildConfig.DEBUG

于 2017-05-29T12:19:48.563 回答
4

将以下内容添加到您的proguard-rules.txt文件中

-assumenosideeffects class android.util.Log {
  public static *** d(...);
  public static *** w(...);
  public static *** v(...);
  public static *** i(...);
}
于 2017-04-02T13:37:37.727 回答
3

我有一个非常简单的解决方案。我使用 IntelliJ 进行开发,所以细节会有所不同,但这个想法应该适用于所有 IDE。

我选择源代码树的根目录,右键单击并选择“替换”。然后我选择替换所有“日志”。与“//日志。”。这将删除所有日志语句。为了稍后将它们放回去,我重复相同的替换,但这次替换所有“//Log”。用“日志”。

对我来说效果很好。只要记住将替换设置为区分大小写,以避免出现“对话框”等意外。为了增加保证,您还可以使用“日志”执行第一步。作为要搜索的字符串。

杰出的。

于 2014-12-20T18:38:45.380 回答
3

在此处输入图像描述

这是我以前在我的android项目上做的..

在 Android Studio 中,我们可以通过 Ctrl+Shift+F 从整个项目中查找(MacOs 中的 Command+Shift+F)和 Ctrl+Shift+R 替换((MacOs 中的Command+Shift+R))进行类似的操作

于 2015-12-11T11:06:56.357 回答
3

正如zserge 的评论所建议的那样,

Timber 非常好,但如果你已经有一个现有的项目 - 你可以试试 github.com/zserge/log。它是 android.util.Log 的直接替代品,具有 Timber 的大部分功能,甚至更多。

他的日志库提供了简单的启用/禁用日志打印开关,如下所示。

另外,需要改行import,语句不需要改。Log.d(...);

if (!BuildConfig.DEBUG)
    Log.usePrinter(Log.ANDROID, false); // from now on Log.d etc do nothing and is likely to be optimized with JIT
于 2016-06-21T06:57:41.120 回答
2

这就是我在投入生产之前在我的 Kotlin 项目中解决它的方法:

buildTypes {
        release {
            minifyEnabled true
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }

-assumenosideeffects class android.util.Log {
    public static boolean isLoggable(java.lang.String, int);
    public static int d(...);
    public static int w(...);
    public static int v(...);
    public static int i(...);
    public static int e(...);
}
于 2020-07-06T15:39:22.113 回答
2
  1. Application->app->proguard-rules.pro 在此处输入图像描述

  2. 在 proguard-rules.pro 中输入以下代码

    -assumenosideeffects class android.util.Log {
        public static *** d(...);
        public static *** v(...);
        public static *** w(...);
        public static *** i(...);
        public static *** e(...);
    }
    

# 如果您希望日志中的调试类型错误,您可以删除特定的调试类

  1. build.gradle(app) ->android 做这件事

    buildTypes {
            debug{
                debuggable false
                minifyEnabled true
                shrinkResources true
                proguardFiles getDefaultProguardFile('proguard-android- 
                optimize.txt'), 'proguard-rules.pro'
            }
            release {
                debuggable false
                minifyEnabled true
                shrinkResources true
                proguardFiles getDefaultProguardFile('proguard-android- 
                optimize.txt'), 'proguard-rules.pro'
            }
        }
    
     lintOptions {
               checkReleaseBuilds false
               // Or, if you prefer, you can continue to check for errors in release builds,
               // but continue the build even when errors are found:
               abortOnError false
      }
    
于 2021-10-07T07:22:05.917 回答
1

我通过提供对不同日志级别的支持以及根据代码是在实时设备上还是在模拟器上运行自动更改日志级别来改进上述解决方案。

public class Log {

final static int WARN = 1;
final static int INFO = 2;
final static int DEBUG = 3;
final static int VERB = 4;

static int LOG_LEVEL;

static
{
    if ("google_sdk".equals(Build.PRODUCT) || "sdk".equals(Build.PRODUCT)) {
        LOG_LEVEL = VERB;
    } else {
        LOG_LEVEL = INFO;
    }

}


/**
 *Error
 */
public static void e(String tag, String string)
{
        android.util.Log.e(tag, string);
}

/**
 * Warn
 */
public static void w(String tag, String string)
{
        android.util.Log.w(tag, string);
}

/**
 * Info
 */
public static void i(String tag, String string)
{
    if(LOG_LEVEL >= INFO)
    {
        android.util.Log.i(tag, string);
    }
}

/**
 * Debug
 */
public static void d(String tag, String string)
{
    if(LOG_LEVEL >= DEBUG)
    {
        android.util.Log.d(tag, string);
    }
}

/**
 * Verbose
 */
public static void v(String tag, String string)
{
    if(LOG_LEVEL >= VERB)
    {
        android.util.Log.v(tag, string);
    }
}


}
于 2012-03-29T10:12:22.703 回答
1

ProGuard 会在你的发布版本中为你做这件事,现在来自 android.com 的好消息:

http://developer.android.com/tools/help/proguard.html

ProGuard 工具通过删除未使用的代码并用语义模糊的名称重命名类、字段和方法来缩小、优化和混淆您的代码。结果是较小的 .apk 文件更难进行逆向工程。由于 ProGuard 使您的应用程序更难进行逆向工程,因此当您的应用程序利用对安全性敏感的功能时(例如在许可应用程序时),使用它非常重要。

ProGuard 已集成到 Android 构建系统中,因此您不必手动调用它。ProGuard 仅在您以发布模式构建应用程序时运行,因此当您以调试模式构建应用程序时,您不必处理混淆代码。运行 ProGuard 是完全可选的,但强烈推荐。

本文档描述了如何启用和配置 ProGuard 以及使用回溯工具来解码混淆的堆栈跟踪

于 2013-03-26T07:39:52.123 回答
1

如果您想使用编程方法而不是使用 ProGuard,那么通过创建您自己的具有两个实例的类,一个用于调试,一个用于发布,您可以选择在任一情况下登录的内容。

因此,如果您不想在发布时记录任何内容,只需实现一个不执行任何操作的 Logger,如下例所示:

import android.util.Log

sealed class Logger(defaultTag: String? = null) {
    protected val defaultTag: String = defaultTag ?: "[APP-DEBUG]"

    abstract fun log(string: String, tag: String = defaultTag)

    object LoggerDebug : Logger() {
        override fun log(string: String, tag: String) {
            Log.d(tag, string)
        }
    }

    object LoggerRelease : Logger() {
        override fun log(string: String, tag: String) {}
    }

    companion object {
        private val isDebugConfig = BuildConfig.DEBUG

        val instance: Logger by lazy {
            if(isDebugConfig)
            LoggerDebug
            else
                LoggerRelease
        }

    }
}

然后使用您的记录器类:

class MainActivity : AppCompatActivity() {

private val logger = Logger.instance

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    logger.log("Activity launched...")
    ...
    myView.setOnClickListener {
        ...

        logger.log("My View clicked!", "View-click")
    }
}

== 更新 ==

如果我们想避免字符串连接以获得更好的性能,我们可以添加一个带有 lambda 的内联函数,该函数只能在调试配置中调用:

// Add this function to the Logger class.
inline fun commit(block: Logger.() -> Unit) {
    if(this is LoggerDebug)
        block.invoke(this)
}

进而:

 logger.commit {
     log("Logging without $myVar waste of resources"+ "My fancy concat")
 }

由于我们使用的是内联函数,因此没有额外的对象分配和额外的虚拟方法调用。

于 2020-12-18T12:14:18.143 回答
0

我喜欢使用 Log.d(TAG, some string, 通常是 String.format())。

TAG 始终是类名

转换 Log.d(TAG, --> Logd( 在你的课文中

private void Logd(String str){
    if (MainClass.debug) Log.d(className, str);
}

这样,当你准备好发布版本时,将 MainClass.debug 设置为 false!

于 2015-01-14T13:53:59.413 回答
0

可以在 linux 和 sed 中使用 bash 删除日志:

find . -name "*\.java" | xargs sed -ri ':a; s%Log\.[ivdwe].*\);%;%; ta; /Log\.[ivdwe]/ !b; N; ba'

适用于多行日志。在此解决方案中,您可以确定生产代码中不存在日志。

于 2016-12-12T10:42:07.177 回答
0

我知道这是一个老问题,但是为什么不将所有日志调用替换为 Boolean logCallWasHere=true; 之类的东西?//---您的日志的其余部分在这里

这就是为什么您会知道何时要将它们放回去,并且它们不会影响您的 if 语句调用:)

于 2017-09-06T23:40:59.540 回答
0

为什么不只是做

if(BuildConfig.DEBUG)
  Log.d("tag","msg");

? 不需要额外的库,没有倾向于搞砸项目的 proguard 规则,当您进行发布构建时,java 编译器只会为这个调用省略字节码。

于 2019-09-12T10:46:14.643 回答
0

我的方式:

1)启用列选择模式(alt+shift+insert)

2) 选择一个 Log.d(TAG, "text"); “日志”部分。

3)然后做 shift + ctrl + alt + j

4) 点击左箭头

5)做移位+结束

6)点击删除。

这会立即删除 java 文件中的所有 LOG 调用。

于 2020-03-26T18:35:38.950 回答
0

使用 kotlin 很简单,只需声明一些顶级函数

val isDebug: Boolean
    get() = BuildConfig.DEBUG

fun logE(tag: String, message: String) {
    if (isDebug) Log.e(tag, message)
}

fun logD(tag: String, message: String) {
    if (isDebug) Log.d(tag, message)
}
于 2020-05-08T08:52:21.787 回答
0

我在我的项目中使用了以下方法

创建自定义记录器类:

public class LoggerData 
{
   
    public static void showLog(String type, Object object) {
        try {
            Log.d("loggerData:" + type + "-", "showLog: " + new Gson().toJson(object));
        } catch (Exception e) {
            Log.d("TAG", "showLog: " + e.getLocalizedMessage());
            Log.d("loggerData:" + type + "-", "showLog: " + object);
        }

    }

    public static void showLog(Object object) {
        
            try {
                Log.d("loggerData:" + "-", "showLog: +" + new Gson().toJson(object));
            } catch (Exception e) {
                Log.d("TAG", "showLog: " + e.getLocalizedMessage());
                Log.d("loggerData:" + "-", "showLog: " + object);
            }
        
    }
}

然后每当需要登录代码时,就像这样使用

  LoggerData.showLog("Refreshed token: ", token);

在构建发布 APK 之前,仅在 LoggerData 类中的一个位置禁用日志

例子

public class LoggerData {
    

    public static void showLog(String type, Object object) {
        try {
            //Log.d("loggerData:" + type + "-", "showLog: " + new Gson().toJson(object));
        } catch (Exception e) {
            //Log.d("TAG", "showLog: " + e.getLocalizedMessage());
            //Log.d("loggerData:" + type + "-", "showLog: " + object);
        }

    }

    public static void showLog(Object object) {
       
            try {
              //  Log.d("loggerData:" + "-", "showLog: +" + new Gson().toJson(object));
            } catch (Exception e) {
                //Log.d("TAG", "showLog: " + e.getLocalizedMessage());
                //Log.d("loggerData:" + "-", "showLog: " + object);
            }
        }
    }

希望它也能帮助你。

于 2021-12-17T13:51:41.253 回答
-1

最简单的方法;

采用DebugLog

发布应用时,DebugLog 会禁用所有日志。

https://github.com/MustafaFerhan/DebugLog

于 2015-07-01T08:55:35.300 回答
-1

如果您不想弄乱其他库或手动编辑代码,这是我的解决方案。我创建了这个 Jupyter notebook来检查所有 java 文件并注释掉所有日志消息。不完美,但它为我完成了工作。

于 2020-02-29T20:40:46.737 回答
-1

您可以尝试使用这种简单的常规方法:

Ctrl+ Shift+R

代替

Log.e(

// Log.e(
于 2020-04-27T17:58:28.317 回答