Was ist der beste Weg, um die log TAG-Konstante in Kotlin zu definieren?

74

Ich erstelle meine ersten Kotlin-Klassen in meiner Android-Anwendung. Normalerweise habe ich zu Protokollierungszwecken eine Konstante mit Namen TAG. Was ich in Java tun würde, ist:

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

Ich weiß, dass ich in Kotlin-Klassen auf folgende TAGWeise erstellen kann:

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

Dies ist in Ordnung für Projekte, die Java und Kotlin verwenden. Was ist jedoch, wenn ich ein neues Projekt starte, das sich nur in Kotlin befindet? Wie kann ich dort TAGKonstante definieren ? Gibt es mehr Kotlin-Wege, auf denen ich diese seltsame Konstruktion nicht habe class.java.simpleName?

Mario Kutlev
quelle
8
Die Verwendung simpleNameist riskant, wenn der Klassenname verschleiert ist.
BakaWaii
1
Riskant? Um es vorsichtig auszudrücken! Es stürzt IMMER meine Apps ab, wenn es auf diese Weise gemacht wird.
SMBiggs
private val TAG = MainActivity :: class.java.simpleName;
Webserveis
check out youtube.com/watch?v=buhk5TRX_rw&t=3s ab 7:24
Cflux

Antworten:

52

Im Allgemeinen sind Konstanten alle Kappen (z. B. FOO) und befinden sich im Begleitobjekt :

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

    }
}

und um das TAG-Feld zu definieren, können Sie Folgendes verwenden:

private val TAG = MyClass::class.qualifiedName
Gabriele Mariotti
quelle
MyClass :: class.qualifiedName gibt einen String zurück, sodass Fehler mit const angezeigt werden. Was ist zu tun?
1
@ NamulHaque- constWerte müssen Kompilierungszeitkonstanten sein. Da Kotlin nichts Vergleichbares hat nameof, kann es nicht garantieren, dass diese Aussage immer den gleichen Wert hat. Auch, wie Gabriele Mariotti sagte, constwurde in der Antwort nicht verwendet.
Salem
24
Laut den Warnungen von Android Studio MyClass::class.qualifiedNameerfordert Hmm Reflexion, was die Komplexität der Anwendung erheblich erhöht und eine zusätzliche JAR-Datei erfordert. Gibt es einen einfacheren Weg, um diese ach so häufige Aufgabe zu erledigen?
SMBiggs
6
@ScottBiggs wie wäre es mit MyClass::class.java.simpleName?
Sazzad Hissain Khan
1
Gibt es eine gute Referenz, um zu lesen, wie die Verwendung von Reflexion die Anwendungskomplexität erhöht? (Ich habe hier nichts darüber gesehen: kotlinlang.org/docs/reference/reflection.html )
StephenT
52

Diese Erweiterung ermöglicht es uns, TAG in jeder Klasse zu verwenden

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

Es wurde auch validiert, um als gültiges Android-Protokoll-Tag zu funktionieren.

Fredy Mederos
quelle
1
Danke, ich benutze es die ganze Zeit!
Fredy Mederos
wird nicht für anonyme innere Klasse arbeiten, sonst großartig
Rahul Tiwari
1
Ich bin neu in Kotlin, also eine ehrliche Frage: Gibt es irgendwelche Nachteile (außer der anonymen inneren Klasse)? Es scheint die beste Antwort zu sein, denn ich denke, dafür sind Kotlin-Erweiterungen gedacht.
fklappan
1
Dies ist eine gute Antwort, aber die Längenprüfung ist nicht erforderlich, wenn Ihr minSDK 24 oder höher ist, da keine Einschränkung der Tag-Länge mehr besteht.
Alther
Wenn ich eine Klasse mit 30 Tags habe, bedeutet dies, dass ich TAG zur Laufzeit 30 Mal instanziiere? Oder wird nur ein Tag instanziiert und darauf verwiesen?
Hatzil
13

In Kotlin können Sie eine Erweiterung erstellen und stattdessen das Tag als Methodenaufruf aufrufen. Dies würde bedeuten, dass Sie es niemals in jeder Klasse definieren müssen. Wir können es jedes Mal dynamisch erstellen, wenn wir die Methode aufrufen:

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

