繁体   English   中英

在 Kotlin 中定义日志 TAG 常量的最佳方法是什么?

[英]What is the best way to define log TAG constant in Kotlin?

我正在我的 Android 应用程序中创建我的第一个 Kotlin 类。 通常出于记录目的,我有一个名称为TAG的常量。 我会用 Java 做的是:

private static final String TAG = MyClass.class.getSimpleName();

我知道在 Kotlin 类中我可以使用这种方式创建TAG

private val TAG = MyClass::class.java.simpleName

这对于使用 Java 和 Kotlin 的项目来说没问题,但是如果我开始一个只使用 Kotlin 的新项目怎么办? 如何定义TAG常量? 有没有更多的 Kotlin 方式我没有这个奇怪的构造class.java.simpleName

这个扩展允许我们在任何类中使用 TAG

val Any.TAG: String
    get() {
        val tag = javaClass.simpleName
        return if (tag.length <= 23) tag else tag.substring(0, 23)
    }

//usage
Log.e(TAG,"some value")

它还验证了可以作为 Android 有效的 Log 标签工作。

一般来说,常量都是大写的(例如 FOO)并位于伴随对象中

class MyClass {
    companion object {
        public const val FOO = 1

    }
}

并定义您可以使用的 TAG 字段:

private val TAG = MyClass::class.qualifiedName

在 Kotlin 中,您可以创建一个扩展,并将标记作为方法调用来调用。 这意味着您永远不必在每个类中定义它,我们可以在每次调用该方法时动态构造它:

inline fun <reified T> T.TAG(): String = T::class.java.simpleName

我喜欢TAGFredy Mederos 建议的扩展功能。

扩展他的答案以支持匿名类:

 /**
 * extension function to provide TAG value
 */
val Any.TAG: String
    get() {
        return if (!javaClass.isAnonymousClass) {
            val name = javaClass.simpleName
            if (name.length <= 23) name else name.substring(0, 23)// first 23 chars
        } else {
            val name = javaClass.name
            if (name.length <= 23) name else name.substring(name.length - 23, name.length)// last 23 chars
        }
    }

通常建议的使用companion object生成伴生类的额外static final实例,因此性能和内存方面都很差。

最好的方式(恕我直言)

将日志标记定义为顶级常量,因此只会生成额外的类 ( MyClassKt ),但与companion object相比,它将没有static final实例(也没有任何实例):

private const val TAG = "MyLogTag"

class MyClass {

    fun logMe() {
        Log.w(TAG, "Message")
    }
}

另一种选择

使用普通val 尽管看到日志标记不是全大写的常量看起来很不寻常,但这不会生成任何类并且开销最小。

class MyClass {

    private val tag = "myLogTag"

    fun logMe() {
        Log.w(tag, "Message")
    }
}

简单地做以下工作对我有用。

private val TAG = this::class.java.simpleName

最好的登录方式(恕我直言)是使用 Timber: https : //github.com/JakeWharton/timber

但是如果你不想使用图书馆,那么

TAG 可以定义为内联扩展属性(例如在Extensions.kt ):

inline val <reified T> T.TAG: String
    get() = T::class.java.simpleName

更多的扩展是不要在Log.d(TAG, "")Log.d(TAG, "")写 TAG :

inline fun <reified T> T.logv(message: String) = Log.v(TAG, message)
inline fun <reified T> T.logi(message: String) = Log.i(TAG, message)
inline fun <reified T> T.logw(message: String) = Log.w(TAG, message)
inline fun <reified T> T.logd(message: String) = Log.d(TAG, message)
inline fun <reified T> T.loge(message: String) = Log.e(TAG, message)

然后你可以在任何类中使用它们:

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    logd("Activity created")
}

您可以通过@JvmField定义您的TAG ,如下所示:

companion object {
    @JvmField val TAG: String = MyClass::class.java.simpleName
}

更多细节可以阅读这篇文章: Kotlin 的隐性成本

我创建了一些日志扩展函数来避免像我们在 Java 中所做的那样声明日志标记(可能性能较低,但考虑到我们正在谈论日志记录,这应该是可以接受的 IMO)。 该方法使用具体化的类型参数和其他 Kotlin 好东西来检索类的简单名称。 这是一个基本示例:

inline fun <reified T> T.logi(message: String) =
   Log.i(T::class.java.simpleName, message)

你可以在这里找到更详细的要点

我正在创建常量作为伴随对象:

companion object {
    val TAG = "SOME_TAG_VALUE"
}

然后,我可以像这样使用它:

MyClass.TAG

使用Kotlin 1.2.20更新答案

class MyClass {
    companion object {

        @JvmField
        public val FOO = 1
    }
}

