241 lines
7.7 KiB
Markdown
241 lines
7.7 KiB
Markdown
# 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
|
||
<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
|
||
<map>
|
||
<string name="colourscheme">light</string>
|
||
</map>
|
||
```
|
||
|
||
```java
|
||
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
|
||
- *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
|
||
- 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_MOUNTED`
|
||
- `Environment.MEDIA_MOUNTED_READ_ONLY`
|
||
- 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
|
||
|
||
##### Getting Scoped storage access
|
||
|
||
- To access files that other apps have created
|
||
- We must have `READ_EXTERNAL_STORAGE` permission (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)
|
||
- 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.db` stored in internal storage
|
||
- Powered by SQLite
|
||
|
||
#### Android and SQLite
|
||
|
||
- Wrapped in two main classes
|
||
- Database represented by `SQLiteDatabase` object
|
||
- 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
|
||
- Create an instance of our `SQLiteOpenHelper` subclass
|
||
- Get reference to `SQLiteDatabase` using
|
||
- `getReadableDatabase()`
|
||
- `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 `CursorAdapter` and a `ListView`
|
||
- 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
|