Der allgemein vorgeschlagene Ansatz zur Verwendung der companion objectgeneriert eine zusätzliche static finalInstanz einer Companion-Klasse und ist daher in Bezug auf Leistung und Speicher schlecht.

Der beste Weg (IMHO)

Definieren Sie ein Protokoll-Tag als Konstante der obersten Ebene, sodass nur eine zusätzliche Klasse generiert wird ( MyClassKt), aber im Vergleich dazu companion objectgibt es keine static finalInstanz davon (und überhaupt keine Instanz):

private const val TAG = "MyLogTag"

class MyClass {

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

Andere Option

Verwenden Sie eine normale val. Obwohl dies ungewöhnlich erscheint, wenn ein Protokoll-Tag nicht als Konstante in Großbuchstaben angezeigt wird, werden dadurch keine Klassen generiert und der geringste Overhead verursacht.

class MyClass {

    private val tag = "myLogTag"

    fun logMe() {
        Log.w(tag, "Message")
    }
}
Jaroslaw Mytkalyk
quelle
11

Einfach das Folgende zu tun hat bei mir funktioniert.

private val TAG = this::class.java.simpleName
nickgzzjr
quelle
Ist dieser Code sicher für die Verwendung in einem companion object?
Augusto Carmo
1
du brauchst kotlin-reflekt.jar
Omer
9

Ich möchte TAGeine Erweiterungsfunktion sein, wie von Fredy Mederos vorgeschlagen .

Erweiterung seiner Antwort zur Unterstützung anonymer Klassen:

