Ta część koncentruje się na najbardziej przydatnych aspektach języka Kotlin. przy tworzeniu aplikacji na Androida.
Praca z fragmentami
W poniższych sekcjach wykorzystano Fragment
przykładów do wyróżnienia niektórych z języków Kotlin
z najlepszymi funkcjami.
Dziedziczenie
klasę w Kotlin można zadeklarować za pomocą słowa kluczowego class
. W następujących
na przykład LoginFragment
jest podklasą klasy Fragment
. Możesz wskazać,
dziedziczenie, używając operatora :
między podklasą a jej klasą nadrzędną:
class LoginFragment : Fragment()
W tej deklaracji klasy LoginFragment
odpowiada za wywoływanie funkcji
konstruktora swojej klasy nadrzędnej Fragment
.
W LoginFragment
możesz zastąpić liczbę wywołań zwrotnych cyklu życia, aby
reagować na zmiany stanu w elemencie Fragment
. Aby zastąpić funkcję, użyj funkcji
override
słowo kluczowe, tak jak w tym przykładzie:
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.login_fragment, container, false)
}
Aby odwołać się do funkcji w klasie nadrzędnej, użyj słowa kluczowego super
, jak pokazano na ilustracji
w tym przykładzie:
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
}
Wartość null i inicjowanie
W poprzednich przykładach niektóre z parametrów zastąpionych metod miały
typy z sufiksem znaku zapytania ?
. Wskazuje to, że argumenty
przekazywane dla tych parametrów mogą mieć wartość null. Koniecznie
bezpiecznie obsługiwać wartości null.
W Kotlin podczas deklarowania obiektu musisz zainicjować jego właściwości.
Oznacza to, że po uzyskaniu instancji klasy można natychmiast
odwołują się do dowolnej z dostępnych właściwości. Obiekty View
w argumencie Fragment
,
nie są jednak gotowe do powiększenia przed wywołaniem funkcji Fragment#onCreateView
, więc
potrzebujesz sposobu na odroczenie inicjalizacji właściwości View
.
Pole lateinit
umożliwia opóźnienie zainicjowania usługi. Jeśli używasz lateinit
,
zainicjuj swoją usługę jak najszybciej.
Ten przykład pokazuje, jak za pomocą lateinit
przypisać obiekty View
w
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)
}
...
}
Konwersja SAM
Możesz rejestrować zdarzenia kliknięcia na Androidzie przez zaimplementowanie tagu
Interfejs OnClickListener
. Button
obiektów zawiera setOnClickListener()
funkcja, która przyjmuje implementację OnClickListener
.
OnClickListener
ma jedną metodę abstrakcyjną, onClick()
, którą musisz wykonać.
do wdrożenia. Ponieważ setOnClickListener()
zawsze pobiera OnClickListener
jako
argument, a ponieważ OnClickListener
zawsze ma ten sam pojedynczy argument
tę implementację można przedstawić za pomocą funkcji anonimowej w argumencie
Kotlin. Ten proces określa się jako
Konwersja pojedynczej metody abstrakcyjnej,
lub konwersji SAM.
Konwersja SAM może znacznie zwiększyć czytelność kodu. Przykład poniżej
pokazuje, jak za pomocą konwersji SAM wdrożyć OnClickListener
w przypadku
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)
}
}
Kod w funkcji anonimowej przekazany do funkcji setOnClickListener()
uruchamia się, gdy użytkownik kliknie loginButton
.
Obiekty towarzyszące
Obiekty towarzyszące
umożliwia definiowanie zmiennych lub połączonych funkcji.
koncepcyjnie związane z typem, ale nie są z nim powiązane. Towarzysz
są podobne do użycia słowa kluczowego static
w Javie na potrzeby zmiennych i metod.
W tym przykładzie TAG
to stała String
. Nie potrzebujesz unikalnego identyfikatora
wystąpienia String
dla każdego wystąpienia LoginFragment
, więc
zdefiniuj go w obiekcie towarzyszącym:
class LoginFragment : Fragment() {
...
companion object {
private const val TAG = "LoginFragment"
}
}
Możesz zdefiniować TAG
na najwyższym poziomie pliku, ale
może też zawierać dużą liczbę zmiennych, funkcji i klas
które są również zdefiniowane na najwyższym poziomie. Obiekty towarzyszące pomagają nawiązać połączenie
zmiennych, funkcji i definicji klasy bez odnoszenia się do żadnych
danej klasy.
Przekazywanie dostępu do usługi
Inicjując właściwości, możesz powtórzyć niektóre z typowych funkcji
takich jak dostęp do ViewModel
w Fragment
. Aby uniknąć nadmiaru
duplikatu kodu, możesz użyć składni przekazywania właściwości w Kotlin.
private val viewModel: LoginViewModel by viewModels()
Przekazywanie usług to typowa implementacja, której możesz używać ponownie
w całej aplikacji. Android KTX udostępnia Ci dostęp do wybranych usług.
viewModels
na przykład pobiera zasób ViewModel
o zakresie ograniczonym do zakresu
obecna wartość Fragment
.
Przekazywanie usług korzysta z refleksji, co zwiększa wydajność. Rozwiązaniem jest zwięzła składnia, która pozwala zaoszczędzić czas przy tworzeniu aplikacji.
Dopuszczalność wartości null
Kotlin zapewnia rygorystyczne reguły dopuszczania wartości null, które zapewniają bezpieczeństwo typu przez cały okres
do aplikacji. W Kotlin odwołania do obiektów nie mogą zawierać wartości null przez
wartość domyślną. Aby przypisać do zmiennej wartość null, musisz zadeklarować wartość nullable
typu zmiennej, dodając ?
na końcu typu podstawowego.
Na przykład poniższe wyrażenie jest niedozwolone w Kotlin. name
jest typu
String
i nie ma wartości null:
val name: String = null
Aby zezwolić na wartość null, musisz użyć typu String
dopuszczającego wartości null (String?
) jako
w tym przykładzie:
val name: String? = null
Interoperacyjność
Ścisłe reguły Kotlin sprawiają, że kod jest bezpieczniejszy i bardziej zwięzły. Te reguły niższe
ryzyko wystąpienia zdarzenia NullPointerException
, które spowodowałoby
. Zmniejszają one też liczbę weryfikacji zerowych, które trzeba przeprowadzić w
w kodzie.
Często przy pisaniu aplikacji na Androida konieczne jest również wywołanie kodu innego niż Kotlin, większość interfejsów API Androida jest napisanych w języku programowania Java.
Zgodność z wartościami null to kluczowy obszar, w którym Java i Kotlin różnią się działaniem. Javy jest mniej rygorystycznie ze składnią dopuszczania wartości null.
Na przykład klasa Account
ma kilka właściwości, np. String
usłudze o nazwie name
. W Javie nie ma reguł Kotlina dotyczących dopuszczalności wartości null,
zamiast polegać na opcjonalnych adnotacjach dopuszczających wartości null, które umożliwiają
czy możesz przypisać wartość null.
Platforma Androida jest napisana głównie w języku Java, więc znalezienie w tym scenariuszu przy wywoływaniu interfejsów API bez adnotacji o dopuszczaniu wartości null.
Typy platform
Jeśli używasz Kotlina, aby odwołać się do elementu name
bez adnotacji, który jest zdefiniowany w
klasy Java Account
, kompilator nie wie, czy String
jest mapowany na
String
lub String?
w Kotlin. Ta niejasność jest reprezentowana przez
typ platformy, String!
.
Funkcja String!
nie ma specjalnego znaczenia dla kompilatora Kotlin. String!
może reprezentować
to String
lub String?
, a kompilator umożliwia przypisanie wartości
obu typów. Pamiętaj, że możesz rzucić NullPointerException
, jeśli
reprezentują typ jako String
i przypisują wartość null.
Aby rozwiązać ten problem, używaj adnotacji dopuszczających wartości null podczas pisania w Javie. Te adnotacje są pomocne zarówno dla programistów Java, jak i Kotlin.
Na przykład oto klasa Account
zdefiniowana w Javie:
public class Account implements Parcelable {
public final String name;
public final String type;
private final @Nullable String accessId;
...
}
Jedna ze zmiennych składowych (accessId
) jest oznaczona adnotacją @Nullable
,
co wskazuje, że może zawierać wartość null. Kotlin leczyłby accessId
jako String?
.
Aby wskazać, że zmienna nie może mieć wartości null, użyj adnotacji @NonNull
:
public class Account implements Parcelable {
public final @NonNull String name;
...
}
W tym scenariuszu wartość name
jest uważana w Kotlin za wartość String
niedopuszczającą wartości nu.
Adnotacje dotyczące wartości null są uwzględniane we wszystkich nowych interfejsach API Androida, a wiele istniejących Interfejsy API dla Androida. Wiele bibliotek Java dodało adnotacje o dopuszczaniu wartości null, aby ulepszyć obsługuje zarówno programistę Kotlin, jak i język Java.
Obsługa dopuszczalności wartości null
Jeśli nie masz pewności co do typu Javy, zastanów się, czy ma on wartość null.
Na przykład element name
klasy Account
nie jest opatrzony adnotacjami, więc
powinien zakładać, że String
to dopuszczalna wartość null.
Jeśli chcesz przyciąć name
tak, by jego wartość nie zawierała początku ani końca
na końcu odstępu, można użyć funkcji trim
systemu Kotlin. Możesz bezpiecznie przyciąć
String?
na kilka różnych sposobów. Jednym z tych sposobów jest użycie parametru not-null
operatora asercji, !!
zgodnie z tym przykładem:
val account = Account("name", "type")
val accountName = account.name!!.trim()
Operator !!
traktuje wszystko po lewej stronie jako niepuste, więc w
W tym przypadku traktujesz name
jako niepustą wartość String
. Jeśli w wyniku działania funkcji
wyrażenie po lewej stronie ma wartość null, aplikacja zgłasza NullPointerException
.
To proste i szybkie rozwiązanie, ale należy z niego korzystać oszczędnie,
ponownie wprowadź wystąpienia NullPointerException
do swojego kodu.
Bezpieczniej jest użyć operatora bezpiecznego połączenia (?.
), jak pokazano w
następujący przykład:
val account = Account("name", "type")
val accountName = account.name?.trim()
Jeśli przy użyciu operatora bezpiecznego połączenia name
ma wartość różną od null, wynik funkcji
name?.trim()
to wartość nazwy bez spacji na początku i na końcu. Jeśli
name
ma wartość null, a wynik funkcji name?.trim()
to null
. Oznacza to, że
aplikacja nigdy nie może zgłosić NullPointerException
podczas wykonywania tej instrukcji.
Operator bezpiecznego połączenia eliminuje potencjalne NullPointerException
,
przekazuje ona wartość zerową do następnej instrukcji. Zamiast tego możesz obsługiwać wartość null
natychmiast, używając operatora Elvisa (?:
), jak pokazano w tym przykładzie:
przykład:
val account = Account("name", "type")
val accountName = account.name?.trim() ?: "Default name"
Jeśli wynikiem wyrażenia po lewej stronie operatora Elvisa jest
null, to wartość po prawej stronie jest przypisana do accountName
. Ten
jest przydatna do podania wartości domyślnej, która w innym przypadku miałaby wartość null.
Możesz też użyć operatora Elvisa, aby wcześniej wrócić z funkcji, jak pokazano na ilustracji. w tym przykładzie:
fun validateAccount(account: Account?) {
val accountName = account?.name?.trim() ?: "Default name"
// account cannot be null beyond this point
account ?: return
...
}
Zmiany w interfejsie Android API
Interfejsy API Androida są coraz bardziej przyjazne Kotlin. Wiele funkcji
najpopularniejsze interfejsy API, w tym AppCompatActivity
i Fragment
, zawierają
adnotacje dotyczące wartości null, a niektóre wywołania, np. Fragment#getContext
, mają
więcej alternatyw, które są przyjazne dla Kotlin.
Na przykład dostęp do pola Context
elementu Fragment
niemal zawsze ma wartość inną niż null,
bo większość połączeń w Fragment
ma miejsce, gdy Fragment
jest dołączony do Activity
(podklasy Context
). Mając to na uwadze,
Fragment#getContext
nie zawsze zwraca wartość niepustą, ponieważ
w sytuacjach, gdy Fragment
nie jest połączony z tabelą Activity
. Dlatego też zwrot
Typ Fragment#getContext
dopuszcza wartość null.
Ponieważ funkcja Context
zwrócona z metody Fragment#getContext
ma wartość null (i jest
oznaczone jako @Nullable), musisz traktować je jako Context?
w kodzie Kotlin.
Oznacza to zastosowanie jednego z wymienionych wcześniej operatorów w celu
do wartości null przed uzyskaniem dostępu do jej właściwości i funkcji. W przypadku niektórych z tych
w przypadku różnych scenariuszy Android zawiera alternatywne interfejsy API, które zapewniają tę wygodę.
Fragment#requireContext
na przykład zwraca niepustą wartość Context
i zwraca
IllegalStateException
, jeśli funkcja Context
ma wartość null, zostanie wywołana. W ten sposób
możesz traktować wynikowy Context
jako niepusty bez konieczności stosowania
operatory zabezpieczeń telefonii komórkowej i sposoby obejścia tego problemu.
Inicjowanie usługi
Właściwości w Kotlin nie są domyślnie inicjowane. Muszą zostać zainicjowane gdy inicjowana jest klasa zamykająca.
Właściwości można inicjować na kilka różnych sposobów. Przykład poniżej
pokazuje, jak zainicjować zmienną index
przez przypisanie do niej wartości w parametrze
deklaracja klasy:
class LoginFragment : Fragment() {
val index: Int = 12
}
To inicjowanie można również zdefiniować w bloku inicjującym:
class LoginFragment : Fragment() {
val index: Int
init {
index = 12
}
}
W powyższych przykładach zdarzenie index
jest inicjowane, gdy LoginFragment
jest
i całego świata.
Możesz jednak mieć kilka właściwości, których nie można zainicjować podczas obiektu
budowy. Możesz na przykład odwołać się do View
z poziomu
Fragment
, co oznacza, że najpierw należy powiększyć szablon. Inflacja
nie występują, gdy jest utworzony Fragment
. Podczas połączenia zostanie ona zawyżona.
Fragment#onCreateView
Jednym ze sposobów na rozwiązanie tego problemu jest zadeklarowanie widoku jako możliwego do null i zainicjuj go jak najszybciej, jak w poniższym przykładzie:
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)
}
}
Mimo że to działa zgodnie z oczekiwaniami, musisz teraz zarządzać dopuszczaniem wartości null pola View
za każdym razem,
gdy się do niego odwołasz. Lepszym rozwiązaniem jest użycie parametru lateinit
dla atrybutu View
zgodnie z poniższym przykładem:
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)
}
}
Słowo kluczowe lateinit
pozwala uniknąć inicjowania właściwości, gdy
gdy powstaje obiekt. Jeśli do Twojej usługi odwołują się odwołania przed jej zainicjowaniem,
Kotlin rzuca UninitializedPropertyAccessException
.
zainicjuj swoją usługę tak szybko, jak to możliwe.