Gängige Kotlin-Muster mit Android verwenden

In diesem Thema geht es um einige der nützlichsten Aspekte der Kotlin-Sprache. bei der Entwicklung für Android.

Mit Fragmenten arbeiten

In den folgenden Abschnitten werden Fragment-Beispiele verwendet, um einige der Kotlin-Funktionen hervorzuheben. die besten Funktionen.

Inheritance

Sie können eine Klasse in Kotlin mit dem Schlüsselwort class deklarieren. Im Folgenden Beispiel: LoginFragment ist eine abgeleitete Klasse von Fragment. Sie können angeben, Vererbung mithilfe des :-Operators zwischen der Unterklasse und ihrem übergeordneten Element:

class LoginFragment : Fragment()

In dieser Klassendeklaration ist LoginFragment für das Aufrufen der Konstruktor der Basisklasse Fragment.

Innerhalb von LoginFragment können Sie eine Reihe von Lebenszyklus-Callbacks überschreiben, auf Statusänderungen in Fragment reagieren. Verwenden Sie zum Überschreiben einer Funktion die Methode override, wie im folgenden Beispiel gezeigt:

override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
): View? {
    return inflater.inflate(R.layout.login_fragment, container, false)
}

Um auf eine Funktion in der übergeordneten Klasse zu verweisen, verwenden Sie das Schlüsselwort super wie gezeigt im folgenden Beispiel:

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
}

Null-Zulässigkeit und Initialisierung

In den vorherigen Beispielen haben einige der Parameter in den überschriebenen Methoden Typen mit einem angehängten Fragezeichen ?. Das bedeutet, dass die Argumente für diese Parameter übergebene Null sein können. Achten Sie darauf, ihre Null-Zulässigkeit sicher handhaben können.

In Kotlin müssen Sie die Eigenschaften eines Objekts initialisieren, wenn Sie das Objekt deklarieren. Wenn Sie also eine Instanz einer Klasse abrufen, können Sie sofort auf ihre barrierefreien Eigenschaften verweisen. Die View-Objekte in einer Fragment, aber erst durch Aufrufen von Fragment#onCreateView aufgebläht werden. Sie benötigen eine Möglichkeit, die Initialisierung der Property für ein View zu verschieben.

Mit lateinit können Sie die Initialisierung von Properties verzögern. Wenn Sie lateinit verwenden, sollten Sie Ihre Property so schnell wie möglich initialisieren.

Im folgenden Beispiel sehen Sie, wie lateinit verwendet wird, um View-Objekte in onViewCreated:

class LoginFragment : Fragment() {

    private lateinit var usernameEditText: EditText
    private lateinit var passwordEditText: EditText
    private lateinit var loginButton: Button
    private lateinit var statusTextView: TextView

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        usernameEditText = view.findViewById(R.id.username_edit_text)
        passwordEditText = view.findViewById(R.id.password_edit_text)
        loginButton = view.findViewById(R.id.login_button)
        statusTextView = view.findViewById(R.id.status_text_view)
    }

    ...
}

Conversion-Tracking für Display-Kampagnen

Sie können Click-Events in Android erfassen, indem Sie die Funktion OnClickListener-Schnittstelle. Button-Objekte enthalten einen setOnClickListener() -Funktion, die eine OnClickListener-Implementierung annimmt.

OnClickListener hat die einzelne abstrakte Methode onClick(), die Sie ausführen müssen umsetzen. Weil setOnClickListener() immer ein OnClickListener ein Argument, und weil OnClickListener immer dieselbe einzelne Zusammenfassung hat kann diese Implementierung mithilfe einer anonymen Funktion in Kotlin Dieser Prozess wird als Konvertierung einer einzelnen abstrakten Methode oder SAM-Konvertierung.

Durch die Umwandlung von Stichproben werden in Ihrem Code deutlich sauberere Codes erstellt. Im folgenden Beispiel zeigt, wie Sie mithilfe der SAM-Konvertierung ein OnClickListener für eine Button:

loginButton.setOnClickListener {
    val authSuccessful: Boolean = viewModel.authenticate(
            usernameEditText.text.toString(),
            passwordEditText.text.toString()
    )
    if (authSuccessful) {
        // Navigate to next screen
    } else {
        statusTextView.text = requireContext().getString(R.string.auth_failed)
    }
}

