Android Studio w wersji 4.0 wprowadza bardzo wyczekiwaną przeze mnie funkcjonalność, ponieważ pomimo wprowadzenia możliwości korzystania z Javy 8, obsługa tej wersji języka była bardzo mocno ograniczona. Jedną z nowości było wprowadzenie w Javie 8 przeprojektowanego API obsługi daty i czasu, które nie było dostępne dla każdego w Androidzie.
Kto pisał aplikacje na system operacyjny Android z obsługą operacji na dacie i czasie, wie, że nie jest to najprostsze i najwygodniejsze zadanie. Wprowadzenie obsługi Java 8 do Androida niestety nie pomogło w zupełności, pomimo tego, że ta wersja języka wprowadza zaprojektowane na nowo API do obsługi daty i czasu, było ono dostępne od Android SDK 26, więc aplikacja musiałaby wspierać minimum Androida w wersji 8.0. Korzystanie z Kotlina zamiast Javy również nie rozwiązywało problemu, ponieważ Kotlin nie posiada swojego API do realizacji tych zadań. Najlepszym wyjściem z tej sytuacji było skorzystanie z biblioteki Joda-Time, która oferuje API, które jest zamiennikiem do API Javy.
Teraz jest jeszcze jeden sposób na rozwiązanie tego problemu. Wraz z premierą Android Studio 4.0, została wydana nowa wersja pluginu Gradle Androida w wersji 4.0.0, która umożliwia skorzystanie z nowych API Javy 8 również w starszych wersjach systemu Android przy pomocy biblioteki służącej do desugaryzacji (zamianie ładnego, czytelnego kodu dla programisty na kod zrozumiały dla kompilatora).
Konfiguracja projektu
Aby skorzystać z nowego rozwiązania, należy wprowadzić kilka modyfikacji do projektu. Zmiany należy zastosować w pliku build.gradle
swojego modułu:
android {
defaultConfig {
// wymagane dla minSdkVersion 20 lub niższej
multiDexEnabled true
}
compileOptions {
// flaga ustawiająca obsługę nowych API języka
coreLibraryDesugaringEnabled true
// włączenie kompatybilności z Javą 8
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
dependencies {
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.0.5'
}
Jak widać powyżej, konfiguracja projektu nie jest wcale skomplikowana, a może przynieść nam sporo ułatwień podczas prac nad aplikacjami.
W praktyce całość ogranicza się do ustawienia flagi coreLibraryDesugaringEnabled
, instalacji biblioteki do desugaryzacji oraz włączenia kompatybilności z Javą 8 (jak było to robione do tej pory).
LocalDate
Obiekt tej klasy jest przeznaczony do przechowywania informacji o dacie bez informacji o strefie czasowej. Obiekt tej klasy możemy stworzyć w następujący sposób:
LocalDate.now()
- aktualna dataLocalDate.parse(String date)
- obiekt na podstawie daty w formacie ISOLocalDate.of(int year, int month, int day)
- obiekt na podstawie liczbowej prezentacji rok-miesiąc-dzień
Przykład w języku Kotlin:
val localDateNow = LocalDate.now()
Log.i("TAG_DATE", localDateNow.toString())
val localDateParse = LocalDate.parse("2020-06-01")
Log.i("TAG_DATE", localDateParse.toString())
val localDateOf = LocalDate.of(2020, 6, 1)
Log.i("TAG_DATE", localDateOf.toString())
LocalTime
Klasa ta służy do przechowywania informacji o samym czasie bez informacji o strefie czasowej. Obiekt tej klasy możemy stworzyć w następujący sposób:
LocalTime.now()
- aktualny czasLocalTime.parse(String time)
- obiekt na podstawie czasu w formacie ISOLocalTime.of(int hour, int minute, int second, int nanoOfSecond)
(obowiązkowe jest tylko podanie godziny oraz minuty) - obiekt na podstawie liczbowej prezentacji godzina-minuta-sekunda-nanosekunda
Przykład w języku Kotlin:
val localTimeNow = LocalTime.now()
Log.i("TAG_TIME", localTimeNow.toString())
val localTimeParse = LocalTime.parse("15:30:20")
Log.i("TAG_TIME", localTimeParse.toString())
val localTimeOf = LocalTime.of(15, 30, 20)
Log.i("TAG_TIME", localTimeOf.toString())
LocalDateTime
Ta klasa służy do przechowywania daty oraz czasu w jednym obiekcie. Również ona, tak jak dwie poprzednie, nie przechowuje informacji o strefie czasowej. Obiekty tej klasy można stworzyć w następujący sposób:
LocalDateTime.now()
- aktualny czasLocalDateTime.parse(String time)
- obiekt na podstawie czasu w formacie ISOLocalDateTime.of(int year, int month, int day, int hour, int minute, int second, int nanoOfSecond)
(obowiązkowe jest podanie roku, miesiąca, dnia oraz godziny i minuty) - obiekt na podstawie liczbowej prezentacji godzina-minuta-sekunda-nanosekundaLocalDateTime.of(LocalDate date, LocalTime time)
- na podstawie obiektów klasLocalDate
orazLocalTime
Przykład w języku Kotlin:
val localDateTimeNow = LocalDateTime.now()
Log.i("TAG_DATE_TIME", localDateTimeNow.toString())
val localDateTimeParse = LocalDateTime.parse("2020-06-01T15:30:20")
Log.i("TAG_DATE_TIME", localDateTimeParse.toString())
val localDateTimeOf = LocalDateTime.of(2020, 6, 1, 15, 30, 20)
Log.i("TAG_DATE_TIME", localDateTimeOf.toString())
val localDateTimeOfLocal = LocalDateTime.of(LocalDate.now(), LocalTime.now())
Log.i("TAG_DATE_TIME", localDateTimeOfLocal.toString())
Modyfikacja daty i czasu
Nowe API dostarcza bardzo proste metody służące do modyfikacji zapisanych informacji. Z niebywałą łatwością jesteśmy w stanie dodawać czy odejmować konkretny okres czasu.
Podczas tych operacji warto pamiętać, że daty w nowym API są niezmienne w związku z czym metody modyfikujące datę i czas zwracają nową instancję, nie modyfikując istniejącej.
Do modyfikacji dat możemy wykorzystać metody:
LocaleDate.plusDays(long daysToAdd)
lubLocaleDateTime.plusDays(long daysToAdd)
- dodanie określonej ilości dniLocaleDate.plusWeeks(long weeksToAdd)
lubLocaleDateTime.plusWeeks(long weeksToAdd)
- dodanie określonej ilości tygodniLocaleDate.plusMonths(long monthsToAdd)
lubLocaleDateTime.plusMonths(long monthsToAdd)
- dodanie określonej ilości miesięcyLocaleDate.plusYears(long yearsToAdd)
lubLocaleDateTime.plusYears(long yearsToAdd)
- dodanie określonej ilości latLocaleDate.minusDays(long daysToSubtract)
lubLocaleDateTime.minusDays(long daysToSubtract)
- odjęcie określonej ilości dniLocaleDate.minusWeeks(long weeksToSubtract)
lubLocaleDateTime.minusWeeks(long weeksToSubtract)
- odjęcie określonej ilości tygodniLocaleDate.minusMonths(long monthsToSubtract)
lubLocaleDateTime.minusMonths(long monthsToSubtract)
- odjęcie określonej ilości miesięcyLocaleDate.minusYears(long yearsToSubtract)
lubLocaleDateTime.minusYears(long yearsToSubtract)
- odjęcie określonej ilości lat
Przykład w języku Kotlin:
val localDate = LocalDate.now()
Log.i("TAG_MODIFY", "Obecna data: $localDate")
val tomorrowDate = localDate.plusDays(1)
Log.i("TAG_MODIFY", "Jutrzejsza data: $tomorrowDate")
val date3MonthsBefore = localDate.minusMonths(3)
Log.i("TAG_MODIFY", "Data sprzed 3 miesięcy: $date3MonthsBefore")
Do modyfikacji czasu możemy wykorzystać metody:
LocalTime.plusHours(long hoursToAdd)
lubLocalDateTime.plusHours(long hoursToAdd)
- dodanie określonej ilości godzinLocalTime.plusMinutes(long minutesToAdd)
lubLocalDateTime.plusMinutes(long minutesToAdd)
- dodanie określonej ilości minutLocalTime.plusSeconds(long secondstoAdd)
lubLocalDateTime.plusSeconds(long secondstoAdd)
- dodanie określonej ilości sekund-
LocalTime.plusNanos(long nanosToAdd)
lubLocalDateTime.plusNanos(long nanosToAdd)
- dodanie określonej ilości nanosekund LocalTime.minusHours(long hoursToSubtract)
lubLocalDateTime.minusHours(long hoursToSubtract)
- odjęcie określonej ilości godzinLocalTime.minusMinutes(long minutesToSubtract)
lubLocalDateTime.minusMinutes(long minutesToSubtract)
- odjęcie określonej ilości minutLocalTime.minusSeconds(long secondsToSubtract)
lubLocalDateTime.minusSeconds(long secondsToSubtract)
- odjęcie określonej ilości sekundLocalTime.minusNanos(long nanosToSubtract)
lubLocalDateTime.minusNanos(long nanosToSubtract)
- odjęcie określonej ilości nanosekund
Przykład w języku Kotlin:
val localTime = LocalTime.now()
Log.i("TAG_MODIFY", "Obecny czas: $localTime")
val time4HoursAfter = localTime.plusHours(4)
Log.i("TAG_MODIFY", "Czas za 4 godziny: $time4HoursAfter")
val time500SecondsBefore = localTime.minusSeconds(500)
Log.i("TAG_MODIFY", "Czas sprzed 500 sekund: $time500SecondsBefore")
Formatowanie daty i czasu
Nowe API daty i czasu wprowadza również nową klasę służącą za formatowanie. Dzięki niej możemy sformatować datę i czas do określonego formatu jako tekst, jak również z niestandardowego formatu tekstu na odpowiedni obiekt:
val localDateTime = LocalDateTime.now()
val dateTimeFormatter = DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm:ss")
val stringFromDateTime = dateTimeFormatter.format(localDateTime)
Log.i("TAG_FORMAT", "Data i czas sformatowane do stringa: $stringFromDateTime")
val dateTimeFromString = LocalDateTime.parse("01.06.2020 15:30:20", dateTimeFormatter)
Log.i("TAG_FORMAT", "Data i czas ze stringa: $dateTimeFromString")
Porównywanie dat
Dzięki nowemu API również w łatwy sposób możemy porównywać ze sobą dwie daty, aby sprawdzić, czy nie są równie lub któraś z nich jest późniejsza. Możemy skorzystać z następujących metod:
date1.isAfter(date2)
- data, na której wywołujemy metodę, jest po dacie z parametrudate1.isBefore(date2)
- data, na której wywołujemy metodę, jest przed datą z parametrudate1.isEqual(date2)
- data, na której wywołujemy metodę, jest równa dacie z parametrudate1.compareTo(date2)
- większe od 0 - odpowiednikisAfter()
, mniejsze od 0 - odpowiednikisBefore()
, równe 0 - odpowiednikisEqual()
A to jest przykład w języku Kotlin:
val localDate1 = LocalDate.of(2020, 6, 1)
val localDate2 = LocalDate.of(2020, 5, 1)
val isAfter = localDate1.isAfter(localDate2)
Log.i("TAG_COMPARE", "Czy pierwsza data jest po drugiej: $isAfter")
val isBefore = localDate1.isBefore(localDate2)
Log.i("TAG_COMPARE", "Czy pierwsza data jest przed drugą: $isBefore")
val isEqual = localDate1.isEqual(localDate2)
Log.i("TAG_COMPARE", "Czy obie daty są równe: $isEqual")
val compareTo = localDate1.compareTo(localDate2)
when {
compareTo > 0 -> {
Log.i("TAG_COMPARE", "Pierwsza data jest po drugiej dacie")
}
compareTo < 0 -> {
Log.i("TAG_COMPARE", "Pierwsza data jest przed drugą datą")
}
compareTo == 0 -> {
Log.i("TAG_COMPARE", "Obie daty są równe")
}
}
Podsumowanie
Ten wpis nie wyczerpuje tematu związanego z nowym API Javy 8 do daty i czasu, które właśnie stało się szeroko dostępne w systemie Android. Dostępne są również inne nowe klasy służące do pracy z datą i czasem jak na przykład Period
, która odpowiada za reprezentację okresu pomiędzy datami, jak również ZonedDateTime
, która jest odpowiednikiem LocalDateTime
jednak przechowującą również informację na temat strefy czasowej. Po bardziej szczegółowe informacje odsyłam do dokumentacji języka Java.
Jak widać, nowe API jest bardzo pomocne i zdecydowanie ułatwia pracę z datą i czasem w trakcie pisania aplikacji na system Android. Szkoda tylko, że trzeba było czekać na nie aż ponad 6 lat, od daty wydania Javy 8.