Android个人财务记账APP:按时间管理用户交易数据的实现咨询
Hey there! As someone who’s helped tons of new Android devs build similar apps, let’s walk through your question step by step.
先聊核心问题:该不该用SharedPreferences?
Short answer: SharedPreferences is not ideal for your use case, but it can work if you’re only testing with a handful of simple records. Here’s the breakdown:
- SharedPreferences is designed for small, key-value pairs (like user settings, theme preferences) — it’s not built to handle structured, multi-entry data like transaction records.
- As your app grows (users add more transactions), storing and querying data with SharedPreferences will get messy fast: you’ll have to handle serialization/deserialization manually, and sorting/filtering by time will become inefficient.
For a personal finance app, Room Database is the way to go. It’s Google’s official SQLite wrapper for Android, built to handle structured data, supports easy querying/sorting, and scales well as your app grows. But if you still want to try SharedPreferences for learning purposes, I’ll cover that too.
如果一定要用SharedPreferences,怎么实现?
You’ll need to serialize your transaction data into a format that SharedPreferences can store (like JSON), then deserialize it when you need to work with the data. Here’s a step-by-step example:
1. Define a Transaction data class
First, create a class to represent each transaction:
data class Transaction( val id: String, val amount: Double, val category: String, val timestamp: Long, // Store time as Unix timestamp for easy sorting val note: String? = null )
2. Use a JSON library (like Gson) for serialization
Add Gson to your app’s build.gradle (Module level):
dependencies { implementation 'com.google.code.gson:gson:2.10.1' }
3. Write helper methods to save/load transactions
Create a utility class to handle SharedPreferences operations:
class TransactionPrefsHelper(context: Context) { private val prefs = context.getSharedPreferences("transactions", Context.MODE_PRIVATE) private val gson = Gson() // Save list of transactions fun saveTransactions(transactions: List<Transaction>) { val json = gson.toJson(transactions) prefs.edit().putString("transaction_list", json).apply() } // Load all transactions fun loadTransactions(): List<Transaction> { val json = prefs.getString("transaction_list", null) ?: return emptyList() val type = object : TypeToken<List<Transaction>>() {}.type return gson.fromJson(json, type) } }
4. Implement sorting and time-based filtering
Once you load the transactions, you can sort them and filter by time:
// Get all transactions sorted by time (newest first) val allTransactions = prefsHelper.loadTransactions() val sortedTransactions = allTransactions.sortedByDescending { it.timestamp } // Filter transactions between two timestamps (e.g., start of day to end of day) val startTime = // Unix timestamp for start of target period val endTime = // Unix timestamp for end of target period val filteredTransactions = sortedTransactions.filter { it.timestamp in startTime..endTime }
推荐方案:用Room Database实现(更适合长期开发)
Room is built exactly for scenarios like this. Here’s how to set it up:
1. Add Room dependencies to build.gradle
dependencies { implementation "androidx.room:room-runtime:2.5.2" kapt "androidx.room:room-compiler:2.5.2" // For Kotlin coroutines support (optional but recommended) implementation "androidx.room:room-ktx:2.5.2" }
2. Define the Transaction Entity
This class represents a table in the database:
@Entity(tableName = "transactions") data class Transaction( @PrimaryKey(autoGenerate = true) val id: Int = 0, val amount: Double, val category: String, val timestamp: Long, val note: String? = null )
3. Create a DAO (Data Access Object)
This defines all the database operations you need:
@Dao interface TransactionDao { // Insert a new transaction @Insert suspend fun insertTransaction(transaction: Transaction) // Get all transactions sorted by timestamp (newest first) @Query("SELECT * FROM transactions ORDER BY timestamp DESC") suspend fun getAllTransactionsSorted(): List<Transaction> // Get transactions between two timestamps @Query("SELECT * FROM transactions WHERE timestamp BETWEEN :startTime AND :endTime ORDER BY timestamp DESC") suspend fun getTransactionsInTimeRange(startTime: Long, endTime: Long): List<Transaction> }
4. Create the Room Database class
@Database(entities = [Transaction::class], version = 1) abstract class AppDatabase : RoomDatabase() { abstract fun transactionDao(): TransactionDao companion object { // Singleton instance to avoid multiple database connections @Volatile private var INSTANCE: AppDatabase? = null fun getDatabase(context: Context): AppDatabase { return INSTANCE ?: synchronized(this) { val instance = Room.databaseBuilder( context.applicationContext, AppDatabase::class.java, "app_database" ).build() INSTANCE = instance instance } } } }
5. Use Room in your app
You can use coroutines to perform database operations (since they can’t run on the main thread):
// Get database instance val db = AppDatabase.getDatabase(context) val transactionDao = db.transactionDao() // Insert a transaction CoroutineScope(Dispatchers.IO).launch { val newTransaction = Transaction( amount = 50.0, category = "Food", timestamp = System.currentTimeMillis(), note = "Lunch at café" ) transactionDao.insertTransaction(newTransaction) } // Get all sorted transactions CoroutineScope(Dispatchers.IO).launch { val sortedTransactions = transactionDao.getAllTransactionsSorted() // Update UI with data (switch to main thread) withContext(Dispatchers.Main) { // Update your RecyclerView or list here } } // Get transactions in a time range CoroutineScope(Dispatchers.IO).launch { val startTime = // Unix timestamp for start of period val endTime = // Unix timestamp for end of period val filteredTransactions = transactionDao.getTransactionsInTimeRange(startTime, endTime) // Update UI withContext(Dispatchers.Main) { // Handle filtered data } }
Final Notes
- Room will handle all the heavy lifting of sorting and querying, so you don’t have to write manual sorting logic like you would with SharedPreferences.
- As your app grows, you can easily add more features (like categories, reports) with Room, which would be much harder with SharedPreferences.
内容的提问来源于stack exchange,提问作者Mehran