Code in der anonymen Funktion, die an setOnClickListener() übergeben wurde wird ausgeführt, wenn ein Nutzer auf loginButton klickt.

Companion-Objekte

Companion-Objekte bieten einen Mechanismus zum Definieren von Variablen oder Funktionen, die verknüpft sind. konzeptionell an einen Typ an, sind aber nicht an ein bestimmtes Objekt gebunden. Companion-Anzeige -Objekten ähneln der Verwendung des Java-Schlüsselworts static für Variablen und Methoden.

Im folgenden Beispiel ist TAG eine String-Konstante. Sie benötigen keine eindeutige Instanz von String für jede Instanz von LoginFragment, daher sollten Sie in einem Companion-Objekt definieren:

class LoginFragment : Fragment() {

    ...

    companion object {
        private const val TAG = "LoginFragment"
    }
}

Sie können TAG auf oberster Ebene der Datei definieren, aber die Datei kann auch eine große Anzahl von Variablen, Funktionen und Klassen enthalten. die ebenfalls auf oberster Ebene definiert sind. Companion-Objekte helfen beim Verbinden Variablen, Funktionen und der Klassendefinition, ohne sich auf für eine bestimmte Instanz dieser Klasse.

Property-Delegierung

Beim Initialisieren von Eigenschaften können Sie einige der gängigen Methoden von Android wiederholen. Muster wie der Zugriff auf ein ViewModel innerhalb einer Fragment. Um übermäßige duplizierten Codes können Sie die Syntax der Property-Delegierung von Kotlin verwenden.

private val viewModel: LoginViewModel by viewModels()

Die Property-Delegierung bietet eine gängige Implementierung, die Sie wiederverwenden können in Ihrer App zu optimieren. Android KTX stellt Ihnen einige Property-Bevollmächtigte zur Verfügung. Mit viewModels wird beispielsweise ein ViewModel abgerufen, das dem (aktuell: Fragment).

Bei der Property-Delegierung wird die Reflexion verwendet, was den Leistungsaufwand erhöht. Der Kompromiss besteht in einer prägnanten Syntax, die Entwicklungszeit spart.

Null-Zulässigkeit

Kotlin bietet strenge Regeln für die Null-Zulässigkeit, damit die Typsicherheit während der gesamten Dauer aufrechterhalten wird. für Ihre App. In Kotlin dürfen Verweise auf Objekte keine Nullwerte enthalten: Standardeinstellung. Wenn Sie einer Variablen einen Nullwert zuweisen möchten, müssen Sie eine Nullwerte zulässig deklarieren. Variablentyp ändern, indem Sie ? am Ende des Basistyps hinzufügen.

Der folgende Ausdruck ist beispielsweise in Kotlin nicht zulässig. name ist vom Typ String und darf nicht Nullwerte enthalten:

val name: String = null

Um einen Nullwert zuzulassen, müssen Sie einen String-Typ, für den Nullwerte zulässig sind, String? verwenden: Beispiel:

val name: String? = null

Interoperabilität

Die strengen Regeln von Kotlin machen Ihren Code sicherer und prägnanter. Diese Regeln sind niedriger die Wahrscheinlichkeit eines NullPointerException, der dazu führt, dass deine App Abstürze. Außerdem reduzieren sie die Anzahl der Nullprüfungen, die Sie in Ihrem Code.

Häufig müssen Sie beim Schreiben einer Android-App auch Nicht-Kotlin-Code aufrufen, da Die meisten Android-APIs sind in der Programmiersprache Java geschrieben.

Die Null-Zulässigkeit ist ein Schlüsselbereich, in dem sich Java und Kotlin im Verhalten unterscheiden. Java ist weniger mit der Syntax für Null-Zulässigkeit.

Die Klasse Account hat beispielsweise einige Attribute, darunter ein String-Element mit dem Namen name. In Java gibt es keine Kotlin-Regeln für die Null-Zulässigkeit, mithilfe optionaler Anmerkungen zur Null-Zulässigkeit ob Sie einen Nullwert zuweisen können.

Da das Android-Framework hauptsächlich in Java geschrieben ist, dieses Szenario, wenn APIs ohne Anmerkungen zur Null-Zulässigkeit verwendet werden.

Plattformtypen

