Android studio date from string

Room + Time

If you’ve started using Room (and you should if you haven’t), there’s a high probability that you will need to store + retrieve some kind of date/time. Room does not provide any support for that out of the box, instead it provides the extensible @TypeConverter annotation, which allows you to provide mappings from arbitrary objects to types Room understands, and vice-versa.

The canonical example in the docs for that API is in fact for date/time:

I used this exact code in my app, and while it technically works, it has two big issues. The first is that it uses the Date class, which should be avoided in nearly all instances. The main issue with Date is the fact that it does not support timezones. At all.

The second issue is that it persists the value as a simple Long , which again can’t store any timezone information.

So let’s say we use the above converters to persist a Date instance to the database, and then later retrieve it. How do you know what timezone the original value is from? The simple answer is that you can’t know. The best you can do is to try and make sure that all Date instances use a common timezone such as UTC. While this allows you to compare different retrieved values against each other (i.e. for sorting), you can never find out the original time zone.

I decided to spend an hour attempting to fix the timezone issue in my app.

SQLite + date/time

The first thing I investigated was SQLite’s support for date and time values, and indeed it does support them. As you’re using Room, it controls which SQL data types your class values map to. For instance String will map to TEXT , Int to INTEGER , etc. But how do we tell Room to map our object date + time values? Well the simple answer is that we don’t need to.

SQLite is a loosely typed database system and stores all values as one of: NULL , INTEGER , TEXT , REAL or BLOB . You’ll notice that there is no special date or time type like you may find in other database systems. Instead they provides the following documentation on how to store date/time values:

SQLite does not have a storage class set aside for storing dates and/or times. Instead, the built-in Date And Time Functions of SQLite are capable of storing dates and times as TEXT, REAL, or INTEGER values

It is these date and time functions which will allow us to store high-fidelity date-time values with minimal/no accuracy loss, specifically using the TEXT type since that support ISO 8601 strings.

Thus we just need to save our values as specially formatted text which contains all of the information we need. We can then use the mentioned SQLite functions to convert our text to a date/time in SQL if needed. The only thing we need to do is make sure our code is using the correct format.

Back to the app

So we know that SQLite supports what we need, but we need to decide how we’re going to represent this in our app.

I’m using ThreeTen-BP in my app, which is a backport of the JDK 8 date and time library (JSR-310) but works on JDK 6+. This library supports timezones, so we’re going to use one of its classes to represent date + times in the app: OffsetDateTime. This class is an immutable representation of both a time and date within a specific offset from UTC/GMT.

So when we look at one of my entities, we now use OffsetDateTime instead of Date:

Читайте также:  Outlook для android как сменить пароль

That’s the entities updated, but now we have to update our TypeConverters so that Room understands how to persist/restore the OffsetDateTime values:

Instead of our previous mapping of Date to/from Long , we’re now mapping OffsetDateTime to/from String .

The methods are pretty simple to look at: one formats a OffsetDateTime to a String, and the other parses a String into an OffsetDateTime. The key puzzle here is making sure that we use the correct String format. Thankfully ThreeTen-BP provides a compatible one for us as DateTimeFormatter. ISO_OFFSET_DATE_TIME .

You might not be using this library though so lets take a look at an example formatted string: 2013-10-07T17:23:19.540-04:00 . Hopefully you can see what date this represents: 7th October 2013, 17:23:19.540 UTC-4. As long as you format/parse to a string like that, SQLite will be able to understand it.

So at this point, we’re nearly done. If you run the app, with an appropriate database version increase + migration, you’ll see that everything should be working nicely.

For more information on migrations with Room, see Florina Muntenescu’s post:

Understanding migrations with Room

Performing database migrations with the SQLite API always made me feel like I was defusing a bomb — as if I was one…

Sorting the Room out

The one thing we haven’t yet fixed is querying on date columns in SQL. The previous Date/Long mapping had an implicit benefit in that numbers are extremely efficient to sort and query. Moving to a String somewhat breaks that though, so let’s fix it.

Say we previously had a query which return all users ordered by their join date. You would probably have had something like this:

Since joined_date was a number ( long , remember), SQLite would do a simple number comparison and return the results. If you run the same query with the new text implementation, you’ll probably notice that the results look the same, but are they?

Well the answer is yes, most of the time. With the text implementation, SQLite is doing a text sort rather than a number sort, which for the majority of cases will be correct. Lets look at some example data:

A simple left-to-right String sort works here, since all of the components of the string are in descending order (year, then month, then day, and so on). The issues comes with the last component of the string, the timezone offset. Lets tweak the data slightly and see what happens:

You can see that the timezone for the 3rd row has changed from UTC to UTC-2. This results in its joined time actually being 09:01:12 in UTC, thus it should actually be sorted as the 2nd row. The returned list contained the same order as before though. This is because we’re still using string ordering, which does not take the timezone into account.

