android-sdd 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +143 -0
- package/package.json +27 -0
- package/skills/Android Ecosystem/Baseline Profile Generator/SKILL.md +277 -0
- package/skills/Android Ecosystem/Glance/SKILL.md +315 -0
- package/skills/Android Platform/Configuration/SKILL.md +201 -0
- package/skills/Android Platform/Filesystem/SKILL.md +216 -0
- package/skills/Android Platform/Lifecycle/SKILL.md +233 -0
- package/skills/Android Platform/Manifest/SKILL.md +226 -0
- package/skills/Android Platform/Process Death Recovery/SKILL.md +214 -0
- package/skills/Android Platform/Resources/SKILL.md +234 -0
- package/skills/Android Platform/SavedStateHandle/SKILL.md +217 -0
- package/skills/Android Platform/State Restoration/SKILL.md +210 -0
- package/skills/Architecture/Bounded Context/SKILL.md +207 -0
- package/skills/Architecture/Clean Architecture/SKILL.md +229 -0
- package/skills/Architecture/Domain Modeling/SKILL.md +236 -0
- package/skills/Architecture/Entity Design/SKILL.md +243 -0
- package/skills/Architecture/Feature Isolation/SKILL.md +216 -0
- package/skills/Architecture/MVI/SKILL.md +224 -0
- package/skills/Architecture/MVVM/SKILL.md +198 -0
- package/skills/Architecture/Modularization/SKILL.md +194 -0
- package/skills/Architecture/Offline First/SKILL.md +249 -0
- package/skills/Architecture/Repository Pattern/SKILL.md +216 -0
- package/skills/Architecture/Side Effect Management/SKILL.md +278 -0
- package/skills/Architecture/State Management/SKILL.md +229 -0
- package/skills/Architecture/Unidirectional Data Flow/SKILL.md +196 -0
- package/skills/Architecture/Use Case Design/SKILL.md +244 -0
- package/skills/Architecture/Value Object/SKILL.md +226 -0
- package/skills/Build Infrastructure/Build Orchestration/SKILL.md +257 -0
- package/skills/Build Infrastructure/Dependency Compatibility Resolver/SKILL.md +259 -0
- package/skills/Build Infrastructure/Environment Validator/SKILL.md +311 -0
- package/skills/Build System/Build Cache/SKILL.md +233 -0
- package/skills/Build System/Build Flavor Strategy/SKILL.md +171 -0
- package/skills/Build System/Build Variant/SKILL.md +215 -0
- package/skills/Build System/Convention Plugin/SKILL.md +288 -0
- package/skills/Build System/Dependency Management/SKILL.md +261 -0
- package/skills/Build System/Gradle/SKILL.md +284 -0
- package/skills/Build System/Incremental Build/SKILL.md +199 -0
- package/skills/Build System/KAPT/SKILL.md +198 -0
- package/skills/Build System/KSP/SKILL.md +263 -0
- package/skills/Build System/Module Dependency Graph Validation/SKILL.md +223 -0
- package/skills/Build System/Specialized/C++/SKILL.md +308 -0
- package/skills/Build System/Specialized/JNI/SKILL.md +306 -0
- package/skills/Build System/Specialized/NDK/SKILL.md +264 -0
- package/skills/Build System/Version Catalog/SKILL.md +304 -0
- package/skills/Concurrency/Background Processing/SKILL.md +185 -0
- package/skills/Concurrency/Channel/SKILL.md +207 -0
- package/skills/Concurrency/Coroutine/SKILL.md +200 -0
- package/skills/Concurrency/Flow/SKILL.md +179 -0
- package/skills/Concurrency/Mutex Strategy/SKILL.md +185 -0
- package/skills/Concurrency/SharedFlow/SKILL.md +171 -0
- package/skills/Concurrency/StateFlow/SKILL.md +175 -0
- package/skills/Concurrency/Structured Concurrency/SKILL.md +197 -0
- package/skills/Concurrency/Synchronization Policy/SKILL.md +192 -0
- package/skills/Core Language/Annotation Processing/SKILL.md +224 -0
- package/skills/Core Language/DSL/SKILL.md +186 -0
- package/skills/Core Language/Extension Functions Design/SKILL.md +191 -0
- package/skills/Core Language/Immutability/SKILL.md +156 -0
- package/skills/Core Language/KMP/SKILL.md +182 -0
- package/skills/Core Language/Kotlin/SKILL.md +187 -0
- package/skills/Core Language/Reactive State Management/SKILL.md +228 -0
- package/skills/Core Language/Reactive Streams/SKILL.md +235 -0
- package/skills/Core Language/Serialization/SKILL.md +191 -0
- package/skills/Data Layer/Cache Strategy/SKILL.md +261 -0
- package/skills/Data Layer/Conflict Resolution/SKILL.md +248 -0
- package/skills/Data Layer/DAO/SKILL.md +225 -0
- package/skills/Data Layer/DTO Mapping/SKILL.md +269 -0
- package/skills/Data Layer/DataStore/SKILL.md +264 -0
- package/skills/Data Layer/Database Versioning Strategy/SKILL.md +215 -0
- package/skills/Data Layer/Encrypted Database/SKILL.md +212 -0
- package/skills/Data Layer/File Storage/SKILL.md +247 -0
- package/skills/Data Layer/Indexing/SKILL.md +184 -0
- package/skills/Data Layer/Key-Value Store Strategy/SKILL.md +185 -0
- package/skills/Data Layer/Merge Strategy/SKILL.md +240 -0
- package/skills/Data Layer/Migration/SKILL.md +243 -0
- package/skills/Data Layer/Paging/SKILL.md +264 -0
- package/skills/Data Layer/Proto DataStore/SKILL.md +250 -0
- package/skills/Data Layer/Room/SKILL.md +244 -0
- package/skills/Data Layer/SQLite/SKILL.md +255 -0
- package/skills/Data Layer/Sync Engine/SKILL.md +268 -0
- package/skills/Dependency Injection/Dagger/SKILL.md +283 -0
- package/skills/Dependency Injection/Hilt/SKILL.md +345 -0
- package/skills/Dependency Injection/Koin/SKILL.md +282 -0
- package/skills/Developer Experience/Detekt/SKILL.md +272 -0
- package/skills/Developer Experience/Lint Rule/SKILL.md +281 -0
- package/skills/Google Ecosystem/Analytics/SKILL.md +281 -0
- package/skills/Google Ecosystem/Crashlytics/SKILL.md +234 -0
- package/skills/Google Ecosystem/Firebase/SKILL.md +200 -0
- package/skills/Google Ecosystem/Firebase Messaging/SKILL.md +266 -0
- package/skills/Media/Audio/SKILL.md +257 -0
- package/skills/Media/Camera/SKILL.md +229 -0
- package/skills/Media/CameraX/SKILL.md +295 -0
- package/skills/Media/ExoPlayer/SKILL.md +258 -0
- package/skills/Media/Video/SKILL.md +228 -0
- package/skills/Meta Skills/Domain Error Model/SKILL.md +238 -0
- package/skills/Meta Skills/Error Handling/SKILL.md +255 -0
- package/skills/Meta Skills/Error Mapping/SKILL.md +232 -0
- package/skills/Meta Skills/Failure Strategy/SKILL.md +294 -0
- package/skills/Meta Skills/Migration Strategy/SKILL.md +305 -0
- package/skills/Meta Skills/User Friendly Errors/SKILL.md +334 -0
- package/skills/Navigation/Deep Navigation/SKILL.md +209 -0
- package/skills/Navigation/Navigation/SKILL.md +215 -0
- package/skills/Navigation/Nested Navigation/SKILL.md +214 -0
- package/skills/Networking/API Contract/SKILL.md +220 -0
- package/skills/Networking/Authentication/SKILL.md +210 -0
- package/skills/Networking/Certificate Pinning/SKILL.md +167 -0
- package/skills/Networking/Fallback Strategy/SKILL.md +182 -0
- package/skills/Networking/Ktor/SKILL.md +219 -0
- package/skills/Networking/Multipart Upload/SKILL.md +213 -0
- package/skills/Networking/OkHttp/SKILL.md +193 -0
- package/skills/Networking/REST/SKILL.md +178 -0
- package/skills/Networking/Rate Limiting/SKILL.md +170 -0
- package/skills/Networking/Retrofit/SKILL.md +241 -0
- package/skills/Networking/Retry-Backoff/SKILL.md +181 -0
- package/skills/Networking/Server-Sent Events (SSE)/SKILL.md +196 -0
- package/skills/Networking/WebSocket/SKILL.md +224 -0
- package/skills/Observability/Crash Reporting/SKILL.md +219 -0
- package/skills/Observability/Logging/SKILL.md +168 -0
- package/skills/Observability/Metrics/SKILL.md +227 -0
- package/skills/Observability/Structured Logging/SKILL.md +234 -0
- package/skills/Performance/ANR Prevention/SKILL.md +192 -0
- package/skills/Performance/Allocation Optimization/SKILL.md +179 -0
- package/skills/Performance/App Startup/SKILL.md +183 -0
- package/skills/Performance/Baseline Profile/SKILL.md +205 -0
- package/skills/Performance/Battery Optimization/SKILL.md +192 -0
- package/skills/Performance/Benchmark/SKILL.md +182 -0
- package/skills/Performance/Bitmap Optimization/SKILL.md +178 -0
- package/skills/Performance/Compose Optimization/SKILL.md +187 -0
- package/skills/Performance/Heap Management/SKILL.md +184 -0
- package/skills/Performance/Macrobenchmark/SKILL.md +214 -0
- package/skills/Performance/Memory Leak Prevention/SKILL.md +218 -0
- package/skills/Performance/Rendering Performance/SKILL.md +205 -0
- package/skills/Performance/Startup Optimization/SKILL.md +219 -0
- package/skills/Security/Biometric/SKILL.md +224 -0
- package/skills/Security/Certificate Transparency/SKILL.md +158 -0
- package/skills/Security/Cryptography/SKILL.md +244 -0
- package/skills/Security/Encrypted Storage/SKILL.md +273 -0
- package/skills/Security/Frida Detection/SKILL.md +230 -0
- package/skills/Security/Hook Detection/SKILL.md +197 -0
- package/skills/Security/Keystore/SKILL.md +272 -0
- package/skills/Security/Network Security Config/SKILL.md +186 -0
- package/skills/Security/Obfuscation/SKILL.md +226 -0
- package/skills/Security/Proguard/SKILL.md +202 -0
- package/skills/Security/R8/SKILL.md +234 -0
- package/skills/Security/Reverse Engineering Resistance/SKILL.md +267 -0
- package/skills/Security/Root Detection/SKILL.md +220 -0
- package/skills/Security/Secure Networking/SKILL.md +220 -0
- package/skills/System Integration/AlarmManager/SKILL.md +182 -0
- package/skills/System Integration/App Widget/SKILL.md +182 -0
- package/skills/System Integration/Deep Link/SKILL.md +187 -0
- package/skills/System Integration/Foreground Service/SKILL.md +212 -0
- package/skills/System Integration/Notification/SKILL.md +237 -0
- package/skills/System Integration/WorkManager/SKILL.md +256 -0
- package/skills/System Integration/clipboard/SKILL.md +155 -0
- package/skills/System Integration/share-intent/SKILL.md +182 -0
- package/skills/Testing/Compose Testing/SKILL.md +296 -0
- package/skills/Testing/Espresso/SKILL.md +292 -0
- package/skills/Testing/Fake Data/SKILL.md +245 -0
- package/skills/Testing/Integration Testing/SKILL.md +288 -0
- package/skills/Testing/Mocking/SKILL.md +229 -0
- package/skills/Testing/Snapshot Testing/SKILL.md +259 -0
- package/skills/Testing/UI Testing/SKILL.md +293 -0
- package/skills/Testing/Unit Testing/SKILL.md +309 -0
- package/skills/UI System/Bottom Sheet Patterns/SKILL.md +279 -0
- package/skills/UI System/Compose/SKILL.md +296 -0
- package/skills/UI System/Compose Animation/SKILL.md +281 -0
- package/skills/UI System/Compose Multiplatform/SKILL.md +261 -0
- package/skills/UI System/Compose Navigation/SKILL.md +255 -0
- package/skills/UI System/Compose Performance/SKILL.md +274 -0
- package/skills/UI System/Design System/SKILL.md +217 -0
- package/skills/UI System/Empty State Strategy/SKILL.md +208 -0
- package/skills/UI System/Keyboard Navigation/SKILL.md +214 -0
- package/skills/UI System/Loading Strategy/SKILL.md +254 -0
- package/skills/UI System/Material 3/SKILL.md +279 -0
- package/skills/UI System/RTL/SKILL.md +179 -0
- package/src/index.ts +182 -0
- package/tsconfig.json +19 -0
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: filesystem
|
|
3
|
+
description: >
|
|
4
|
+
Android filesystem — internal storage, external storage, cache, and file sharing.
|
|
5
|
+
Load this skill when reading or writing files, sharing files between apps,
|
|
6
|
+
choosing the right storage location, or handling storage permissions.
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Filesystem
|
|
10
|
+
|
|
11
|
+
## Overview
|
|
12
|
+
|
|
13
|
+
Android provides several storage locations with different visibility, persistence, and permission requirements. Choosing the right location depends on whether files are private to the app, shareable with other apps, or user-visible. Scoped Storage (API 29+) significantly restricts direct filesystem access.
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Core Principles
|
|
18
|
+
|
|
19
|
+
- Default to **internal storage** — private, no permissions needed
|
|
20
|
+
- Use **external storage** only for files the user should access via file manager
|
|
21
|
+
- Never use `Environment.getExternalStorageDirectory()` — deprecated, requires dangerous permission
|
|
22
|
+
- Use `FileProvider` for sharing files with other apps — never expose raw file paths
|
|
23
|
+
- Cache files can be deleted by the system at any time — don't rely on them for critical data
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## Storage Locations
|
|
28
|
+
|
|
29
|
+
| Location | API | Permission | Cleared on Uninstall | Visible to User |
|
|
30
|
+
| ------------------ | ------------------------------- | ------------------------ | -------------------- | --------------- |
|
|
31
|
+
| `filesDir` | `context.filesDir` | None | ✅ Yes | ❌ No |
|
|
32
|
+
| `cacheDir` | `context.cacheDir` | None | ✅ Yes | ❌ No |
|
|
33
|
+
| `externalFilesDir` | `context.getExternalFilesDir()` | None (API 19+) | ✅ Yes | ✅ Yes |
|
|
34
|
+
| `externalCacheDir` | `context.externalCacheDir` | None | ✅ Yes | ✅ Yes |
|
|
35
|
+
| MediaStore | Via ContentResolver | `READ_MEDIA_*` (API 33+) | ❌ No | ✅ Yes |
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## Internal Storage
|
|
40
|
+
|
|
41
|
+
```kotlin
|
|
42
|
+
// ✅ Write to internal storage
|
|
43
|
+
fun writeFile(context: Context, fileName: String, content: String) {
|
|
44
|
+
val file = File(context.filesDir, fileName)
|
|
45
|
+
file.writeText(content)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// ✅ Read from internal storage
|
|
49
|
+
fun readFile(context: Context, fileName: String): String? {
|
|
50
|
+
val file = File(context.filesDir, fileName)
|
|
51
|
+
return if (file.exists()) file.readText() else null
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// ✅ Subdirectory in internal storage
|
|
55
|
+
fun getAppDirectory(context: Context, dirName: String): File {
|
|
56
|
+
return File(context.filesDir, dirName).also { it.mkdirs() }
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// ✅ Write binary data
|
|
60
|
+
fun writeBinaryFile(context: Context, fileName: String, bytes: ByteArray) {
|
|
61
|
+
File(context.filesDir, fileName).writeBytes(bytes)
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
## Cache Storage
|
|
68
|
+
|
|
69
|
+
```kotlin
|
|
70
|
+
// ✅ Use cache for temporary/reproducible data
|
|
71
|
+
fun cacheImage(context: Context, name: String, bitmap: Bitmap) {
|
|
72
|
+
val file = File(context.cacheDir, name)
|
|
73
|
+
file.outputStream().use { out ->
|
|
74
|
+
bitmap.compress(Bitmap.CompressFormat.JPEG, 90, out)
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// ✅ Check cache before downloading
|
|
79
|
+
fun getCachedFile(context: Context, name: String): File? {
|
|
80
|
+
val file = File(context.cacheDir, name)
|
|
81
|
+
return if (file.exists()) file else null
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// ✅ Limit cache size manually if needed
|
|
85
|
+
fun trimCache(context: Context, maxSizeBytes: Long) {
|
|
86
|
+
val cacheDir = context.cacheDir
|
|
87
|
+
val files = cacheDir.listFiles()?.sortedBy { it.lastModified() } ?: return
|
|
88
|
+
var totalSize = files.sumOf { it.length() }
|
|
89
|
+
for (file in files) {
|
|
90
|
+
if (totalSize <= maxSizeBytes) break
|
|
91
|
+
totalSize -= file.length()
|
|
92
|
+
file.delete()
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
## External App-Specific Storage
|
|
100
|
+
|
|
101
|
+
```kotlin
|
|
102
|
+
// ✅ Write to app-specific external directory (no permission needed)
|
|
103
|
+
fun writeToExternalFiles(context: Context, fileName: String, content: String) {
|
|
104
|
+
val dir = context.getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS)
|
|
105
|
+
?: return // external storage may not be available
|
|
106
|
+
File(dir, fileName).writeText(content)
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// ✅ Check external storage availability
|
|
110
|
+
fun isExternalStorageWritable(): Boolean =
|
|
111
|
+
Environment.getExternalStorageState() == Environment.MEDIA_MOUNTED
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
## File Sharing with FileProvider
|
|
117
|
+
|
|
118
|
+
```xml
|
|
119
|
+
<!-- AndroidManifest.xml -->
|
|
120
|
+
<provider
|
|
121
|
+
android:name="androidx.core.content.FileProvider"
|
|
122
|
+
android:authorities="${applicationId}.fileprovider"
|
|
123
|
+
android:exported="false"
|
|
124
|
+
android:grantUriPermissions="true">
|
|
125
|
+
<meta-data
|
|
126
|
+
android:name="android.support.FILE_PROVIDER_PATHS"
|
|
127
|
+
android:resource="@xml/file_paths" />
|
|
128
|
+
</provider>
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
```xml
|
|
132
|
+
<!-- res/xml/file_paths.xml -->
|
|
133
|
+
<paths>
|
|
134
|
+
<files-path name="internal_files" path="." />
|
|
135
|
+
<cache-path name="cache" path="." />
|
|
136
|
+
<external-files-path name="external_files" path="." />
|
|
137
|
+
</paths>
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
```kotlin
|
|
141
|
+
// ✅ Share a file via FileProvider
|
|
142
|
+
fun shareFile(context: Context, file: File) {
|
|
143
|
+
val uri = FileProvider.getUriForFile(
|
|
144
|
+
context,
|
|
145
|
+
"${context.packageName}.fileprovider",
|
|
146
|
+
file
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
val intent = Intent(Intent.ACTION_SEND).apply {
|
|
150
|
+
type = "application/pdf"
|
|
151
|
+
putExtra(Intent.EXTRA_STREAM, uri)
|
|
152
|
+
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
context.startActivity(Intent.createChooser(intent, "Share File"))
|
|
156
|
+
}
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
---
|
|
160
|
+
|
|
161
|
+
## Reading Files from Assets
|
|
162
|
+
|
|
163
|
+
```kotlin
|
|
164
|
+
// ✅ Read from assets/
|
|
165
|
+
fun readAsset(context: Context, fileName: String): String {
|
|
166
|
+
return context.assets.open(fileName).bufferedReader().use { it.readText() }
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// ✅ Read JSON config from assets
|
|
170
|
+
fun readJsonAsset(context: Context, fileName: String): JsonObject {
|
|
171
|
+
val content = readAsset(context, fileName)
|
|
172
|
+
return Json.parseToJsonElement(content).jsonObject
|
|
173
|
+
}
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
---
|
|
177
|
+
|
|
178
|
+
## Coroutine-Safe File I/O
|
|
179
|
+
|
|
180
|
+
```kotlin
|
|
181
|
+
// ✅ Always perform file I/O on Dispatchers.IO
|
|
182
|
+
suspend fun writeFileSafely(context: Context, fileName: String, content: String) {
|
|
183
|
+
withContext(Dispatchers.IO) {
|
|
184
|
+
File(context.filesDir, fileName).writeText(content)
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
suspend fun readFileSafely(context: Context, fileName: String): String? {
|
|
189
|
+
return withContext(Dispatchers.IO) {
|
|
190
|
+
val file = File(context.filesDir, fileName)
|
|
191
|
+
if (file.exists()) file.readText() else null
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
---
|
|
197
|
+
|
|
198
|
+
## Anti-Patterns
|
|
199
|
+
|
|
200
|
+
- Using `Environment.getExternalStorageDirectory()` — deprecated since API 29
|
|
201
|
+
- File I/O on the main thread — causes ANR
|
|
202
|
+
- Exposing raw `file://` URIs to other apps — crashes on API 24+, use FileProvider
|
|
203
|
+
- Storing sensitive data in external storage — accessible by other apps
|
|
204
|
+
- Relying on cache files for critical data — system can delete them anytime
|
|
205
|
+
- Not checking external storage availability before writing
|
|
206
|
+
- Hardcoding absolute paths — they differ across devices and API levels
|
|
207
|
+
|
|
208
|
+
---
|
|
209
|
+
|
|
210
|
+
## Related Skills
|
|
211
|
+
|
|
212
|
+
- `file-storage` — higher-level file storage patterns
|
|
213
|
+
- `binary-storage` — storing binary/media files
|
|
214
|
+
- `encrypted-database` — encrypting sensitive stored data
|
|
215
|
+
- `workmanager` — background file operations
|
|
216
|
+
- `datastore` — structured key-value persistence
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: lifecycle
|
|
3
|
+
description: >
|
|
4
|
+
Android Lifecycle management for Activities, Fragments, and Compose.
|
|
5
|
+
Load this skill when working with lifecycle-aware components, collecting
|
|
6
|
+
flows with lifecycle awareness, managing resources tied to lifecycle,
|
|
7
|
+
or avoiding memory leaks caused by lifecycle misuse.
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Lifecycle
|
|
11
|
+
|
|
12
|
+
## Overview
|
|
13
|
+
|
|
14
|
+
The Android Lifecycle represents the states an Activity, Fragment, or other component goes through from creation to destruction. Lifecycle-awareness ensures that operations start and stop at the right time, preventing memory leaks, crashes, and wasted resources.
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## Core Principles
|
|
19
|
+
|
|
20
|
+
- Never hold references to Activity or Fragment in long-lived objects
|
|
21
|
+
- Always use lifecycle-aware collection for Flows — never raw `launch {}`
|
|
22
|
+
- Release resources in the **symmetric opposite** of where they were acquired
|
|
23
|
+
- Prefer `ViewModel` to survive configuration changes — never store UI state in Activity
|
|
24
|
+
- Use `repeatOnLifecycle` over `launchWhenStarted` — it properly cancels on stop
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## Lifecycle States
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
INITIALIZED → CREATED → STARTED → RESUMED
|
|
32
|
+
↑ ↓
|
|
33
|
+
DESTROYED ← STOPPED
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
| State | Activity callback | Fragment callback |
|
|
37
|
+
| --------- | ----------------- | ----------------- |
|
|
38
|
+
| CREATED | `onCreate()` | `onViewCreated()` |
|
|
39
|
+
| STARTED | `onStart()` | `onStart()` |
|
|
40
|
+
| RESUMED | `onResume()` | `onResume()` |
|
|
41
|
+
| STOPPED | `onStop()` | `onStop()` |
|
|
42
|
+
| DESTROYED | `onDestroy()` | `onDestroyView()` |
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## Flow Collection — The Right Way
|
|
47
|
+
|
|
48
|
+
```kotlin
|
|
49
|
+
// ✅ repeatOnLifecycle — cancels collection when below target state
|
|
50
|
+
viewLifecycleOwner.lifecycleScope.launch {
|
|
51
|
+
repeatOnLifecycle(Lifecycle.State.STARTED) {
|
|
52
|
+
viewModel.state.collect { state ->
|
|
53
|
+
renderState(state)
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// ✅ Multiple flows — launch inside repeatOnLifecycle
|
|
59
|
+
viewLifecycleOwner.lifecycleScope.launch {
|
|
60
|
+
repeatOnLifecycle(Lifecycle.State.STARTED) {
|
|
61
|
+
launch { viewModel.state.collect { renderState(it) } }
|
|
62
|
+
launch { viewModel.events.collect { handleEvent(it) } }
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// ✅ Compose — collectAsStateWithLifecycle (preferred over collectAsState)
|
|
67
|
+
@Composable
|
|
68
|
+
fun MyScreen(viewModel: MyViewModel = hiltViewModel()) {
|
|
69
|
+
val state by viewModel.state.collectAsStateWithLifecycle()
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// ❌ Wrong — doesn't cancel when app goes to background
|
|
73
|
+
lifecycleScope.launch {
|
|
74
|
+
viewModel.state.collect { renderState(it) }
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// ❌ Wrong — deprecated, doesn't resume collection after stop
|
|
78
|
+
lifecycleScope.launchWhenStarted {
|
|
79
|
+
viewModel.state.collect { renderState(it) }
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
## ViewModel and Lifecycle
|
|
86
|
+
|
|
87
|
+
```kotlin
|
|
88
|
+
// ✅ ViewModel survives configuration changes
|
|
89
|
+
class UserViewModel : ViewModel() {
|
|
90
|
+
// State here survives rotation
|
|
91
|
+
private val _state = MutableStateFlow(UserUiState())
|
|
92
|
+
val state: StateFlow<UserUiState> = _state.asStateFlow()
|
|
93
|
+
|
|
94
|
+
// ✅ viewModelScope is cancelled when ViewModel is cleared
|
|
95
|
+
fun loadUsers() {
|
|
96
|
+
viewModelScope.launch {
|
|
97
|
+
// safe — auto-cancelled on ViewModel destruction
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// ✅ onCleared — clean up resources
|
|
102
|
+
override fun onCleared() {
|
|
103
|
+
super.onCleared()
|
|
104
|
+
// cancel non-coroutine resources if needed
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// ✅ Get ViewModel in Fragment
|
|
109
|
+
class UserFragment : Fragment() {
|
|
110
|
+
private val viewModel: UserViewModel by viewModels()
|
|
111
|
+
// shared ViewModel across fragments in same Activity
|
|
112
|
+
private val sharedViewModel: SharedViewModel by activityViewModels()
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
## Lifecycle-Aware Resource Management
|
|
119
|
+
|
|
120
|
+
```kotlin
|
|
121
|
+
// ✅ Register/unregister in symmetric callbacks
|
|
122
|
+
class LocationFragment : Fragment() {
|
|
123
|
+
|
|
124
|
+
private lateinit var locationManager: LocationManager
|
|
125
|
+
|
|
126
|
+
override fun onStart() {
|
|
127
|
+
super.onStart()
|
|
128
|
+
locationManager.startUpdates() // start in onStart
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
override fun onStop() {
|
|
132
|
+
locationManager.stopUpdates() // stop in onStop
|
|
133
|
+
super.onStop()
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// ✅ ViewBinding — avoid leaks
|
|
138
|
+
class UserFragment : Fragment(R.layout.fragment_user) {
|
|
139
|
+
|
|
140
|
+
private var _binding: FragmentUserBinding? = null
|
|
141
|
+
private val binding get() = _binding!!
|
|
142
|
+
|
|
143
|
+
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
|
144
|
+
super.onViewCreated(view, savedInstanceState)
|
|
145
|
+
_binding = FragmentUserBinding.bind(view)
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
override fun onDestroyView() {
|
|
149
|
+
_binding = null // ✅ clear binding reference
|
|
150
|
+
super.onDestroyView()
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
---
|
|
156
|
+
|
|
157
|
+
## DefaultLifecycleObserver
|
|
158
|
+
|
|
159
|
+
```kotlin
|
|
160
|
+
// ✅ Use DefaultLifecycleObserver for custom lifecycle-aware components
|
|
161
|
+
class AnalyticsTracker(private val analytics: Analytics) : DefaultLifecycleObserver {
|
|
162
|
+
|
|
163
|
+
override fun onStart(owner: LifecycleOwner) {
|
|
164
|
+
analytics.startSession()
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
override fun onStop(owner: LifecycleOwner) {
|
|
168
|
+
analytics.endSession()
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Register in Activity or Fragment
|
|
173
|
+
lifecycle.addObserver(AnalyticsTracker(analytics))
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
---
|
|
177
|
+
|
|
178
|
+
## Compose Lifecycle
|
|
179
|
+
|
|
180
|
+
```kotlin
|
|
181
|
+
// ✅ LaunchedEffect — scoped to composition, cancelled on leave
|
|
182
|
+
@Composable
|
|
183
|
+
fun UserScreen(userId: String) {
|
|
184
|
+
LaunchedEffect(userId) {
|
|
185
|
+
// runs when userId changes, cancelled when composable leaves
|
|
186
|
+
viewModel.loadUser(userId)
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// ✅ DisposableEffect — for non-coroutine cleanup
|
|
191
|
+
@Composable
|
|
192
|
+
fun MapScreen() {
|
|
193
|
+
DisposableEffect(Unit) {
|
|
194
|
+
val listener = registerMapListener()
|
|
195
|
+
onDispose {
|
|
196
|
+
listener.unregister()
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// ✅ rememberUpdatedState — keep latest value in long-running effect
|
|
202
|
+
@Composable
|
|
203
|
+
fun Timer(onTick: () -> Unit) {
|
|
204
|
+
val currentOnTick by rememberUpdatedState(onTick)
|
|
205
|
+
LaunchedEffect(Unit) {
|
|
206
|
+
while (true) {
|
|
207
|
+
delay(1_000)
|
|
208
|
+
currentOnTick()
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
---
|
|
215
|
+
|
|
216
|
+
## Anti-Patterns
|
|
217
|
+
|
|
218
|
+
- Storing Activity/Fragment reference in ViewModel or Repository — causes memory leaks
|
|
219
|
+
- Using `lifecycleScope.launch {}` without `repeatOnLifecycle` for Flow collection
|
|
220
|
+
- Using `launchWhenStarted` — deprecated, doesn't cancel on stop
|
|
221
|
+
- Starting work in `onCreate` that should be in `onStart` — runs even when not visible
|
|
222
|
+
- Forgetting to clear ViewBinding in `onDestroyView` — memory leak
|
|
223
|
+
- Using `GlobalScope` for any lifecycle-bound work
|
|
224
|
+
|
|
225
|
+
---
|
|
226
|
+
|
|
227
|
+
## Related Skills
|
|
228
|
+
|
|
229
|
+
- `savedstatehandle` — persisting state across process death
|
|
230
|
+
- `process-death-recovery` — surviving process kill
|
|
231
|
+
- `viewmodel` — state management with ViewModel
|
|
232
|
+
- `compose` — Compose-specific lifecycle patterns
|
|
233
|
+
- `reactive-streams` — lifecycle-aware Flow collection
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: manifest
|
|
3
|
+
description: >
|
|
4
|
+
Android Manifest configuration and best practices.
|
|
5
|
+
Load this skill when editing AndroidManifest.xml, declaring components,
|
|
6
|
+
configuring permissions, intent filters, or app-level attributes.
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Manifest
|
|
10
|
+
|
|
11
|
+
## Overview
|
|
12
|
+
|
|
13
|
+
The AndroidManifest.xml is the entry point configuration of every Android app. It declares all components, permissions, hardware requirements, and app-level behavior. Incorrect manifest configuration causes runtime crashes, security vulnerabilities, and Play Store rejections.
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Core Principles
|
|
18
|
+
|
|
19
|
+
- Declare only what the app **actually needs** — minimal permissions
|
|
20
|
+
- Never declare a component that isn't used — increases attack surface
|
|
21
|
+
- Always declare `android:exported` explicitly on components with intent filters
|
|
22
|
+
- Keep sensitive config (API keys, secrets) out of the manifest — use BuildConfig or encrypted storage
|
|
23
|
+
- Use `tools:` namespace for merge directives — never duplicate entries
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## Application Block
|
|
28
|
+
|
|
29
|
+
```xml
|
|
30
|
+
<application
|
|
31
|
+
android:name=".MyApplication"
|
|
32
|
+
android:icon="@mipmap/ic_launcher"
|
|
33
|
+
android:roundIcon="@mipmap/ic_launcher_round"
|
|
34
|
+
android:label="@string/app_name"
|
|
35
|
+
android:theme="@style/Theme.App"
|
|
36
|
+
android:supportsRtl="true"
|
|
37
|
+
android:allowBackup="false"
|
|
38
|
+
android:fullBackupContent="false"
|
|
39
|
+
android:dataExtractionRules="@xml/data_extraction_rules"
|
|
40
|
+
android:networkSecurityConfig="@xml/network_security_config">
|
|
41
|
+
|
|
42
|
+
<!-- components go here -->
|
|
43
|
+
|
|
44
|
+
</application>
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## Activity Declaration
|
|
50
|
+
|
|
51
|
+
```xml
|
|
52
|
+
<!-- ✅ Main launcher activity -->
|
|
53
|
+
<activity
|
|
54
|
+
android:name=".MainActivity"
|
|
55
|
+
android:exported="true"
|
|
56
|
+
android:windowSoftInputMode="adjustResize"
|
|
57
|
+
android:configChanges="orientation|screenSize|keyboardHidden">
|
|
58
|
+
<intent-filter>
|
|
59
|
+
<action android:name="android.intent.action.MAIN" />
|
|
60
|
+
<category android:name="android.intent.category.LAUNCHER" />
|
|
61
|
+
</intent-filter>
|
|
62
|
+
</activity>
|
|
63
|
+
|
|
64
|
+
<!-- ✅ Internal activity — not exported -->
|
|
65
|
+
<activity
|
|
66
|
+
android:name=".feature.detail.DetailActivity"
|
|
67
|
+
android:exported="false" />
|
|
68
|
+
|
|
69
|
+
<!-- ✅ Deep link support -->
|
|
70
|
+
<activity
|
|
71
|
+
android:name=".MainActivity"
|
|
72
|
+
android:exported="true">
|
|
73
|
+
<intent-filter android:autoVerify="true">
|
|
74
|
+
<action android:name="android.intent.action.VIEW" />
|
|
75
|
+
<category android:name="android.intent.category.DEFAULT" />
|
|
76
|
+
<category android:name="android.intent.category.BROWSABLE" />
|
|
77
|
+
<data android:scheme="https" android:host="example.com" />
|
|
78
|
+
</intent-filter>
|
|
79
|
+
</activity>
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
## Permissions
|
|
85
|
+
|
|
86
|
+
```xml
|
|
87
|
+
<!-- ✅ Declare only what's needed -->
|
|
88
|
+
<uses-permission android:name="android.permission.INTERNET" />
|
|
89
|
+
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
|
90
|
+
|
|
91
|
+
<!-- ✅ Runtime permissions — still declare in manifest -->
|
|
92
|
+
<uses-permission android:name="android.permission.CAMERA" />
|
|
93
|
+
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
|
|
94
|
+
|
|
95
|
+
<!-- ✅ Restrict to specific OS version if not needed on older -->
|
|
96
|
+
<uses-permission
|
|
97
|
+
android:name="android.permission.POST_NOTIFICATIONS"
|
|
98
|
+
android:minSdkVersion="33" />
|
|
99
|
+
|
|
100
|
+
<!-- ✅ Remove permission added by a library -->
|
|
101
|
+
<uses-permission
|
|
102
|
+
android:name="android.permission.READ_PHONE_STATE"
|
|
103
|
+
tools:node="remove" />
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### Permission Groups by Category
|
|
107
|
+
|
|
108
|
+
| Category | Permissions |
|
|
109
|
+
| ------------- | ------------------------------------------------------------------------------ |
|
|
110
|
+
| Network | `INTERNET`, `ACCESS_NETWORK_STATE`, `CHANGE_NETWORK_STATE` |
|
|
111
|
+
| Location | `ACCESS_FINE_LOCATION`, `ACCESS_COARSE_LOCATION`, `ACCESS_BACKGROUND_LOCATION` |
|
|
112
|
+
| Camera | `CAMERA` |
|
|
113
|
+
| Storage | `READ_MEDIA_IMAGES`, `READ_MEDIA_VIDEO`, `READ_MEDIA_AUDIO` |
|
|
114
|
+
| Notifications | `POST_NOTIFICATIONS` (API 33+) |
|
|
115
|
+
| Bluetooth | `BLUETOOTH_SCAN`, `BLUETOOTH_CONNECT` (API 31+) |
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
## Services
|
|
120
|
+
|
|
121
|
+
```xml
|
|
122
|
+
<!-- ✅ Foreground service -->
|
|
123
|
+
<service
|
|
124
|
+
android:name=".service.DownloadService"
|
|
125
|
+
android:exported="false"
|
|
126
|
+
android:foregroundServiceType="dataSync" />
|
|
127
|
+
|
|
128
|
+
<!-- ✅ WorkManager worker — no declaration needed -->
|
|
129
|
+
<!-- Workers are not components and don't need manifest entries -->
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
## Receivers
|
|
135
|
+
|
|
136
|
+
```xml
|
|
137
|
+
<!-- ✅ Boot receiver -->
|
|
138
|
+
<receiver
|
|
139
|
+
android:name=".receiver.BootReceiver"
|
|
140
|
+
android:exported="true">
|
|
141
|
+
<intent-filter>
|
|
142
|
+
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
|
143
|
+
</intent-filter>
|
|
144
|
+
</receiver>
|
|
145
|
+
|
|
146
|
+
<!-- ✅ Internal receiver — not exported -->
|
|
147
|
+
<receiver
|
|
148
|
+
android:name=".receiver.DownloadReceiver"
|
|
149
|
+
android:exported="false" />
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
---
|
|
153
|
+
|
|
154
|
+
## Providers
|
|
155
|
+
|
|
156
|
+
```xml
|
|
157
|
+
<!-- ✅ FileProvider for sharing files -->
|
|
158
|
+
<provider
|
|
159
|
+
android:name="androidx.core.content.FileProvider"
|
|
160
|
+
android:authorities="${applicationId}.fileprovider"
|
|
161
|
+
android:exported="false"
|
|
162
|
+
android:grantUriPermissions="true">
|
|
163
|
+
<meta-data
|
|
164
|
+
android:name="android.support.FILE_PROVIDER_PATHS"
|
|
165
|
+
android:resource="@xml/file_paths" />
|
|
166
|
+
</provider>
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
---
|
|
170
|
+
|
|
171
|
+
## Manifest Merge (Multi-Module)
|
|
172
|
+
|
|
173
|
+
```xml
|
|
174
|
+
<!-- ✅ Override a library's manifest entry -->
|
|
175
|
+
<activity
|
|
176
|
+
android:name="com.library.SomeActivity"
|
|
177
|
+
android:exported="false"
|
|
178
|
+
tools:node="merge" />
|
|
179
|
+
|
|
180
|
+
<!-- ✅ Remove a component added by a library -->
|
|
181
|
+
<activity
|
|
182
|
+
android:name="com.library.UnwantedActivity"
|
|
183
|
+
tools:node="remove" />
|
|
184
|
+
|
|
185
|
+
<!-- ✅ Replace an attribute from a library -->
|
|
186
|
+
<application
|
|
187
|
+
android:allowBackup="false"
|
|
188
|
+
tools:replace="android:allowBackup" />
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
---
|
|
192
|
+
|
|
193
|
+
## Hardware Features
|
|
194
|
+
|
|
195
|
+
```xml
|
|
196
|
+
<!-- ✅ Declare required hardware -->
|
|
197
|
+
<uses-feature
|
|
198
|
+
android:name="android.hardware.camera"
|
|
199
|
+
android:required="true" />
|
|
200
|
+
|
|
201
|
+
<!-- ✅ Declare optional hardware — don't block install -->
|
|
202
|
+
<uses-feature
|
|
203
|
+
android:name="android.hardware.camera.autofocus"
|
|
204
|
+
android:required="false" />
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
---
|
|
208
|
+
|
|
209
|
+
## Anti-Patterns
|
|
210
|
+
|
|
211
|
+
- `android:exported` not set on components with intent-filters — security risk, required API 31+
|
|
212
|
+
- Storing API keys or secrets in `<meta-data>` — visible in decompiled APK
|
|
213
|
+
- Declaring `READ_EXTERNAL_STORAGE` on API 33+ — use `READ_MEDIA_*` instead
|
|
214
|
+
- Requesting `ACCESS_BACKGROUND_LOCATION` without foreground location first
|
|
215
|
+
- Declaring unused permissions — increases permissions dialog burden and audit surface
|
|
216
|
+
- Missing `android:supportsRtl="true"` — breaks RTL layouts
|
|
217
|
+
- `android:allowBackup="true"` without configuring backup rules — leaks sensitive data
|
|
218
|
+
|
|
219
|
+
---
|
|
220
|
+
|
|
221
|
+
## Related Skills
|
|
222
|
+
|
|
223
|
+
- `deep-link` — intent filter setup for deep links
|
|
224
|
+
- `foreground-service` — foreground service declaration
|
|
225
|
+
- `network-security-config` — network security configuration
|
|
226
|
+
- `resources` — resource references in manifest
|