- How to store data locally in an Android app
- Ways to store data
- Using Shared Preferences
- Using internal storage
- External Storage
- SQLite database
- Android From Scratch: How to Store Application Data Locally
- 1. Storing Key-Value Pairs
- 2. Using an SQLite Database
- 3. Using the Internal Storage
- 4. Using the External Storage
- Conclusion
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 From Scratch: How to Store Application Data Locally
When it comes to persisting application data locally, Android developers are definitely spoiled for choice. In addition to direct access to both the internal and external storage areas of an Android device, the Android platform offers SQLite databases for storing relational data, and special files for storing key-value pairs. What’s more, Android apps can also use third-party databases that offer NoSQL support.
In this tutorial, I’ll show you how to make use of all those storage options in your Android apps. I’ll also help you understand how to pick the most appropriate storage option for your data.
Do you find it easier to learn with video? Why not check out our course:
1. Storing Key-Value Pairs
If you are looking for a quick way to store a few strings or numbers, you should consider using a preferences file. Android activities and services can use the getDefaultSharedPreferences() method of the PreferenceManager class to get a reference to a SharedPreferences object that can be used to both read from and write to the default preferences file.
To start writing to the preferences file, you must call the edit() method of the SharedPreferences object, which returns a SharedPreferences.Editor object.
The SharedPreferences.Editor object has several intuitive methods you can use to store new key-value pairs to the preferences file. For example, you could use the putString() method to put a key-value pair whose value is of type String . Similarly, you could use the putFloat() method to put a key-value pair whose value is of type float . The following code snippet creates three key-value pairs:
Once you’ve added all the pairs, you must call the commit() method of the SharedPreferences.Editor object to make them persist.
Reading from a SharedPreferences object is a lot easier. All you need to do is call the appropriate get*() method. For example, to get a key-value pair whose value is of type String , you must call the getString() method. Here’s a code snippet that retrieves all the values we added earlier:
As you can see in the above code, as the second parameter, all the get*() methods expect a default value, which is the value that must be returned if the key is not present in the preferences file.
Note that preferences files are limited to strings and primitive data types only. If you wish to store more complex data types or binary data, you must choose a different storage option.
2. Using an SQLite Database
Every Android app can create and make use of SQLite databases to store large amounts of structured data. As you might already know, SQLite is not only light-weight, but also very fast. If you have experience working with relational database management systems and are familiar with both SQL, which is short for Structured Query Language, and JDBC, which is short for Java Database Connectivity, this might be your preferred storage option.
To create a new SQLite database, or to open one that already exists, you can use the openOrCreateDatabase() method inside your activity or service. As its arguments, you must pass the name of your database and the mode in which you want to open it. The most used mode is MODE_PRIVATE , which makes sure that the database is accessible only to your application. For example, here’s how you would open or create a database called my.db:
Once the database has been created, you can use the execSQL() method to run SQL statements on it. The following code shows you how to use the CREATE TABLE SQL statement to create a table called user, which has three columns:
Although it’s possible to insert new rows into the table using the execSQL() method, it’s better to use the insert() method instead. The insert() method expects a ContentValues object containing the values for each column of the table. A ContentValues object is very similar to a Map object and contains key-value pairs.
Here are two ContentValues objects you can use with the user table:
As you might have guessed, the keys you pass to the put() method must match the names of the columns in the table.
Once your ContentValues objects are ready, you can pass them to the insert() method along with the name of the table.
To query the database, you can use the rawQuery() method, which returns a Cursor object containing the results of the query.
A Cursor object can contain zero or more rows. The easiest way to loop through all its rows is to call its moveToNext() method inside a while loop.
To fetch the value of an individual column, you must use methods such as getString() and getInt() , which expect the index of the column. For example, here’s how you would retrieve all the values you inserted in the user table:
Once you have fetched all the results of your query, make sure that you call the close() method of the Cursor object in order to release all the resources it holds.
Similarly, when you have finished all your database operations, don’t forget to call the close() method of the SQLiteDatabase object.
3. Using the Internal Storage
Every Android app has a private internal storage directory associated with it, in which the app can store text and binary files. Files inside this directory are not accessible to the user or to other apps installed on the user’s device. They are also automatically removed when the user uninstalls the app.
Before you can use the internal storage directory, you must determine its location. In order to do so, you can call the getFilesDir() method, which is available in both activities and services.
To get a reference to a file inside the directory, you can pass the name of the file along with the location you determined. For example, here’s how you would get a reference to a file called alice.csv:
From this point on, you can use your knowledge of Java I/O classes and methods to read from or write to the file. The following code snippet shows you how to use a FileOutputStream object and its write() method to write to the file:
4. Using the External Storage
Because the internal storage capacity of Android devices is usually fixed, and often quite limited, several Android devices support external storage media such as removable micro-SD cards. I recommend that you use this storage option for large files, such as photos and videos.
Unlike internal storage, external storage might not always be available. Therefore, you must always check if it’s mounted before using it. To do so, use the getExternalStorageState() method of the Environment class.
Once you are sure that the external storage is available, you can get the path of the external storage directory for your app by calling the getExternalFilesDir() method and passing null as an argument to it. You can then use the path to reference files inside the directory. For example, here’s how you would reference a file called bob.jpg in your app’s external storage directory:
By asking the user to grant you the WRITE_EXTERNAL_STORAGE permission, you can gain read/write access to the entire file system on the external storage. You can then use well-known public directories to store your photos, movies, and other media files. The Environment class offers a method called getExternalStoragePublicDirectory() to determine the paths of those public directories.
For example, by passing the value Environment.DIRECTORY_PICTURES to the method, you can determine the path of the public directory in which you can store photos. Similarly, if you pass the value Environment.DIRECTORY_MOVIES to the method, you get the path of the public directory in which movies can be stored.
Here’s how you would reference a file called bob.jpg in the public pictures directory:
Once you have the File object, you can again use the FileInputStream and FileOutputStream classes to read from or write to it.
Conclusion
You now know how to make the most of the local storage options provided by the Android SDK. Regardless of the storage option you choose, read/write operations can be time-consuming if large amounts of data are involved. Therefore, to make sure that the main UI thread always stays responsive, you must consider running the operations in a different thread.
To learn more about saving application data locally, refer to the official data storage API guide.
Источник