7.7 KiB
Storage
Storage Principles
App-Specific Requirements
- Store files that are meant for our use only
- Store files that we intend to share with other apps
- e.g. taking a photo
- Store private, primitive data
- Config data, passwords, cookies & keys, etc.
- Store private, structured data
- text messages
Mobile Storage Principles
- Efficiency and sharing vs privacy and security
- Limited storage capacity
- Highly personal information stored
- Lots of different data types stored on phone
- Media
- Images, videos, music
Access to the mail should be protected since this is sensitive user data. However, if a reference to an image attachment is given to an image viewer, that image viewer will not have permission to open the attachment since it has no reason to hold a permission to access all e-mail.
Storage Directories
/system -> stores Android OS & libraries
/data -> where user data is stored (every application is a user)
/mnt/sdcard (symlinked as sdcard) -> External Storage
Basic Logical Data Storage on Android
Different Volumes
Internal
- Limited space for app-specific data
- Reliable (not removable)
- Soldered RAM
External
- Lots of data
- May (or may not) be physically removable
Different kinds of data
- Data that is only meaningful for our app should use app-specific storage
- Shareable media content should use shared storage so that other apps can access the content
- Also called scoped storage
File-based abstractions
- Shared preferences
- File-based storage
- SQLite database
- Structured data, small binary blobs
Internal File Storage
Internal data storage is private to the app
- Other apps (and the user) cannot access it
- Kernel-enforced user perms
- Removed on uninstall
- Data is stored in files
/data/data/com.example.project/files/- Full path not accessible;
files/becomes the chroot - This ensures we cannot traverse the directory upwards and gain access to other applications' files
Cache Files
/data/data/com.example.project/cache
getCacheDir() -> gets application’s file directory
Programmer should manage the files
- May be deleted when internal storage becomes full
- Will be deleted when application is uninstalled
- A well-behaved application will delete them when no longer in use
- Recommended to use less than 1MB
Shared Preferences
- Stored on a per-application basis
- e.g. saving config info (settings)
- Should not be used for data transfer (use intents and binders instead)
- Primitive data in key-value pairs
- Can have multiple preference files per application
<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<map>
<string name="colourscheme">light</string>
</map>
SharedPreferences settings = getSharedPreferences(CONFIG_STORAGE_NAME, 0);
SharedPreferences.Editor editor = settings.editor;
if (settings.getBoolean(MainActivity.CONFIG_THEME, false))
setTheme(...);
else
...
External Storage
Legacy name
- Every Android device provides externally accessible storage (SD card)
- Even phones without a physical SD card must have a logical representation of external storage
- Most phones will have one storage device partitioned into
internal / external - Phones must do this to conform to the Android API
- Most phones will have one storage device partitioned into
- Private Application files
- Internal storage on the external partition
getExternalFilesDir()/sdcard/Android/data/com.example.project/
- Public general files
- World reachable
- Other apps can read and modify these files
- Each user has their own virtual SD card
- Even phones without a physical SD card must have a logical representation of external storage
- Can be mounted externally (and unmounted/disconnected)
Getting Access to External Storage
- Should check state with
Enviroment.getExternalStorageState()- It is a separate file system, potentially removable
Environment.MEDIA_MOUNTEDEnvironment.MEDIA_MOUNTED_READ_ONLY
- It is a separate file system, potentially removable
- Use
getExternalStoragePublicDirectory (String type)to obtain file for the directory- Media is stored by type
- Music in the music dir, etc.
- Pass a type to obtain the subdirectory for that type
- Media is stored by type
Getting Scoped storage access
- To access files that other apps have created
- We must have
READ_EXTERNAL_STORAGEpermission (in the manifest)- With this one permission, the app can do whatever (look at downloads, listen to music, look at pictures, etc.)
- This is where scoped storage comes into play
- The files must reside in the media collection (images/music/videos, etc.)
- or request legacy storage
MANAGE_EXTERNAL_STORAGE(before Android 11)
- We must have
- Apps only have access to
- App-specific directory on external storage
- Specific types of media that the app has created and contributed to well-defined collections
- If the app takes a picture, it is allowed to store and look at that picture but not allowed to access pictures not taken by that app
- You don't need to request perms to read/write images that we are writing to the MediaStorage
Android Databases
When the data stored is logically structured, queries are needed to find it based on the structure.
- Android provides local database support
- Can run full SQL queries
- Each app’s databases are local to it
Database.dbstored in internal storage
- Powered by SQLite
Android and SQLite
- Wrapped in two main classes
- Database represented by
SQLiteDatabaseobject- Allows us to run SQL queries on the database
SQLiteOpenHelper- Supports the application lifecycle
SQLiteOpenHelper->onCreate()- Creates the database the first time it is called
- This database will exist until the application is installed
SQLiteOpenHelper->onUpdate(int oldVer, int newVer)- Changing the version number allows the database to be dropped and recreated
- Database represented by
- Create an instance of our
SQLiteOpenHelpersubclass - Get reference to
SQLiteDatabaseusinggetReadableDatabase()getWriteableDatabase()
Querying a Database
void execSQL() -> used to execute SQL queries that don't return anything
query() and rawQuery()
- These return a Cursor object pointing to the results
Cursor rawQuery (String sql, String[] selectionArgs)
Cursors
- Provide random access to results of a query
- Enable us to enumerate the rows returned by the query
moveToFirst(),moveToNext()getString(colIndex),getInt(colIndex)
- Have a
close()method to close the query when finished
We can pass cursors to other applications
CursorLoader
- A query may last some time
- Database may be large
- May be in a different process
- Don’t block the main thread
CursorLoader- Populates views asynchronously
- Auto-updating
(deprecated)
Data-Driven Views
- Connect a cursor to a
CursorAdapterand aListView- Think mapping rows of a database to which entry in the list
- Each row must have an ID field
RecyclerView- An optimised, flexible version of the above
- Only creates views for visible data
- As the user scrolls, more data is added
- We can define our own adaptor
Database Abstraction
Abstraction of database architecture
- Easier to update storage code
- Expose column indices as static class variables
c.getInt(0)✗c.getInt(DBHelper.NAME)✓
- Helper methods keep database internals from leaking into other classes
- Return a collection of results rather than the cursor
- Sanitise user inputs
- SQL injections still apply
