Data entry android app

How to store data locally in an Android app

Almost every app we use or develop has to store data for one purpose or another. It’s not all the same data, either — some apps need access to settings, images, and much more. The big question is how to manage this data so that your device can grab only what it needs. Luckily for developers, Android is full of ways to store data, and we’re here to run you through how they work.

For this article, we’ll discuss the different data storage techniques available to Android developers, along with sample code to get you started or to refresh your memory.

Ways to store data

Using Shared Preferences

Shared Preferences is the way to go if you’re saving primitive data as key-value pairs. It requires a key, which is a String, and the corresponding value for the said key. The value can be any of the following: a boolean, float, int, long, or another string.

Your Android device stores each app’s Shared Preferences inside of an XML file in a private directory. Apps can also have more than one Shared Preferences file, and they’re ideally used to store app preferences.

Before you can store data with shared preferences, you must first get a SharedPreferences object. There are two Context methods that you can use to retrieve a SharedPreferences object.

For when your app will have a single preferences file, and

for when your app could have multiple preferences files, or if you prefer to name your SharedPreferences instance.

On getting the SharedPreferences object, you then access its Editor using the edit() method. To actually add a value, use the Editor’s putXXX() method, where XXX is one of Boolean, String, Float, Long, Int, or StringSet. You can also remove a key-value preference pair with remove().

Finally, make sure to call the Editor’s commit() method after putting or removing values. If you don’t call commit, your changes will not be persisted.

For our sample app, we allow the user to specify a SharedPreferences filename. If the user specifies a name, we request for the SharedPreferences with that name; if not, we request the default SharedPreference object.

Unfortunately, there is no way to get a single list of all SharedPreferences files stored by your app. Instead, you will need a static list or access to the SharedPreferences name if you’re storing more than one file.

You could also save your SharedPreferences names in the default file. If you need to store user preferences, you may want to use the PreferenceActivity or PreferenceFragment command. Just remember that they both use Shared Preferences, too.

Using internal storage

There are plenty of times where you may need to persist data, but you find Shared Preferences too limiting. For example, you may need to persist objects or images in Java. You might also need to persist your data logically with the file system hierarchy. This is where internal storage comes in. It is specifically for when you need to store data on the file system, but you don’t want other apps or users to have access.

This data storage is so private, in fact, that it’s deleted from the device as soon as you uninstall your app.

Using internal storage is similar to saving with any other file system. You can get references to File objects, and you can store data of virtually any type using a FileOutputStream. What sets it apart is the fact that its contents are only accessible by your app.

To get access to your internal file directory, use the Context getFilesDir() method. To create (or access) a directory within this internal file directory, use the getDir(directoryName, Context.MODE_XXX) method. The getDir() method returns a reference to a File object representing the specified directory, creating it first if it doesn’t exist.

In the sample above, if the user-specified filename is empty, we get the base internal storage directory. If the user specifies a name, we get the named directory, creating first if needed.

Читайте также:  Домашний вай фай андроид

To read files, use your preferred file reading method. For our example, we read the complete file using a Scanner object. To read a file that’s directly within your internal storage directory (not in any subdirectory), you can use the openFileInput(fileName) method.

Similarly, to access a file for writing directly within the Internal Storage directory, use the openFileOutput(fileName) method. To save files, we use the FileOutputStream write.

As you can see in the image above, the file path is in a folder not accessible by the file manager or other apps. The only exception to this will be if you have a rooted device.

External Storage

Google has made a few key changes to external storage, beginning with Android 10 and continuing in Android 11. To give users better control over their files and cut down on clutter, apps now have scoped access to external storage by default. This means that they can tap into the specific directory on external storage and the media that the app creates.

For more information about requesting scoped directory access, check out this Android developer tutorial.

If your app tries to access a file that it did not create, you will have to permit it to do so every single time. Data you store outside of select folders will also disappear if you delete your app.

Apps are expected to store files in one of two app-specific locations designed for the app’s specific persistent files and cached files, respectively. To access these locations, the app must verify the storage is available (which is not guaranteed, as it is for internal storage). The volume’s state can be queried using:

If MEDIA_MOUNTED is returned, that means you can read and write files to external storage. You will find a number of predefined directories that should aid with logical storage and prevent clutter. These include the likes of DIRECTORY_DOCUMENTS and DIRECTORY_MOVIES.

You can read a full explanation of how to use scoped storage here.

SQLite database

Finally, Android provides support for apps to use SQLite databases for data storage. The databases you create remain specific to your app and can only be accessed inside your app. Of course, you should have at least some knowledge of SQL before you attempt to store data with an SQLite database.

