Skip to content

Local Persistent Storage Methods

Local persistent storage is a critical topic, especially in overseas app development. This is largely due to stricter internet laws abroad, which often prohibit storing certain types of data on server databases. In these cases, local storage becomes essential.

In domestic apps, local storage is also commonly used—for example, as a cache layer to store the latest fetched data. If a network request fails, the app can show cached data to the user. This approach is valid for data that doesn’t change often, though product managers should ultimately decide if this is reasonable from a product perspective.

March 2025 update: Some teams call this a "fallback solution." For instance, when implementing an H5 subscription page, they also set up an offline backup mechanism under this name.


File Storage

This method is convenient: you can serialize Entity objects into strings or files (txt, json, csv) and store them locally. On read, deserialize them back into Entity objects and use them as a Service data source in MVVM.

Considerations:

  • Reliability: Custom file storage code should ideally be developed using TDD. Testing manually is less reliable than writing tests first.
  • Upgrades: File schema changes might throw exceptions. Solutions include using RxJava onError or try-catch in Kotlin Coroutines.
  • Code repetition: Encapsulation is key to minimize duplication.
  • Major business use: Avoid using file storage for critical business logic, since schema changes can break your code. Official databases like SQLite or frameworks like Room are safer.

Relational Database Storage

On Android, the only official relational database is SQLite, which is ideal for client-side storage.

Access methods:

  • SQLiteOpenHelper: Directly manage SQLite, but results in repetitive code.
  • Third-party frameworks: GreenDao, LitePal, etc. These simplify operations but require ongoing maintenance. Many frameworks stop updating after a year or two and may not support new Android features (e.g., LitePal does not support RxJava or Kotlin Coroutines).

Jetpack Room:

  • Google introduced Room to solve these issues.
  • Supports RxJava and Kotlin Coroutines for thread-safe database operations.
  • Implements ORM via Database, Dao, and Entity classes.
  • In MVVM, Room can serve as the Service layer.

Non-relational Database Storage

Realm (Frozen Model / Freeze):

  • Developed by the MongoDB team, Realm is a mobile-first, non-relational database.
  • Unlike Room, GreenDao, or SQLite-based ORMs, Realm does not rely on strict relational structure; it stores JSON-like objects directly.
  • This may be unfamiliar to engineers used to relational databases (similar to Java SpringBoot developers facing Node.js), but Realm is an excellent choice for certain use cases.

Key-Value Storage

Key-value storage is similar to Redis in backend development: lightweight storage using key-value mapping.

Popular Android solutions:

  • SharedPreferences (SP): Built-in Android solution. Stores key-value pairs in an XML file. Simple to use, but CRUD operations are IO operations, so they should ideally not run on the UI thread. Many developers skip this because SP is very fast, but proper threading with Kotlin Coroutines or RxJava is more correct.

  • MMKV: Developed by Tencent (WeChat team) as a high-performance key-value storage solution. Faster than SP, supports memory-mapped files, and is suitable for large-scale key-value storage.

  • DataStore: Google's modern replacement for SharedPreferences, supports asynchronous and type-safe access, integrates well with Kotlin Coroutines and Flow, and avoids SP’s threading issues.

Just something casual. Hope you like it. Built with VitePress