在Kotlin中定义log TAG常数的最佳方法是什么?


76

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

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

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

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

这对于使用Java和Kotlin的项目是可以的,但是如果我启动仅在Kotlin中的新项目该怎么办?如何定义TAG常数?在我没有这种奇怪构造的情况下,还有更多的科特林方式class.java.simpleName吗?


8
如果混淆了类名,则使用simpleName存在风险
BakaWaii

1
有风险?至少可以说!这样一来,它总是会使我的应用程序崩溃。
SMBiggs

私有val TAG = MainActivity :: class.java.simpleName;
Webserveis

Answers:


52

通常,常量是所有大写字母(例如FOO),并且位于伴随对象中

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

    }
}

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

private val TAG = MyClass::class.qualifiedName

MyClass :: class.qualifiedName返回String,所以它用const显示err ...该怎么办?

1
@EnamulHaqueconst值必须是编译时间常数。由于Kotlin没有什么像nameof它不能保证该语句始终具有相同的值。而且,正如加布里埃尔·马里奥蒂(Gabriele Mariotti)所说,const答案中没有使用。
Moira

24
根据Android Studio的警告,HmmMyClass::class.qualifiedName需要进行反射,这大大增加了应用程序的复杂性,并需要附加的jar文件。有没有更简单的方法可以执行此常见任务?
SMBiggs

7
@ScottBiggs如何使用MyClass::class.java.simpleName
Sazzad Hissain Khan,

1
关于使用反射如何增加应用程序复杂性,有很好的参考资料吗?(我在这里什么都没看到:kotlinlang.org/docs/reference/reflection.html
StephenT

54

此扩展允许我们在任何类中使用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标签。


1
谢谢,我一直都在用它!
弗雷迪·梅德罗斯

不会为匿名的内部阶级工作,否则很棒
Rahul Tiwari

1
我是Kotlin的新手,这是一个诚实的问题:这有什么缺点(匿名内部类除外)?这似乎是最好的答案,因为我认为那是kotlin扩展的目的
。.– fklappan

1
这是一个很好的答案,但是如果您的minSDK为24或更高,则不再需要长度检查,因为标签长度不再受限。
Alther

1
这去哪儿了?什么是扩展名?
behelit

13

在Kotlin中,您可以创建一个扩展,然后将tag调用作为方法调用。这意味着您不必在每个类中都定义它,我们可以在每次调用该方法时动态地构造它:

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

12

通常建议的使用companion objectgenerate的方法会生成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")
    }
}

11

只需执行以下对我有用。

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

这段代码可以安全地在内部使用companion object吗?
奥古斯托·卡莫

1
您将需要kotlin-reflect.jar
Omer

10

我喜欢TAG成为Fredy 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
        }
    }

2
很好的答案,但是从API 24开始,没有长度限制,因此,如果您应用的最小SDK为24+,则可以取消长度检查。
Alther

我喜欢这个解决方案,并在当前项目中进行了尝试,但是由于某些原因,在某些类中,它无法解析Android Studio中的引用...而在某些情况下,它仍然可以工作。奇怪
szaske '20

@szaske是那些内部类吗?
拉胡尔·蒂瓦里

9

记录(imho)的最佳方法是使用Timber: https //github.com/JakeWharton/timber

但是,如果您不想使用库,那么

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

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

还有一些扩展,可以一直不写TAG Log.d(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")
}

Павел,您想如何使用Timber?
CoolMind

@CoolMind只需在应用程序中进行设置onCreate() { if (BuildConfig.DEBUG) Timber.plant(Timber.DebugTree()) },然后在任何类中使用它们,Timber.d("msg")而无需使用显式的LOG_TAG
Pavel Berdnikov

啊,谢谢,我明白了。我寻找了TAG,木材已经被添加。
CoolMind

Kotlin是否也具有旧的Java Log类具有的标签的char限制?
Brill Pappin

1
@BrillPappin afaik,标签长度限制与android版本有关,与语言无关。根据文档,自api24以来没有限制,但是我发现问题可能出现在24以上版本
Pavel Berdnikov

5

您可以TAG通过@JvmField以下方式定义您的:

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

有关更多详细信息,您可以阅读本文:Kotlin的隐藏成本


5
您的消息来源说:“实际上,此注释仅是出于Java兼容性的原因而创建的,我绝对不建议您使用晦涩的互操作注释将您美丽的Kotlin代码弄乱”。那么为什么建议您使用@JvmField?
卡斯滕·哈格曼

3

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

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

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


这应该是公认的答案。我不明白为什么还要继续模仿的Java方法
弗朗西斯马梅利


1

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

companion object {
    val TAG = "SOME_TAG_VALUE"
}

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

MyClass.TAG

1

用声明TAG变量 val

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

1
如果不使用::class.java.simpleName,则必须包括在内kotlin-reflect.jar
CoolMind

1

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

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成员变量不再是“静态”的,并且不遵循建议的命名约定。


2
对于Kotlin来说,这是减少模板代码的方法!
SMBiggs

1

您可以尝试以下方法:

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

0

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可以为您提供一些实现自定义日志记录工具的想法。


0

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

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

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

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


1
为什么不仅仅将其命名为TAG?在任何类中,您通常是否都具有名称中带有TAG的其他常量?
Ewoks

0

这是我在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")
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.