用途

MyClass.FOO

使用val声明 TAG 变量

class YourClass {
   companion object {
      //if use java and kotlin both in project
      //private val TAG = MyClass::class.java.simpleName

      //if use only kotlin in project
      private val TAG = YourClass::class.simpleName
   }
}

使用变量,如

Log.d(YourClass.TAG, "Your message");
//or 
Log.e(TAG, "Your message");

我找到了一种更“复制粘贴”的方法,因为它不需要您输入类的名称:

package com.stackoverflow.mypackage

class MyClass
{
    companion object {
        val TAG = this::class.toString().split(".").last().dropLast(10)
    }
}

这不是最优雅的解决方案,但它有效。

this::class.toString().split(".").last()会给你"com.stackoverflow.mypackage.MyClass$Companion"所以你需要dropLast(10)来删除$Companion

或者你可以这样做:

package com.stackoverflow.mypackage

class MyClass
{
    val TAG = this::class.simpleName
}

但是随后TAG成员变量不再是“静态的”并且不遵循推荐的命名约定。

你可以试试这个:

companion object {
    val TAG = ClearCacheTask::class.java.simpleName as String
}

AnkoLogger使用一个接口来定义日志标签。

interface AnkoLogger {
            /**
             * The logger tag used in extension functions for the [AnkoLogger].
             * Note that the tag length should not be more than 23 symbols.
             */
            val loggerTag: String
                get() = getTag(javaClass)
        }
private fun getTag(clazz: Class<*>): String {
        val tag = clazz.simpleName
        return if (tag.length <= 23) {
            tag
        } else {
            tag.substring(0, 23)
        }
    }
inline fun AnkoLogger.info(message: () -> Any?) {
    val tag = loggerTag
    if (Log.isLoggable(tag, Log.INFO)) {
        Log.i(tag, message()?.toString() ?: "null")
    }
}

你可以这样使用它:

class MyClass : AnkoLogger {
    fun someFun(){
       info("logging info")
    }
}

也许 AnkoLogger 可以给你一些实现自定义日志工具的想法。

在 Android Studio 中,重命名的常用方法是右键单击名称,选择 Refactor->Rename。 所以,我认为做这样的事情很好,

class MyClass {
    companion object {
        private const LOG_TAG = "MyClass"
    }
}

因为如果您像我描述的那样重命名类MyClass ,那么 IDE 也会建议重命名您的 LOG_TAG 字符串。

最终,使用这种方法与其他方法相比有利有弊。 因为 LOG_TAG 是一个字符串,所以不需要像将LOG_TAG设置为MyClass::class.simpleName那样导入 kotlin- MyClass::class.simpleName 此外,因为变量是使用const关键字声明为编译时常量,所以生成的字节码更小,因为它不需要生成更多隐藏的 getter,如本文所述

这是我在 kotlin 中的扩展函数,只需将其添加到您的扩展文件中即可。

val Any.TAG: String
get() {
    return if (!javaClass.isAnonymousClass) {
        val name = javaClass.simpleName
        if (name.length <= 23 || Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) name else
            name.substring(0, 23)// first 23 chars
    } else {
        val name = javaClass.name
        if (name.length <= 23 || Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
            name else name.substring(name.length - 23, name.length)// last 23 chars
    }
}

然后你可以在任何类中使用 TAG,如下所示:

Log.d(TAG, "country list")

我定义了一个接口,它将 TAG 定义为具有默认 getter 实现的属性。 接口既可以由类“实现”,也可以由其伴随对象“实现”:

//Prefix allows easier filtering in LogCat
private const val PREFIX = "somePrefix.";

interface HasLogTag {
    val TAG: String
        get() {
            val name = javaClass.canonicalName?.removeSuffix(".Companion")?.substringAfterLast(".")
            return "$PREFIX${name}"
        }
}

接口使用如下:

import yourPackage.HasLogTag     
....
class MyClass : HasLogTag {
    ...
    //Alternatively: Let the companion object "implement" the interface
    companion object : HasLogTag {
    ...
    Log.e(TAG, "Some Info)
}

由于每次使用都会调用 getter,因此为伴随对象定义 TAG 没有任何好处。

注意:在之前的版本中,我使用反射来找出是类本身还是伴随对象定义了接口。

然而,这似乎严重减慢了我的应用程序的启动速度。

有时我更喜欢使用实时模板。 在 android studio 中,您可以复制“logd”模板,重命名并将“TAG”更改为“javaClass.simpleName”。

现在,如果您键入“logc”(或您拥有的任何模板名称),它将生成包括方法名称在内的整行,而不会用扩展、字段或静态函数污染代码。

设置

如何使用

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM