根据谷歌,我必须在发布我的Android应用程序谷歌播放之前“停用源代码中的Log方法的任何调用”。出版清单第3节节选:

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

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

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

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

那么,是否有更好的源代码级方法来做到这一点呢?或者是一些聪明的ProGuard语法,有效但安全地删除所有Log行?


当前回答

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

为了使保护工作中的副作用假设,有一个先决条件。

在你的gradle文件中,你必须指定proguard-android- optimization .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- optimization .txt文件没有添加这些行,所以现在假设副作用可以工作。

然后,就我个人而言,我使用SLF4J,特别是当我开发一些分发给其他人的库时。其优点是默认情况下没有输出。如果积分器想要一些日志输出,他可以使用Android的Logback并激活日志,这样日志就可以重定向到文件或LogCat。

如果我真的需要从最终库中剥离日志,我就会添加到我的Proguard文件中(当然是在启用了Proguard -android- optimization .txt文件之后):

-assumenosideeffects class * implements org.slf4j.Logger {
    public *** trace(...);
    public *** debug(...);
    public *** info(...);
    public *** warn(...);
    public *** error(...);
}

其他回答

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

为了使保护工作中的副作用假设,有一个先决条件。

在你的gradle文件中,你必须指定proguard-android- optimization .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- optimization .txt文件没有添加这些行,所以现在假设副作用可以工作。

然后,就我个人而言,我使用SLF4J,特别是当我开发一些分发给其他人的库时。其优点是默认情况下没有输出。如果积分器想要一些日志输出,他可以使用Android的Logback并激活日志,这样日志就可以重定向到文件或LogCat。

如果我真的需要从最终库中剥离日志,我就会添加到我的Proguard文件中(当然是在启用了Proguard -android- optimization .txt文件之后):

-assumenosideeffects class * implements org.slf4j.Logger {
    public *** trace(...);
    public *** debug(...);
    public *** info(...);
    public *** warn(...);
    public *** error(...);
}

如果您希望使用编程方法而不是使用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")
    }
}

== update ==

如果我们想避免字符串连接以获得更好的性能,我们可以添加一个带有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")
 }

因为我们使用的是内联函数,所以没有额外的对象分配,也没有额外的虚方法调用。

克里斯托弗的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命令)

为什么不直接做

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

吗?不需要额外的库,没有保护规则,往往会搞砸项目,java编译器只会为这个调用留下字节码,当你发布构建。

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

创建自定义记录器类:

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);
            }
        }
    }

希望它也能帮助到你。