SQLite date time functions

So how do we fix it? Remember those SQLite date/time functions? We just need to make sure we use them when interacting with any date/time columns in SQL. There are 5 functions which SQLite provides:

  1. date(. ) returns just the date.
  2. time(. ) returns just the time.
  3. datetime(. ) returns both the date and time.
  4. julianday(. ) returns the Julian Day.
  5. strftime(. ) returns a value formatted with your given format string. The first four can be thought of as variations of strftime with a pre-defined format.

Since we want to sort on both the date and time, we can use the datetime(. ) function. If we go back to our DAO, the query now becomes:

Easy enough right? After we’ve made this change we now get the correct semantic ordering:

And that was my hour* of work complete! We now support timezoned date/times in Room.

Источник

Быстрый старт Data Binding в Android

Введение

Профессионально андроид-разработкой занимаюсь чуть больше года, до этого разрабатывал по Windows Phone и мне понравилась возможность связывать данные из вью модели с самим View при помощи механизма Bindings. А после изучения RX, многие задачи стали решаться более чисто, вью-модель полностью отделилась от View. Она стала оперировать только моделью, совсем не заботясь о том, как она будет отображаться.

В Android такой строгости я не заметил, Activity или Fragment как простейшие представители контроллера чаще всего имеют полный доступ как ко View, так и к модели, зачастуя решая, какой View будет видим, решая таким образом чисто вьюшные задачи. Поэтому я довольно радостно воспринял новость о появлении Data Binding в Android на прошедшем Google IO.

Читайте также:  Cool tool для андроид

Пока что это только бета релиз, но уже можно протестировать функционал и увидеть направление, в котором двигаются разработчики из Google.

Начало

Я использую Android Studio 1.3. Data binding поддерживает Android 2.1 и выше (API level 7+).

Для сборки используется новый android плагин для Gradle (нужна версия 1.3.0-beta1 и старше). Так как связи отрабатываются во время компиляции, нам понадобиться ещё один плагин к Gradle ‘com.android.databinding:dataBinder:1.0-rc0’. В отличие от того же Windows Phone где механизм привязок реализован глубоко по средством DependencyProperty и в RealTime, в Android эта функция реализуется как бы поверх обычных свойств, во время компиляции и дополнительной кодогенерации, поэтому в случае ошибок будьте готовы разбирать ответ от компилятора.

Итак, заходим в файл build.gradle, который лежит в корневом каталоге проекта (в нём идут настройки Gradle для всего проекта). В блоке dependencies вставляем:

Теперь подключим плагин к конкретному модулю, откроем build.gradle файл, который лежит внутри модуля. По умолчанию app/build.gradle и добавим строчку:

Настройка Layout

Мы должны обернуть наш внешний View в тег
Уже сейчас можно начать его использовать класс Binding для доступа к элементам интерфейса, без использования findViewById. В MainActivity добавим поле и перепишем метод onCreate:

Название поля берётся из Id View, без Id в биндере поле не появиться, если изменить Id View, то поле в биндере сразу же переметнуться. Если с зажатым CTRL нажать на название поля View, то сразу перейдешь к нему в файле разметки. Как по мне так уже одного такого функционала достаточно для того чтобы начать использовать биндинги.

Привязка данных

Например у нас есть карточка пользователя имя и возраст.

Изменим Layout, заменим содержимое LinearLayout на:

И в onCreate заменим последнюю строку на:

Запускаем. Всё работает.
Наверное у всех проектах в активити или в фрагментах встречается такие строчки:

Тут то мы и начинаем использовать непосредственно привязки данных. Перепишем модель:

И добавим в Layout:

На красные выделения студии игнорируем.
Так как мы используем класс View, то его нужно импортировать, добавим в ноду
Или используем его вместе с названием пакета:

Так же возможно в ноде

Конвертеры

Импорт в свою очередь даёт возможность писать конвертеры. Добавим в модель поле с датой рождения и удалим возраст:

Импортируем его в разметку:

Обратная связь и Binding

Попробуем сменить имя пользователя.
Добавим в Layout:

Запускаем и кликаем, тост всплыл, но имя не изменилось. Это случилось из-за того, что модель ни как не известила binder о своём изменении.

Можно создать новую модель и вставить её, но с точки зрения памяти это расточительно:

Или вытащить старую, заменить данные и вставить опять:

Но тогда обновятся все View, связанные с этой моделью. Лучшим вариантом будет связать модель с binder, чтобы она могла его оповестить о своём изменении. Для этого перепишем класс модели, добавив геттеры и сеттеры,

помечая геттеры атрибутом @Bindable, и добавив в сеттеры вызов notifyPropertyChanged(BR.lastName);