We’ll discuss each of these in turn, and we use data binding techniques for our sample code. Android provides complete support for SQLite databases. The recommended way of creating SQLite databases is to subclass the SQLiteOpenHelper class and override the onCreate() method. For this sample, we create a single table.

Источник

Android Data Binding: List Tricks

For Those Little Lists of Views

I was thinking about the previous article in which I wrote about using Android Data Binding with RecyclerView. What if you have a list of elements and don’t really need a RecyclerView to handle it? After all, if you’re only going to show three or four elements on the screen and they are never going to be recycled, there’s no need to bring out the big guns.

Often developers will loop through their entries and create Views manually:

That’s pretty easy. Wouldn’t it be nice if we could bind to a list in the XML? Something like this would be great:

Simple List Binding Adapter

I want to use a list of entries to create Views in a LinearLayout and bind those views to the values in the list. Every different layout has its own generated Binding class, so if I want to make a general Binding Adapter, I can’t just call the normal setter. I certainly don’t want to use reflection — it is costly. Instead, just like with RecyclerView, we can use convention to solve the problem.

We will use a convention of having only one variable and that variable is always named some consistent value. No matter what is in the list, the layout will have just one variable with a single name “data.” We can then use the ViewDataBinding.setVariable() method to bind the data in the layout.

And you’d bind it to your ViewGroup like this:

The above LinearLayout will automatically add children using the item.xml layout with the “data” variable set to the items in entries. This can be used for any ViewGroup in which addView() is enough to manage child Views.

Читайте также:  Record the call android

Dynamic Lists

The above Binding Adapter works great for static lists, but what if your list changes on the fly? Perhaps the user has added a new option and that item has to be added to the list of radio buttons. ObservableList gives us the ability to watch for changes and react to them. We use an OnListChangedCallback to observe the changes to the list:

There are a few things in the setEntries() Binding Adapter worth noting. First, I use data binding’s feature that lets me get the old values as well as new values. By providing twice as many data parameters as attributes, the first set of parameters receives the old values and the second set receives the new values. I use this to remove the listener from the old entries list.

Second, Android Data Binding normally watches for changes to a list and when a change occurs, it will reevaluate the expression. I want to manage the changes in the Binding Adapter, so it doesn’t do anything when no instance change occurs. I’m using ListenerUtil to track the EntryChangeListener, an OnListChangedCallback. ListenerUtil keeps track of the listener so that it can be retrieved between calls and I use it so that I can remove or modify the old listener and maybe add it to the new list. I need to provide an identifier to use as the key, so I’ve created one:

Third, setEntries() relies on EntryChangeListener to update the child Views when there is only data change. Otherwise, it will completely replace the child Views. For example, if the layout ID changes, we scrap the old children and just repopulate the whole thing.

Other than that, it is fairly simple. Here are the other methods that it uses:

Those do basically what was done in the original Binding Adapter. The resetViews() method first removes all the Views from the ViewGroup, then inflates Views and binds the data in the list.

You could have a simple EventChangeListener that just resets the views every time:

To be honest, that’s probably good enough for most use cases. You’re not supposed to use this with large numbers of Views — that’s what RecyclerView is for. However, if I want to animate Views when there is a change, I’ll want to handle the change events better. You might consider something like this for the change listener:

It just rebinds the data from the current View. Unfortunately, that might work if we have very intelligent transitions, but the default transition doesn’t know what to do when the data changes. Instead, we’ll have to actually replace the Views:

Now we see a nice fade-out and fade-in effect of the Views when they change. The rest of the implemented methods are fairly straight-forward using the TransitionManager to animate Views.

Considerations

You may be tempted to use the list binding technique instead of RecyclerView. Don’t. Data Binding lists is not a replacement for RecyclerView. Instead, use it to bind to a small set of Views that are all visible within your layout. A good rule of thumb is that if you have to scroll the list, use a RecyclerView. If you don’t, use data binding.

The example I used at first was only four lines of code and I’ve somehow made a Binding Adapter that is nearly 150 lines of code. But now that it’s written, I can use it anywhere in an application to populate my small data-driven UI lists. It will even animate values as it changes and I never have to worry about directly updating the View. And now you don’t have to worry about it, either.

You can see the code in the DataBoundList project here. In that project, users are added and removed from a list and it updates a LinearLayout dynamically. You’d never use this for a list of users because it could easily scroll. That means you should use RecyclerView, but this is just a demo. I hope you find this approach useful for binding lists to ViewGroups in your applications.

Источник

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

Введение

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

Читайте также:  Spider man android apk obb

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

Пока что это только бета релиз, но уже можно протестировать функционал и увидеть направление, в котором двигаются разработчики из 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.

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

Источник

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