 /**
 * 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
        }
    }
Rahul Tiwari
quelle
2
Gute Antwort, aber ab API 24 gibt es keine Längenbeschränkung. Wenn das minimale SDK Ihrer App 24+ ist, können Sie die Längenprüfungen abschaffen.
Alther
Ich mag diese Lösung und habe sie in einem aktuellen Projekt ausprobiert, aber aus irgendeinem Grund würde sie in einigen Klassen die Referenz in Android Studio nicht auflösen ... und in anderen würde sie funktionieren. Seltsam
szaske
@szaske sind diese inneren Klassen?
Rahul Tiwari
8

Der beste Weg, um (imho) zu protokollieren, ist die Verwendung von Timber: https://github.com/JakeWharton/timber

Aber wenn Sie keine Bibliothek verwenden möchten, dann

TAG kann als Inline-Erweiterungseigenschaft definiert werden (z. B. in Extensions.kt):

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

Einige weitere Erweiterungen, um nicht ständig TAG zu schreiben in 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)

Und dann können Sie sie in jeder Klasse verwenden:

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    logd("Activity created")
}
Pavel Berdnikov
quelle
Павел, wie willst du Timber verwenden?
CoolMind
@CoolMind Einfach in Anwendungen einrichten onCreate() { if (BuildConfig.DEBUG) Timber.plant(Timber.DebugTree()) }und dann in jeder Klasse verwenden, Timber.d("msg")ohne explizite LOG_TAG zu benötigen
Pavel Berdnikov
Ah, danke, ich habe verstanden. Ich habe gesucht TAG, Timber wurde bereits hinzugefügt.
CoolMind
Hat Kotlin auch das Zeichenlimit für Tags, die die alte Java-Log-Klasse hatte?
Brill Pappin
1
@BrillPappin afaik, die Beschränkung der Tag-Länge bezieht sich auf die Android-Version, nicht auf die Sprache. Laut Dokumentation gibt es seit api24 keine Begrenzung, aber ich habe festgestellt, dass das Problem bei Versionen über 24 auftreten kann
Pavel Berdnikov
5

Sie können Ihre TAGdurch @JvmFieldwie folgt definieren:

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

Weitere Informationen finden Sie in diesem Artikel: Kotlins versteckte Kosten

Andrew Wang
quelle
5
Ihre Quelle sagt dies: "Tatsächlich wurde diese Annotation ausschließlich aus Gründen der Java-Kompatibilität erstellt, und ich würde definitiv nicht empfehlen, Ihren schönen Kotlin-Code mit einer obskuren Interop-Annotation zu überladen." Warum empfehlen Sie dann die Verwendung von @JvmField?
Carsten Hagemann
3

Ich habe einige Protokollerweiterungsfunktionen erstellt, um zu vermeiden, dass das Protokoll-Tag wie in Java deklariert wird (möglicherweise weniger leistungsfähig, aber da wir über die Protokollierung sprechen, sollte dies IMO akzeptabel sein). Der Ansatz verwendet reifizierte Typparameter und andere Kotlin-Goodies, um den einfachen Klassennamen abzurufen. Hier ist ein einfaches Beispiel:

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

Eine ausführlichere Übersicht finden Sie hier

Paolo
quelle
Dies sollte die akzeptierte Antwort sein. Ich verstehe nicht, warum ich den Java-Ansatz weiterhin nachahmen sollte
Francesco Mameli
2

Antwort mit aktualisiert Kotlin 1.2.20

class MyClass {
    companion object {

        @JvmField
        public val FOO = 1
    }
}

Verwendet

MyClass.FOO
Kasim Rangwala
quelle
1

Ich erstelle die Konstante als Begleitobjekt:

companion object {
    val TAG = "SOME_TAG_VALUE"
}

Dann kann ich es so verwenden:

MyClass.TAG
Matias Elorriaga
quelle
1

Deklarieren Sie die TAG-Variable mit 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
   }
}

Verwenden Sie die Variable like

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

quelle
1
Wenn Sie nicht verwenden ::class.java.simpleName, müssen Sie einschließen kotlin-reflect.jar.
CoolMind
1

Ich habe einen Weg gefunden, der mehr "Kopieren-Einfügen" -fähig ist, da Sie nicht den Namen Ihrer Klasse eingeben müssen:

package com.stackoverflow.mypackage

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

Es ist nicht die eleganteste Lösung, aber es funktioniert.

this::class.toString().split(".").last()wird dir geben, "com.stackoverflow.mypackage.MyClass$Companion"damit du das dropLast(10)entfernen musst $Companion.

Alternativ können Sie dies tun:

package com.stackoverflow.mypackage

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

Dann ist die TAGMitgliedsvariable jedoch nicht mehr "statisch" und folgt nicht den empfohlenen Namenskonventionen.

jpihl
quelle
2
Soviel zu Kotlin, um den Code der Kesselplatte zu reduzieren !
SMBiggs
1

Sie können dies versuchen:

companion object {
    val TAG = ClearCacheTask::class.java.simpleName as String
}
Alireza Ghanbarinia
quelle
0

AnkoLogger verwendet eine Schnittstelle, um das Protokoll-Tag zu definieren.

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

Sie können es so verwenden:

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

Vielleicht kann AnkoLogger Ihnen einige Ideen zur Implementierung eines benutzerdefinierten Protokollierungswerkzeugs geben.

Fredy Mederos
quelle
0

In Android Studio können Sie normalerweise etwas umbenennen, indem Sie mit der rechten Maustaste auf den Namen klicken und Refactor-> Rename auswählen. Ich denke, es ist in Ordnung, so etwas zu tun.

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

Wenn Sie die Klasse MyClasswie beschrieben umbenennen , schlägt die IDE vor, auch Ihren LOG_TAG-String umzubenennen.

Letztendlich gibt es Vor- und Nachteile der Verwendung dieser Methode im Vergleich zu anderen Methoden. Da LOG_TAG ein String ist, müssen Sie die Datei kotlin-reflekt.jar nicht importieren, wie Sie es tun würden, wenn Sie LOG_TAGgleich setzen MyClass::class.simpleName. Da die Variable mit dem constSchlüsselwort als Konstante zur Kompilierungszeit deklariert wird, ist der generierte Bytecode kleiner, da keine weiteren versteckten Getter generiert werden müssen, wie in diesem Artikel beschrieben .

Max
quelle
1
Warum nicht einfach nur TAG nennen? Haben Sie normalerweise eine andere Konstante mit TAG im Namen in einer Klasse?
Ewoks
0

Hier ist meine Erweiterungsfunktion in Kotlin. Fügen Sie sie einfach in Ihre Erweiterungsdatei ein.

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

Dann können Sie TAG in jeder Klasse wie der folgenden verwenden:

Log.d(TAG, "country list")
Rajesh.k
quelle