Видим новый класс BR, в котором содержатся идентификаторы полей, чьи геттеры помечены атрибутом @Bindable . В Layout оставляем android:text=»@«, меняем только isAdult на adult, c ‘is’ в названии поля возникли проблемы. Запускаем всё работает.

ObservableFields

В пакете android.databinding есть классы, которые могут упростить нотификацию binder об изменении модели:

  • Обёртки над элементарными типами
  • ObservableField
  • ObservableArrayMap
  • ObservableArrayList

Попробуем изменить модель:

По коллекциям аналогично, единственное приведу пример обращения ко ключу к Map:

Из View в Model

Теперь попробуем взять новое имя из UI, привязав EditText к некоторой модели. Так как внутри EditText крутится Editable, то и привязать будет к
Изменю MainActivity:

А в разметку добавлю:

Вот тут возникает проблема, если привязать ObservableField к EditText, то всё будет работать только в сторону View. Как я понял, проблема в том, что Editable, который лежит внутри ObservableField, отличается от того, который лежит внутри EditText.

Если у кого есть идеи — делитесь.

Очень любопытно было увидеть библиотеку для поддержки Data Binding в Android от Google. В документации тоже нет информации про обратную связь данных, но я надеюсь на скорую её реализацию. После официального выхода стабильной версии можно будет посмотреть на интеграцию с JavaRX.

[ Оффициальная документация ]
[ Ссылка на мой простенький пример ]

Источник

Save data in a local database using Room Part of Android Jetpack.

Apps that handle non-trivial amounts of structured data can benefit greatly from persisting that data locally. The most common use case is to cache relevant pieces of data so that when the device cannot access the network, the user can still browse that content while they are offline.

Читайте также:  Как работать компьютеру с андроид

The Room persistence library provides an abstraction layer over SQLite to allow fluent database access while harnessing the full power of SQLite. In particular, Room provides the following benefits:

  • Compile-time verification of SQL queries.
  • Convenience annotations that minimize repetitive and error-prone boilerplate code.
  • Streamlined database migration paths.

Because of these considerations, we highly recommend that you use Room instead of using the SQLite APIs directly.

Setup

To use Room in your app, add the following dependencies to your app’s build.gradle file:

Groovy

Kotlin

Primary components

There are three major components in Room:

  • The database class that holds the database and serves as the main access point for the underlying connection to your app’s persisted data.
  • Data entities that represent tables in your app’s database.
  • Data access objects (DAOs) that provide methods that your app can use to query, update, insert, and delete data in the database.

The database class provides your app with instances of the DAOs associated with that database. In turn, the app can use the DAOs to retrieve data from the database as instances of the associated data entity objects. The app can also use the defined data entities to update rows from the corresponding tables, or to create new rows for insertion. Figure 1 illustrates the relationship between the different components of Room.

Figure 1. Diagram of Room library architecture.

Sample implementation

This section presents an example implementation of a Room database with a single data entity and a single DAO.

Data entity

The following code defines a User data entity. Each instance of User represents a row in a user table in the app’s database.

Kotlin

To learn more about data entities in Room, see Defining data using Room entities.

Data access object (DAO)

The following code defines a DAO called UserDao . UserDao provides the methods that the rest of the app uses to interact with data in the user table.

Kotlin

Database

The following code defines an AppDatabase class to hold the database. AppDatabase defines the database configuration and serves as the app’s main access point to the persisted data. The database class must satisfy the following conditions:

  • The class must be annotated with a @Database annotation that includes an entities array that lists all of the data entities associated with the database.
  • The class must be an abstract class that extends RoomDatabase .
  • For each DAO class that is associated with the database, the database class must define an abstract method that has zero arguments and returns an instance of the DAO class.

Kotlin

Note: If your app runs in a single process, you should follow the singleton design pattern when instantiating an AppDatabase object. Each RoomDatabase instance is fairly expensive, and you rarely need access to multiple instances within a single process.

If your app runs in multiple processes, include enableMultiInstanceInvalidation() in your database builder invocation. That way, when you have an instance of AppDatabase in each process, you can invalidate the shared database file in one process, and this invalidation automatically propagates to the instances of AppDatabase within other processes.

Usage

After you have defined the data entity, the DAO, and the database object, you can use the following code to create an instance of the database:

Kotlin

You can then use the abstract methods from the AppDatabase to get an instance of the DAO. In turn, you can use the methods from the DAO instance to interact with the database:

Kotlin

Additional resources

To learn more about Room, see the following additional resources:

Sample

  • Android Sunflower, a gardening app that illustrates Android development best practices with Android Jetpack.
  • Tivi, a TV show tracking app that uses the latest libraries and tools.

Codelabs

Blogs

Content and code samples on this page are subject to the licenses described in the Content License. Java is a registered trademark of Oracle and/or its affiliates.

Источник

Оцените статью