Wenn Sie mit Kotlin auf ein nicht annotiertes name-Mitglied verweisen, das in einem Java Account, weiß der Compiler nicht, ob String einer String oder String? in Kotlin. Diese Ambiguität wird durch eine Plattformtyp: String!.

String! hat für den Kotlin-Compiler keine besondere Bedeutung. String! kann für entweder einen String- oder einen String?-Wert. Mit dem Compiler können Sie den Wert einen der beiden Typen. Beachten Sie, dass eine NullPointerException ausgegeben wird, wenn Sie den Typ als String darstellen und einen Nullwert zuweisen.

Um dieses Problem zu beheben, sollten Sie beim Schreiben immer Annotationen zur Null-Zulässigkeit verwenden. Code in Java zu schreiben. Diese Annotationen sind sowohl für Java- als auch für Kotlin-Entwickler hilfreich.

Hier sehen Sie als Beispiel die Klasse Account, wie sie in Java definiert ist:

public class Account implements Parcelable {
    public final String name;
    public final String type;
    private final @Nullable String accessId;

    ...
}

Eine der Membervariablen, accessId, ist mit @Nullable annotiert: , was darauf hinweist, dass sie einen Nullwert enthalten kann. Kotlin behandelt dann accessId als String?.

Mit der Annotation @NonNull geben Sie an, dass eine Variable nie null sein darf:

public class Account implements Parcelable {
    public final @NonNull String name;
    ...
}

In diesem Szenario gilt name in Kotlin als eine String, die keine Nullwerte zulässt.

Anmerkungen zur Null-Zulässigkeit sind in allen neuen Android-APIs und vielen bestehenden Android-APIs Viele Java-Bibliotheken haben Anmerkungen zur Null-Zulässigkeit hinzugefügt, um unterstützen sowohl Kotlin- als auch Java-Entwickler.

Umgang mit der Null-Zulässigkeit

Wenn Sie sich bei einem Java-Typ nicht sicher sind, sollten Sie davon ausgehen, dass er Nullwerte zulässt. Beispiel: Das name-Mitglied der Klasse Account ist nicht annotiert. sollte davon ausgehen, dass es sich um eine String mit Nullwerten handelt.

Wenn Sie name kürzen möchten, sodass sein Wert keine vorangestellten oder Leerzeichen einfügen, können Sie die trim-Funktion von Kotlin verwenden. Sie können einen String?. Eine dieser Möglichkeiten ist die Verwendung des Operators not-null Assertion-Operator !! aus, wie im folgenden Beispiel gezeigt:

val account = Account("name", "type")
val accountName = account.name!!.trim()

Der !!-Operator behandelt alles auf seiner linken Seite als nicht null. In diesem Fall behandeln Sie name als String ungleich null. Wenn das Ergebnis der Ausdruck links null ist, löst Ihre App einen NullPointerException aus. Dieser Operator ist schnell und einfach, sollte aber sparsam verwendet werden, fügen Sie Instanzen von NullPointerException wieder in Ihren Code ein.

Sicherer ist die Verwendung des Safe-Call-Operators ?., wie in der folgendes Beispiel:

val account = Account("name", "type")
val accountName = account.name?.trim()

Wenn name mit dem Operator "safe-call" nicht null ist, wird das Ergebnis von name?.trim() ist ein Namenswert ohne voran- oder nachgestellte Leerzeichen. Wenn name ist null, dann ist das Ergebnis von name?.trim() null. Das bedeutet, dass Ihre Anwendung darf beim Ausführen dieser Anweisung niemals NullPointerException auslösen.

Die Funktion „Safe Call“ spart Ihnen zwar eine mögliche NullPointerException, wird ein Nullwert an die nächste Anweisung übergeben. Sie können stattdessen Nullwerte Fälle sofort mithilfe eines Elvis-Operators (?:) zu stellen, wie im Folgenden Beispiel:

val account = Account("name", "type")
val accountName = account.name?.trim() ?: "Default name"

Wenn das Ergebnis des Ausdrucks auf der linken Seite des Elvis-Operators null enthält, wird der Wert auf der rechten Seite accountName zugewiesen. Dieses ist nützlich, um einen Standardwert bereitzustellen, der ansonsten null wäre.

Sie können auch den Elvis-Operator verwenden, um eine Funktion frühzeitig zurückzugeben, wie gezeigt im folgenden Beispiel:

