基于MVP模式多视图共享Repository持久化数据的实现方案
Great question! The core idea you've outlined in your Repository class is absolutely valid for reusing data across multiple presenters—you just need to tweak the implementation to ensure the data persists correctly across View1 and View2's lifecycles. Let's break this down step by step.
Is your initial Repository code feasible?
Yes, but with a critical adjustment. Your current code defines a private list and a getData() method to populate it, but creating a new Repository instance for each presenter would mean each gets a fresh, empty list. To share data between Presenter1 and Presenter2, your Repository needs to be a single instance (singleton) so both presenters work with the same underlying data store.
Here's a refined, thread-safe singleton version of your Repository for in-memory caching:
class Repository private constructor() { // Use thread-safe collections if modifying from background threads private val list = mutableListOf<YourDataType>() @Volatile private var isDataLoaded = false // Singleton instance (thread-safe lazy initialization) companion object { val instance: Repository by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) { Repository() } } suspend fun getData(): List<YourDataType> { if (!isDataLoaded) { // Replace with your actual data fetch logic (network/local) val fetchedData = fetchDataFromSource() list.clear() list.addAll(fetchedData) isDataLoaded = true } // Return an immutable copy to prevent external modification of internal state return list.toList() } private suspend fun fetchDataFromSource(): List<YourDataType> { // Simulate network delay/data fetch delay(1000) return listOf(YourDataType("Item 1"), YourDataType("Item 2")) } } // Example data class for your content data class YourDataType(val name: String)
How to keep the list persistent across View1 and View2 lifecycles?
The solution depends on whether you need data to survive only while the app is running, or persist through app restarts.
1. In-memory persistence (app session only)
If you only need data to stay available as long as the app is active (no need to survive force closes or restarts), the singleton approach above is sufficient:
- Both Presenter1 and Presenter2 will access the same
Repository.instance, so once View1 loads the data into thelist, Presenter2 can immediately retrieve it without re-fetching. - Thread safety reminder: If
getData()uses asynchronous operations (like network calls), use thread-safe collections (e.g.,CopyOnWriteArrayList) or synchronize access to thelistto avoid race conditions. The@Volatileflag onisDataLoadedensures all threads see the latest state of this flag.
2. Persistent local storage (survives app restarts)
For data that needs to persist even if the app is killed, extend the Repository to include local caching. Popular options are Room (for structured data), SharedPreferences (for small key-value sets), or file storage. Here's how to integrate Room:
First, define your Room entities and DAO:
// Room Entity @Entity data class YourDataType( @PrimaryKey val id: String, val name: String ) // Room DAO @Dao interface YourDataDao { @Query("SELECT * FROM YourDataType") suspend fun getAll(): List<YourDataType> @Insert(onConflict = OnConflictStrategy.REPLACE) suspend fun insertAll(items: List<YourDataType>) } // Room Database @Database(entities = [YourDataType::class], version = 1) abstract class AppDatabase : RoomDatabase() { abstract fun yourDataDao(): YourDataDao companion object { // Singleton database instance val instance: AppDatabase by lazy { Room.databaseBuilder( context, // Pass your app context here AppDatabase::class.java, "app_database" ).build() } } }
Then update the Repository to use both in-memory and local caching:
class Repository private constructor( private val yourDataDao: YourDataDao ) { private val list = mutableListOf<YourDataType>() @Volatile private var isDataLoaded = false companion object { val instance: Repository by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) { Repository(AppDatabase.instance.yourDataDao()) } } suspend fun getData(): List<YourDataType> { if (!isDataLoaded) { // First check local storage for cached data val localData = yourDataDao.getAll() if (localData.isNotEmpty()) { list.clear() list.addAll(localData) isDataLoaded = true return list.toList() } // No local data? Fetch from network and cache locally val fetchedData = fetchDataFromSource() list.clear() list.addAll(fetchedData) yourDataDao.insertAll(fetchedData) isDataLoaded = true } return list.toList() } private suspend fun fetchDataFromSource(): List<YourDataType> { // Replace with actual network request logic delay(1000) return listOf(YourDataType("1", "Item 1"), YourDataType("2", "Item 2")) } }
With this setup:
- View1 triggers
getData(): the Repository checks local storage first, fetches from the network if needed, then saves data to both memory and local storage. - View2's Presenter calls
getData(): it gets the in-memory list immediately (if already loaded by View1), or falls back to local storage if the app was restarted.
Key Takeaways
- Singleton Repository: The foundation for sharing data across presenters—ensures all components use the same data source.
- Caching Strategy: Choose in-memory only for temporary data, or combined in-memory + local storage for persistent data.
- Thread Safety: Always handle asynchronous operations carefully to avoid race conditions in data access.
内容的提问来源于stack exchange,提问作者MaaAn13

