Mobile Application DevelopmentUnit 615 min read
Data Storage in Mobile Apps: Preferences, Files & SQLite
Unit 6 of Mobile Application Development explores three essential data storage methods in Android—SharedPreferences for simple key-value pairs, Files for structured data, and SQLite for relational databases—with step-by-step implementation, comparisons, and real-world use cases like eSewa’s transaction logs and Daraz’s
TAKEAWAYS:
- SharedPreferences stores primitive data (strings, booleans) as key-value pairs using XML files, ideal for app settings but limited to small, non-relational data.
- File storage (internal/external) handles larger, structured data (JSON, CSV) but requires manual serialization and lacks built-in querying.
- SQLite is a lightweight relational database embedded in Android, perfect for complex queries (e.g., user profiles, transaction histories) with ACID compliance.
- Internal storage is private to the app; external storage (SD card) requires permissions and user consent, risking data loss if unmanaged.
- ContentProviders enable secure data sharing between apps (e.g., Contacts app) via URIs, adhering to Android’s security model.
- Best practices: Use SharedPreferences for configs, SQLite for structured data, and encrypt sensitive files (e.g., bank apps like Ncell’s transaction records).
1. SharedPreferences: Simple Key-Value Storage
SharedPreferences is Android’s built-in mechanism for storing primitive data types (strings, integers, booleans, floats) in XML format. It’s lightweight and ideal for app settings, login states, or user preferences.
How It Works
- Data is stored as key-value pairs in a private XML file (e.g.,
app_preferences.xml). - No need for a database schema; keys are strings, values are primitives.
- Methods:
getSharedPreferences(): Retrieves a SharedPreferences object.edit(): Opens an editor to modify values.putString(),putInt(), etc.: Add/update values.commit()orapply(): Saves changes (commit is synchronous; apply is asynchronous).
Example: Storing User Login State
// Save login state
SharedPreferences prefs = getSharedPreferences("UserPrefs", MODE_PRIVATE);
SharedPreferences.Editor editor = prefs.edit();
editor.putBoolean("isLoggedIn", true);
editor.putString("username", "user123");
editor.apply();
// Retrieve login state
boolean loggedIn = prefs.getBoolean("isLoggedIn", false);
String username = prefs.getString("username", "guest");
Visual: SharedPreferences File Structure
Advantages and Limitations
| Advantages | Limitations |
|---|---|
| Simple API, no setup required | Limited to primitive data types |
| Persists across app restarts | No querying or complex operations |
| Lightweight, fast access | Not suitable for large datasets |
2. File Storage: Internal vs. External
Android supports two types of file storage:
- Internal Storage: Private to the app, no permissions needed.
- External Storage: Public (e.g., SD card), requires
READ_EXTERNAL_STORAGE/WRITE_EXTERNAL_STORAGEpermissions.
How It Works
- Internal Storage:
- Files are stored in
/data/data/<package_name>/files/. - Use
openFileOutput()to write andopenFileInput()to read.
- Files are stored in
- External Storage:
- Files are stored in
/storage/emulated/0/Android/data/<package_name>/files/or public directories likeEnvironment.getExternalStoragePublicDirectory(). - Requires runtime permissions (Android 6.0+).
- Files are stored in
Example: Saving a JSON File Internally
// Save JSON to internal storage
String json = "{\"name\":\"John\", \"age\":30}";
FileOutputStream fos = openFileOutput("user_data.json", Context.MODE_PRIVATE);
fos.write(json.getBytes());
fos.close();
// Read JSON
FileInputStream fis = openFileInput("user_data.json");
byte[] buffer = new byte[fis.available()];
fis.read(buffer);
String fileContent = new String(buffer);
fis.close();
Visual: File Storage Paths
Real-World Example: Daraz Order History
Daraz stores order details (JSON/XML) in internal storage for quick access during the user’s session. For backup, it uses external storage (with user consent) to sync orders to the cloud.
3. SQLite: Relational Database for Mobile Apps
SQLite is a serverless, file-based database included in Android. It supports SQL queries, transactions, and is ideal for structured data like user profiles, transaction logs, or app content.
erDiagram
users ||--o{ orders : places
users {
int id PK
string name
string email
}
orders {
int id PK
int user_id FK
string order_details
date timestamp
}SQLite ER diagram for a user-order system (like Daraz)How It Works
- Database Helper Class: Extend
SQLiteOpenHelperto create/upgrade the database. - SQL Queries: Use
sqlite3commands (CREATE TABLE, INSERT, SELECT) viaSQLiteDatabase. - CRUD Operations: Perform Create, Read, Update, Delete operations.
Example: User Profile Database
// Database Helper
public class UserDBHelper extends SQLiteOpenHelper {
public static final String DATABASE_NAME = "UserDatabase.db";
public static final String TABLE_NAME = "users";
public static final String COL_ID = "id";
public static final String COL_NAME = "name";
public static final String COL_EMAIL = "email";
public UserDBHelper(Context context) {
super(context, DATABASE_NAME, null, 1);
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("CREATE TABLE " + TABLE_NAME +
" (" + COL_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " +
COL_NAME + " TEXT, " + COL_EMAIL + " TEXT)");
}
}
// Insert a user
UserDBHelper helper = new UserDBHelper(this);
SQLiteDatabase db = helper.getWritableDatabase();
ContentValues values = new ContentValues();
values.put(UserDBHelper.COL_NAME, "Alice");
values.put(UserDBHelper.COL_EMAIL, "alice@example.com");
db.insert(UserDBHelper.TABLE_NAME, null, values);
// Query users
Cursor cursor = db.query(UserDBHelper.TABLE_NAME,
null, null, null, null, null, null);
while (cursor.moveToNext()) {
String name = cursor.getString(cursor.getColumnIndex(UserDBHelper.COL_NAME));
Log.d("User", name);
}
Visual: SQLite Table Creation and Query
Visual: SQLite Query Execution Steps
flowchart TD
A["Start"] --> B["Open Database"]
B --> C["Prepare Query: 'SELECT * FROM users'"]
C --> D["Execute Query"]
D --> E["Cursor: Move to First Row"]
E --> F{"Has Next Row?"}
F -->|"Yes"| G["Read Data"]
G --> E
F -->|"No"| H["Close Cursor"]
H --> I["End"]Advantages and Limitations
| Advantages | Limitations |
|---|---|
| Full SQL support | No built-in encryption (use SQLCipher) |
| ACID transactions | Slower than SharedPreferences for small data |
| Scalable for complex queries | Requires more boilerplate code |
4. ContentProviders: Secure Data Sharing
ContentProviders enable apps to share data securely via URIs. They are essential for inter-app communication (e.g., Contacts app).
How It Works
- Define a
ContentProviderclass extendingContentProvider. - Implement methods like
query(),insert(),update(),delete(). - Use
ContentResolverto interact with the provider.
Example: Sharing Contacts
// Define a ContentProvider for contacts
public class ContactProvider extends ContentProvider {
private static final String CONTENT_URI = "content://com.example.contacts/contacts";
private SQLiteDatabase db;
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
db = new UserDBHelper(getContext()).getReadableDatabase();
return db.query("contacts", projection, selection, selectionArgs,
null, null, sortOrder);
}
// Implement other required methods...
}
// Query contacts from another app
Cursor cursor = getContentResolver().query(
Uri.parse("content://com.example.contacts/contacts"),
null, null, null, null);
Visual: ContentProvider Architecture
In the Real World
eSewa (Nepal):
- Uses SQLite to store transaction logs (user ID, amount, timestamp) for offline processing. When online, it syncs with the server via ContentProvider.
- Why SQLite? Needs fast local queries for receipt generation and fraud detection.
Khalti (Nepal):
- Stores user payment preferences (e.g., default bank account) in SharedPreferences for quick access during checkout.
- Why SharedPreferences? Small, frequently accessed data with no complex relationships.
Daraz (Nepal):
- Uses internal file storage (JSON) to cache product listings for offline browsing. Large images are stored in external storage (with user permission).
- Why Files? Simpler than SQLite for unstructured data like product catalogs.
Ncell (Nepal):
- Employs SQLite with encryption (SQLCipher) to secure customer call logs and usage data. Uses ContentProvider to share data with billing apps.
- Why Encryption? Protects sensitive telecom data under GDPR-like regulations.
NEPSE (Nepal Stock Exchange):
- Mobile apps (e.g., NEPSE’s official app) use SQLite to cache stock prices and portfolios. Offline charts are rendered from local JSON files.
- Why Hybrid Storage? Balances query speed (SQLite) and large dataset handling (files).
Exam Tip
SharedPreferences:
- Remember to use
apply()for async saves andcommit()for sync (thoughapply()is preferred). - Common Exam Question: "How would you store a user’s last login time?" → Use
putLong()withSystem.currentTimeMillis().
- Remember to use
File Storage:
- Permissions: Always check
Environment.getExternalStorageState()before writing to external storage. - Exam Pitfall: Forgetting to handle
FileNotFoundExceptionorIOExceptionin file operations.
- Permissions: Always check
SQLite:
- Schema Design: Know how to define PRIMARY KEY, FOREIGN KEY, and constraints.
- Querying: Practice
JOINoperations (e.g., linking users to orders). - Exam Question: "Write SQL to fetch all orders > Rs. 5000 for a user." →
SELECT * FROM orders WHERE user_id = ? AND amount > 5000;
ContentProviders:
- Understand the URI format:
content://<authority>/<path>. - Exam Tip: Always use
ContentResolverto interact with providers, not direct database access.
- Understand the URI format:
Comparisons:
- Exam Question: "When would you use SharedPreferences vs. SQLite?"
Use Case SharedPreferences SQLite App settings ✅ Yes ❌ No User profiles ❌ No ✅ Yes Large datasets ❌ No ✅ Yes Simple key-value pairs ✅ Yes ❌ Overkill
- Exam Question: "When would you use SharedPreferences vs. SQLite?"
Worked Example: Bank Loan Calculator App
Scenario: A bank app (like NMB Bank) needs to store loan details (principal, interest rate, tenure) and calculate monthly payments. Which storage method to use?
Solution
- SharedPreferences: Store user preferences (e.g.,
showInterestBreakdown = true). - SQLite: Store loan history (principal, rate, tenure, EMI) for querying.
- Internal Files: Cache EMI calculation results (JSON) for quick access.
SQLite Table Design
CREATE TABLE loans (
loan_id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER,
principal REAL,
rate REAL, -- Annual interest rate (e.g., 8.5)
tenure INTEGER, -- In months
emi REAL,
FOREIGN KEY (user_id) REFERENCES users(id)
);
EMI Calculation Query
// Calculate EMI using SQLite
String query = "SELECT principal, rate, tenure FROM loans WHERE loan_id = ?";
Cursor cursor = db.rawQuery(query, new String[]{String.valueOf(loanId)});
if (cursor.moveToFirst()) {
double principal = cursor.getDouble(0);
double rate = cursor.getDouble(1) / 12 / 100; // Monthly rate
int tenure = cursor.getInt(2);
double emi = (principal * rate * Math.pow(1 + rate, tenure)) /
(Math.pow(1 + rate, tenure) - 1);
// Update EMI in database
ContentValues values = new ContentValues();
values.put("emi", emi);
db.update("loans", values, "loan_id = ?", new String[]{String.valueOf(loanId)});
}
Visual: EMI Calculation Flow
flowchart TD
A["Start"] --> B["Fetch Loan Data from SQLite"]
B --> C["Calculate EMI: P * r * (1 + r)^n / ((1 + r)^n - 1)"]
C --> D["Update EMI in SQLite"]
D --> E["Display EMI to User"]
E --> F["End"]Summary Table: Storage Methods Comparison
| Method | Data Type | Use Case | Performance | Security |
|---|---|---|---|---|
| SharedPreferences | Primitives (String, int, etc.) | App settings, flags | ⚡ Very Fast | Low (XML file) |
| Internal Files | Any (JSON, CSV, binary) | Large structured data | ⚡ Fast | Medium (encrypt manually) |
| External Files | Any | Backups, media | 🐢 Slow (I/O) | Low (permissions required) |
| SQLite | Structured (tables) | User data, transactions | 🐢 Moderate | Medium (encrypt with SQLCipher) |
| ContentProvider | Any (via URI) | Inter-app data sharing | 🐢 Moderate | High (Android security) |
Final Checklist for Exams
SharedPreferences:
- Know the methods:
getSharedPreferences(),edit(),putXxx(),apply(). - Remember: No complex queries; only key-value pairs.
- Know the methods:
File Storage:
- Internal vs. external paths.
- Permissions:
WRITE_EXTERNAL_STORAGE(deprecated in Android 11+; useMANAGE_EXTERNAL_STORAGEcarefully).
SQLite:
- DatabaseHelper lifecycle (
onCreate(),onUpgrade()). - CRUD operations with
ContentValuesandCursor. - Joins and aggregations (e.g.,
GROUP BY,HAVING).
- DatabaseHelper lifecycle (
ContentProviders:
- URI format and authority.
ContentResolverusage.
Real-World Mapping:
- eSewa → SQLite for transactions.
- Daraz → Files for product cache.
- Ncell → Encrypted SQLite for call logs.
In the real world
- Ncell’s Transaction Records: Uses SQLite to store structured transaction logs (user_id, amount, timestamp) for quick querying and reporting. The database ensures ACID compliance for critical financial data.
- Daraz Order History: Stores order details (JSON/XML) in internal storage for offline access, while syncing to external storage (cloud) for backup with user consent.
- Khalti Payment App: Uses SharedPreferences to cache user login tokens (e.g.,
isLoggedIn,userToken) for seamless session management, reducing server calls.
Based on the TU BIM syllabus for Mobile Application Development (IT272), unit 6.
Discussion
Loading…