fun validateAccount(account: Account?) {
    val accountName = account?.name?.trim() ?: "Default name"

    // account cannot be null beyond this point
    account ?: return

    ...
}

Änderungen bei der Android API

Android APIs werden immer besser für Kotlin geeignet. Viele der Android-Tools enthalten, z. B. AppCompatActivity und Fragment, Anmerkungen zur Null-Zulässigkeit. Bei bestimmten Aufrufen wie Fragment#getContext mehr Kotlin-kompatible Alternativen.

Zum Beispiel ist der Zugriff auf das Context einer Fragment fast immer nicht null, da die meisten Aufrufe in einer Fragment stattfinden, während die Fragment ist an Activity angehängt (eine abgeleitete Klasse von Context). Allerdings Fragment#getContext gibt nicht immer einen Wert ungleich Null zurück, da es Szenarien, in denen Fragment nicht an Activity angehängt ist. Die Rückgabe Für den Typ von Fragment#getContext sind Nullwerte zulässig.

Da für das von Fragment#getContext zurückgegebene Context Nullwerte zulässig sind (und als @Nullable annotiert sind, müssen Sie sie in Ihrem Kotlin-Code als Context? behandeln. Das bedeutet, dass einer der zuvor genannten Operatoren verwendet wird, Null-Zulässigkeit, bevor auf ihre Attribute und Funktionen zugegriffen wird. Für einige von diesen bietet Android alternative APIs, die diesen Komfort bieten. Fragment#requireContext gibt beispielsweise einen Context-Wert zurück, der nicht null ist, und wirft eine IllegalStateException, wenn sie aufgerufen wird, wenn ein Context null sein würde. Auf diese Weise können Sie die resultierende Context als Nicht-Null behandeln, ohne Safe-Call-Operatoren oder Behelfslösungen.

Property-Initialisierung

Eigenschaften in Kotlin werden standardmäßig nicht initialisiert. Sie müssen initialisiert werden wenn ihre einschließende Klasse initialisiert wird.

Sie können Attribute auf verschiedene Arten initialisieren. Im folgenden Beispiel zeigt, wie Sie eine index-Variable initialisieren, indem Sie ihr einen Wert im Klassendeklaration:

class LoginFragment : Fragment() {
    val index: Int = 12
}

Diese Initialisierung kann auch in einem Initialisierungsblock definiert werden:

class LoginFragment : Fragment() {
    val index: Int

    init {
        index = 12
    }
}

In den Beispielen oben wird index initialisiert, wenn LoginFragment konstruiert sein.

Es kann jedoch sein, dass einige Eigenschaften während des Objekts Bauarbeiten. Beispielsweise können Sie auf View innerhalb einer Fragment, was bedeutet, dass das Layout zuerst aufgebläht werden muss. Inflation tritt nicht auf, wenn eine Fragment erstellt wird. Stattdessen wird er beim Aufrufen von Fragment#onCreateView

Eine Möglichkeit, dieses Szenario zu umgehen, besteht darin, die Ansicht als Nullwerte zu deklarieren und so schnell wie möglich initialisieren, wie im folgenden Beispiel gezeigt:

class LoginFragment : Fragment() {
    private var statusTextView: TextView? = null

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
            super.onViewCreated(view, savedInstanceState)

            statusTextView = view.findViewById(R.id.status_text_view)
            statusTextView?.setText(R.string.auth_failed)
    }
}

Das funktioniert zwar wie erwartet, du musst aber jetzt die Null-Zulässigkeit von View verwalten. wenn Sie darauf verweisen. Besser ist es, lateinit für View zu verwenden Initialisierung, wie im folgenden Beispiel gezeigt:

class LoginFragment : Fragment() {
    private lateinit var statusTextView: TextView

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
            super.onViewCreated(view, savedInstanceState)

            statusTextView = view.findViewById(R.id.status_text_view)
            statusTextView.setText(R.string.auth_failed)
    }
}

Mit dem Schlüsselwort lateinit können Sie die Initialisierung einer Property vermeiden, wenn ein erstellt wird. Wenn vor der Initialisierung auf Ihre Property verwiesen wird, Kotlin löst eine UninitializedPropertyAccessException aus. Achten Sie daher darauf, um Ihre Property so schnell wie möglich zu initialisieren.