data layer mastery
3
总安装量
0
周安装量
#59880
全站排名
安装命令
npx skills add https://github.com/fwrite0920/android-skills --skill 'Data Layer Mastery'
Skill 文档
Data Layer Mastery (æ°æ®å±ä¸ç²¾)
Instructions
- ç¡®è®¤éæ±å±äºæ°æ®å±ï¼Roomãç½ç»ãè±æºçç¥ï¼
- ä¾ç §ä¸æ¹ç« è顺åºå¥ç¨
- 䏿¬¡åªè°æ´ä¸ä¸ªæ°æ®æµæè´£ä»»è¾¹ç
- 宿åå¯¹ç § Quick Checklist
When to Use
- Scenario Aï¼æ°é¡¹ç®æ°æ®å±å建
- Scenario Dï¼æ§è½é®é¢çæ°æ®å±ç¶é¢
- Scenario Fï¼KMP å ±äº«æ°æ®å±è®¾è®¡
Example Prompts
- “请åè Room Advancedï¼å¸®æè®¾è®¡ Migration ç祔
- “ä¾ç § Network Layer ç« èï¼å建ç»ä¸çé误å¤ç”
- “è¯·ç¨ Offline-First ç« èæ¥çç®å Repository æ¯å¦ç¬¦å SSOT”
Workflow
- å æ£æ¥ Room / Network çåºç¡è®¾è®¡
- åç¡®ç« Offline-First 䏿°æ®åæ¥çç¥
- æåç¨ Quick Checklist éªæ¶
Practical Notes (2026)
- Offline-first åªå¨ä¸ç¨³ç½ç»æé«ä¸è´æ§éæ±æ¶æ¿æ´»
- Repository å¿ é¡»æ¯ SSOTï¼é¿å å¤å¤æ¥æºç«äº
- é误å¤çç»ä¸åï¼é¿å æ¯å±èªè¡å¤æ
Minimal Template
ç®æ :
æ°æ®æº:
ç¼åçç¥:
é误å¤ç:
éªæ¶: Quick Checklist
Room Advanced
Migration çç¥
val MIGRATION_1_2 = object : Migration(1, 2) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("ALTER TABLE users ADD COLUMN avatar_url TEXT")
}
}
val MIGRATION_2_3 = object : Migration(2, 3) {
override fun migrate(database: SupportSQLiteDatabase) {
// 夿è¿ç§»ï¼å建æ°è¡¨ãå¤å¶æ°æ®ãå 餿§è¡¨
database.execSQL("CREATE TABLE users_new (...)")
database.execSQL("INSERT INTO users_new SELECT ... FROM users")
database.execSQL("DROP TABLE users")
database.execSQL("ALTER TABLE users_new RENAME TO users")
}
}
Room.databaseBuilder(context, AppDatabase::class.java, "app.db")
.addMigrations(MIGRATION_1_2, MIGRATION_2_3)
.build()
Paging 3 éæ
@Dao
interface UserDao {
@Query("SELECT * FROM users ORDER BY name")
fun pagingSource(): PagingSource<Int, User>
}
// Repository
class UserRepository(private val dao: UserDao) {
fun getUsers(): Flow<PagingData<User>> = Pager(
config = PagingConfig(pageSize = 20, prefetchDistance = 5),
pagingSourceFactory = { dao.pagingSource() }
).flow
}
// ViewModel
val users = repository.getUsers().cachedIn(viewModelScope)
Full-Text Search (FTS)
@Fts4(contentEntity = Article::class)
@Entity(tableName = "articles_fts")
data class ArticleFts(
@ColumnInfo(name = "title") val title: String,
@ColumnInfo(name = "content") val content: String
)
@Dao
interface ArticleDao {
@Query("SELECT * FROM articles WHERE rowid IN (SELECT rowid FROM articles_fts WHERE articles_fts MATCH :query)")
fun search(query: String): Flow<List<Article>>
}
Network Layer (Retrofit + OkHttp)
Error Handling Strategy
sealed class NetworkResult<out T> {
data class Success<T>(val data: T) : NetworkResult<T>()
data class Error(val code: Int, val message: String) : NetworkResult<Nothing>()
data object NetworkError : NetworkResult<Nothing>()
}
suspend fun <T> safeApiCall(apiCall: suspend () -> Response<T>): NetworkResult<T> {
return try {
val response = apiCall()
if (response.isSuccessful) {
NetworkResult.Success(response.body()!!)
} else {
NetworkResult.Error(response.code(), response.message())
}
} catch (e: IOException) {
NetworkResult.NetworkError
}
}
Interceptors
// Logging
val loggingInterceptor = HttpLoggingInterceptor().apply {
level = if (BuildConfig.DEBUG) BODY else NONE
}
// Auth Token
class AuthInterceptor(private val tokenProvider: TokenProvider) : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val request = chain.request().newBuilder()
.addHeader("Authorization", "Bearer ${tokenProvider.token}")
.build()
return chain.proceed(request)
}
}
// Retry
class RetryInterceptor(private val maxRetries: Int = 3) : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
var attempt = 0
var response: Response? = null
while (attempt < maxRetries) {
try {
response = chain.proceed(chain.request())
if (response.isSuccessful) return response
} catch (e: IOException) {
attempt++
if (attempt >= maxRetries) throw e
}
}
return response!!
}
}
Offline-First Architecture
Repository Pattern (SSOT)
class UserRepository(
private val remoteDataSource: UserRemoteDataSource,
private val localDataSource: UserLocalDataSource
) {
fun getUser(id: String): Flow<User> = flow {
// 1. å
ä» Local åå°
localDataSource.getUser(id)?.let { emit(it) }
// 2. ä» Remote å徿æ°
val remote = remoteDataSource.fetchUser(id)
// 3. åå
¥ Local
localDataSource.saveUser(remote)
// 4. åå°æ´æ°åçæ°æ®
emit(remote)
}
// æä½¿ç¨ NetworkBoundResource pattern
fun getUserWithCache(id: String): Flow<Resource<User>> = networkBoundResource(
query = { localDataSource.getUserFlow(id) },
fetch = { remoteDataSource.fetchUser(id) },
saveFetchResult = { localDataSource.saveUser(it) },
shouldFetch = { it == null || it.isStale() }
)
}
DataStore Migration
SharedPreferences â Preferences DataStore
val Context.dataStore by preferencesDataStore(
name = "settings",
produceMigrations = { context ->
listOf(SharedPreferencesMigration(context, "old_prefs"))
}
)
// 使ç¨
val themeKey = booleanPreferencesKey("dark_theme")
suspend fun setDarkTheme(enabled: Boolean) {
context.dataStore.edit { prefs ->
prefs[themeKey] = enabled
}
}
val darkThemeFlow: Flow<Boolean> = context.dataStore.data
.map { it[themeKey] ?: false }
Quick Checklist
- Room Migration æµè¯éè¿
- Network Error ç»ä¸å¤ç
- Repository å®ä½ SSOT
- DataStore å代 SharedPreferences
- Paging ç¨äºå¤§éæ°æ®å表