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.
Files changed (176) hide show
  1. package/dist/index.js +143 -0
  2. package/package.json +27 -0
  3. package/skills/Android Ecosystem/Baseline Profile Generator/SKILL.md +277 -0
  4. package/skills/Android Ecosystem/Glance/SKILL.md +315 -0
  5. package/skills/Android Platform/Configuration/SKILL.md +201 -0
  6. package/skills/Android Platform/Filesystem/SKILL.md +216 -0
  7. package/skills/Android Platform/Lifecycle/SKILL.md +233 -0
  8. package/skills/Android Platform/Manifest/SKILL.md +226 -0
  9. package/skills/Android Platform/Process Death Recovery/SKILL.md +214 -0
  10. package/skills/Android Platform/Resources/SKILL.md +234 -0
  11. package/skills/Android Platform/SavedStateHandle/SKILL.md +217 -0
  12. package/skills/Android Platform/State Restoration/SKILL.md +210 -0
  13. package/skills/Architecture/Bounded Context/SKILL.md +207 -0
  14. package/skills/Architecture/Clean Architecture/SKILL.md +229 -0
  15. package/skills/Architecture/Domain Modeling/SKILL.md +236 -0
  16. package/skills/Architecture/Entity Design/SKILL.md +243 -0
  17. package/skills/Architecture/Feature Isolation/SKILL.md +216 -0
  18. package/skills/Architecture/MVI/SKILL.md +224 -0
  19. package/skills/Architecture/MVVM/SKILL.md +198 -0
  20. package/skills/Architecture/Modularization/SKILL.md +194 -0
  21. package/skills/Architecture/Offline First/SKILL.md +249 -0
  22. package/skills/Architecture/Repository Pattern/SKILL.md +216 -0
  23. package/skills/Architecture/Side Effect Management/SKILL.md +278 -0
  24. package/skills/Architecture/State Management/SKILL.md +229 -0
  25. package/skills/Architecture/Unidirectional Data Flow/SKILL.md +196 -0
  26. package/skills/Architecture/Use Case Design/SKILL.md +244 -0
  27. package/skills/Architecture/Value Object/SKILL.md +226 -0
  28. package/skills/Build Infrastructure/Build Orchestration/SKILL.md +257 -0
  29. package/skills/Build Infrastructure/Dependency Compatibility Resolver/SKILL.md +259 -0
  30. package/skills/Build Infrastructure/Environment Validator/SKILL.md +311 -0
  31. package/skills/Build System/Build Cache/SKILL.md +233 -0
  32. package/skills/Build System/Build Flavor Strategy/SKILL.md +171 -0
  33. package/skills/Build System/Build Variant/SKILL.md +215 -0
  34. package/skills/Build System/Convention Plugin/SKILL.md +288 -0
  35. package/skills/Build System/Dependency Management/SKILL.md +261 -0
  36. package/skills/Build System/Gradle/SKILL.md +284 -0
  37. package/skills/Build System/Incremental Build/SKILL.md +199 -0
  38. package/skills/Build System/KAPT/SKILL.md +198 -0
  39. package/skills/Build System/KSP/SKILL.md +263 -0
  40. package/skills/Build System/Module Dependency Graph Validation/SKILL.md +223 -0
  41. package/skills/Build System/Specialized/C++/SKILL.md +308 -0
  42. package/skills/Build System/Specialized/JNI/SKILL.md +306 -0
  43. package/skills/Build System/Specialized/NDK/SKILL.md +264 -0
  44. package/skills/Build System/Version Catalog/SKILL.md +304 -0
  45. package/skills/Concurrency/Background Processing/SKILL.md +185 -0
  46. package/skills/Concurrency/Channel/SKILL.md +207 -0
  47. package/skills/Concurrency/Coroutine/SKILL.md +200 -0
  48. package/skills/Concurrency/Flow/SKILL.md +179 -0
  49. package/skills/Concurrency/Mutex Strategy/SKILL.md +185 -0
  50. package/skills/Concurrency/SharedFlow/SKILL.md +171 -0
  51. package/skills/Concurrency/StateFlow/SKILL.md +175 -0
  52. package/skills/Concurrency/Structured Concurrency/SKILL.md +197 -0
  53. package/skills/Concurrency/Synchronization Policy/SKILL.md +192 -0
  54. package/skills/Core Language/Annotation Processing/SKILL.md +224 -0
  55. package/skills/Core Language/DSL/SKILL.md +186 -0
  56. package/skills/Core Language/Extension Functions Design/SKILL.md +191 -0
  57. package/skills/Core Language/Immutability/SKILL.md +156 -0
  58. package/skills/Core Language/KMP/SKILL.md +182 -0
  59. package/skills/Core Language/Kotlin/SKILL.md +187 -0
  60. package/skills/Core Language/Reactive State Management/SKILL.md +228 -0
  61. package/skills/Core Language/Reactive Streams/SKILL.md +235 -0
  62. package/skills/Core Language/Serialization/SKILL.md +191 -0
  63. package/skills/Data Layer/Cache Strategy/SKILL.md +261 -0
  64. package/skills/Data Layer/Conflict Resolution/SKILL.md +248 -0
  65. package/skills/Data Layer/DAO/SKILL.md +225 -0
  66. package/skills/Data Layer/DTO Mapping/SKILL.md +269 -0
  67. package/skills/Data Layer/DataStore/SKILL.md +264 -0
  68. package/skills/Data Layer/Database Versioning Strategy/SKILL.md +215 -0
  69. package/skills/Data Layer/Encrypted Database/SKILL.md +212 -0
  70. package/skills/Data Layer/File Storage/SKILL.md +247 -0
  71. package/skills/Data Layer/Indexing/SKILL.md +184 -0
  72. package/skills/Data Layer/Key-Value Store Strategy/SKILL.md +185 -0
  73. package/skills/Data Layer/Merge Strategy/SKILL.md +240 -0
  74. package/skills/Data Layer/Migration/SKILL.md +243 -0
  75. package/skills/Data Layer/Paging/SKILL.md +264 -0
  76. package/skills/Data Layer/Proto DataStore/SKILL.md +250 -0
  77. package/skills/Data Layer/Room/SKILL.md +244 -0
  78. package/skills/Data Layer/SQLite/SKILL.md +255 -0
  79. package/skills/Data Layer/Sync Engine/SKILL.md +268 -0
  80. package/skills/Dependency Injection/Dagger/SKILL.md +283 -0
  81. package/skills/Dependency Injection/Hilt/SKILL.md +345 -0
  82. package/skills/Dependency Injection/Koin/SKILL.md +282 -0
  83. package/skills/Developer Experience/Detekt/SKILL.md +272 -0
  84. package/skills/Developer Experience/Lint Rule/SKILL.md +281 -0
  85. package/skills/Google Ecosystem/Analytics/SKILL.md +281 -0
  86. package/skills/Google Ecosystem/Crashlytics/SKILL.md +234 -0
  87. package/skills/Google Ecosystem/Firebase/SKILL.md +200 -0
  88. package/skills/Google Ecosystem/Firebase Messaging/SKILL.md +266 -0
  89. package/skills/Media/Audio/SKILL.md +257 -0
  90. package/skills/Media/Camera/SKILL.md +229 -0
  91. package/skills/Media/CameraX/SKILL.md +295 -0
  92. package/skills/Media/ExoPlayer/SKILL.md +258 -0
  93. package/skills/Media/Video/SKILL.md +228 -0
  94. package/skills/Meta Skills/Domain Error Model/SKILL.md +238 -0
  95. package/skills/Meta Skills/Error Handling/SKILL.md +255 -0
  96. package/skills/Meta Skills/Error Mapping/SKILL.md +232 -0
  97. package/skills/Meta Skills/Failure Strategy/SKILL.md +294 -0
  98. package/skills/Meta Skills/Migration Strategy/SKILL.md +305 -0
  99. package/skills/Meta Skills/User Friendly Errors/SKILL.md +334 -0
  100. package/skills/Navigation/Deep Navigation/SKILL.md +209 -0
  101. package/skills/Navigation/Navigation/SKILL.md +215 -0
  102. package/skills/Navigation/Nested Navigation/SKILL.md +214 -0
  103. package/skills/Networking/API Contract/SKILL.md +220 -0
  104. package/skills/Networking/Authentication/SKILL.md +210 -0
  105. package/skills/Networking/Certificate Pinning/SKILL.md +167 -0
  106. package/skills/Networking/Fallback Strategy/SKILL.md +182 -0
  107. package/skills/Networking/Ktor/SKILL.md +219 -0
  108. package/skills/Networking/Multipart Upload/SKILL.md +213 -0
  109. package/skills/Networking/OkHttp/SKILL.md +193 -0
  110. package/skills/Networking/REST/SKILL.md +178 -0
  111. package/skills/Networking/Rate Limiting/SKILL.md +170 -0
  112. package/skills/Networking/Retrofit/SKILL.md +241 -0
  113. package/skills/Networking/Retry-Backoff/SKILL.md +181 -0
  114. package/skills/Networking/Server-Sent Events (SSE)/SKILL.md +196 -0
  115. package/skills/Networking/WebSocket/SKILL.md +224 -0
  116. package/skills/Observability/Crash Reporting/SKILL.md +219 -0
  117. package/skills/Observability/Logging/SKILL.md +168 -0
  118. package/skills/Observability/Metrics/SKILL.md +227 -0
  119. package/skills/Observability/Structured Logging/SKILL.md +234 -0
  120. package/skills/Performance/ANR Prevention/SKILL.md +192 -0
  121. package/skills/Performance/Allocation Optimization/SKILL.md +179 -0
  122. package/skills/Performance/App Startup/SKILL.md +183 -0
  123. package/skills/Performance/Baseline Profile/SKILL.md +205 -0
  124. package/skills/Performance/Battery Optimization/SKILL.md +192 -0
  125. package/skills/Performance/Benchmark/SKILL.md +182 -0
  126. package/skills/Performance/Bitmap Optimization/SKILL.md +178 -0
  127. package/skills/Performance/Compose Optimization/SKILL.md +187 -0
  128. package/skills/Performance/Heap Management/SKILL.md +184 -0
  129. package/skills/Performance/Macrobenchmark/SKILL.md +214 -0
  130. package/skills/Performance/Memory Leak Prevention/SKILL.md +218 -0
  131. package/skills/Performance/Rendering Performance/SKILL.md +205 -0
  132. package/skills/Performance/Startup Optimization/SKILL.md +219 -0
  133. package/skills/Security/Biometric/SKILL.md +224 -0
  134. package/skills/Security/Certificate Transparency/SKILL.md +158 -0
  135. package/skills/Security/Cryptography/SKILL.md +244 -0
  136. package/skills/Security/Encrypted Storage/SKILL.md +273 -0
  137. package/skills/Security/Frida Detection/SKILL.md +230 -0
  138. package/skills/Security/Hook Detection/SKILL.md +197 -0
  139. package/skills/Security/Keystore/SKILL.md +272 -0
  140. package/skills/Security/Network Security Config/SKILL.md +186 -0
  141. package/skills/Security/Obfuscation/SKILL.md +226 -0
  142. package/skills/Security/Proguard/SKILL.md +202 -0
  143. package/skills/Security/R8/SKILL.md +234 -0
  144. package/skills/Security/Reverse Engineering Resistance/SKILL.md +267 -0
  145. package/skills/Security/Root Detection/SKILL.md +220 -0
  146. package/skills/Security/Secure Networking/SKILL.md +220 -0
  147. package/skills/System Integration/AlarmManager/SKILL.md +182 -0
  148. package/skills/System Integration/App Widget/SKILL.md +182 -0
  149. package/skills/System Integration/Deep Link/SKILL.md +187 -0
  150. package/skills/System Integration/Foreground Service/SKILL.md +212 -0
  151. package/skills/System Integration/Notification/SKILL.md +237 -0
  152. package/skills/System Integration/WorkManager/SKILL.md +256 -0
  153. package/skills/System Integration/clipboard/SKILL.md +155 -0
  154. package/skills/System Integration/share-intent/SKILL.md +182 -0
  155. package/skills/Testing/Compose Testing/SKILL.md +296 -0
  156. package/skills/Testing/Espresso/SKILL.md +292 -0
  157. package/skills/Testing/Fake Data/SKILL.md +245 -0
  158. package/skills/Testing/Integration Testing/SKILL.md +288 -0
  159. package/skills/Testing/Mocking/SKILL.md +229 -0
  160. package/skills/Testing/Snapshot Testing/SKILL.md +259 -0
  161. package/skills/Testing/UI Testing/SKILL.md +293 -0
  162. package/skills/Testing/Unit Testing/SKILL.md +309 -0
  163. package/skills/UI System/Bottom Sheet Patterns/SKILL.md +279 -0
  164. package/skills/UI System/Compose/SKILL.md +296 -0
  165. package/skills/UI System/Compose Animation/SKILL.md +281 -0
  166. package/skills/UI System/Compose Multiplatform/SKILL.md +261 -0
  167. package/skills/UI System/Compose Navigation/SKILL.md +255 -0
  168. package/skills/UI System/Compose Performance/SKILL.md +274 -0
  169. package/skills/UI System/Design System/SKILL.md +217 -0
  170. package/skills/UI System/Empty State Strategy/SKILL.md +208 -0
  171. package/skills/UI System/Keyboard Navigation/SKILL.md +214 -0
  172. package/skills/UI System/Loading Strategy/SKILL.md +254 -0
  173. package/skills/UI System/Material 3/SKILL.md +279 -0
  174. package/skills/UI System/RTL/SKILL.md +179 -0
  175. package/src/index.ts +182 -0
  176. package/tsconfig.json +19 -0
@@ -0,0 +1,192 @@
1
+ ---
2
+ name: battery-optimization
3
+ description: >
4
+ Battery usage optimization in Android apps.
5
+ Load this skill when reducing background battery drain, handling Doze
6
+ mode, batching network requests, optimizing location usage,
7
+ or profiling energy usage with Android Studio Energy Profiler.
8
+ ---
9
+
10
+ # Battery Optimization
11
+
12
+ ## Overview
13
+ Battery drain is one of the top reasons users uninstall apps. The main causes are: excessive wake locks, frequent network requests, GPS usage, and background processing that ignores Doze/App Standby. The goal is to do necessary work efficiently and batch or defer work that doesn't need to happen immediately.
14
+
15
+ ---
16
+
17
+ ## Core Principles
18
+
19
+ - **Batch** network requests — fewer wakeups is better than many small ones
20
+ - **Defer** non-urgent work to when the device is charging or on Wi-Fi (WorkManager constraints)
21
+ - Release **WakeLocks** as quickly as possible — always in a `finally` block
22
+ - Use **passive location** or lower accuracy when precise GPS is not needed
23
+ - Never poll — use push (FCM) or `ContentObserver` instead
24
+
25
+ ---
26
+
27
+ ## WorkManager Constraints
28
+
29
+ ```kotlin
30
+ // ✅ Defer heavy sync to favorable conditions
31
+ val syncRequest = PeriodicWorkRequestBuilder<SyncWorker>(1, TimeUnit.HOURS)
32
+ .setConstraints(
33
+ Constraints.Builder()
34
+ .setRequiredNetworkType(NetworkType.UNMETERED) // Wi-Fi only
35
+ .setRequiresCharging(true) // only when charging
36
+ .setRequiresBatteryNotLow(true) // not low battery
37
+ .build()
38
+ )
39
+ .build()
40
+ ```
41
+
42
+ ---
43
+
44
+ ## Batching Network Requests
45
+
46
+ ```kotlin
47
+ // ❌ Individual requests — each wakes radio
48
+ suspend fun syncAll() {
49
+ syncUsers() // wakes radio
50
+ delay(100)
51
+ syncOrders() // wakes radio again
52
+ delay(100)
53
+ syncProducts() // wakes radio again
54
+ }
55
+
56
+ // ✅ Single batched request — one radio wakeup
57
+ suspend fun syncAll() {
58
+ val batch = syncRepository.syncAll() // one API call returning all data
59
+ processBatchResult(batch)
60
+ }
61
+ ```
62
+
63
+ ---
64
+
65
+ ## Doze Mode Awareness
66
+
67
+ ```kotlin
68
+ // Doze mode restricts:
69
+ // - Network access
70
+ // - Wake locks
71
+ // - AlarmManager (except setAndAllowWhileIdle)
72
+ // - JobScheduler / WorkManager (deferred)
73
+ //
74
+ // Exempt from Doze:
75
+ // - FCM high-priority messages
76
+ // - ForegroundService
77
+ // - WorkManager with setExpedited
78
+
79
+ // ✅ Expedited work for urgent tasks during Doze
80
+ val urgentRequest = OneTimeWorkRequestBuilder<UrgentSyncWorker>()
81
+ .setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST)
82
+ .build()
83
+ ```
84
+
85
+ ---
86
+
87
+ ## WakeLock — Use Sparingly
88
+
89
+ ```kotlin
90
+ // ✅ Acquire and release WakeLock safely
91
+ class WakeLockManager @Inject constructor(
92
+ @ApplicationContext context: Context
93
+ ) {
94
+ private val powerManager = context.getSystemService(PowerManager::class.java)
95
+ private val wakeLock = powerManager.newWakeLock(
96
+ PowerManager.PARTIAL_WAKE_LOCK,
97
+ "MyApp::SyncWakeLock"
98
+ )
99
+
100
+ fun withWakeLock(timeoutMs: Long = 10_000, block: () -> Unit) {
101
+ wakeLock.acquire(timeoutMs) // always set timeout
102
+ try {
103
+ block()
104
+ } finally {
105
+ if (wakeLock.isHeld) wakeLock.release() // ✅ always release
106
+ }
107
+ }
108
+ }
109
+ ```
110
+
111
+ ---
112
+
113
+ ## Location Battery Optimization
114
+
115
+ ```kotlin
116
+ // ✅ Use lowest accuracy tier that meets the need
117
+ val locationRequest = LocationRequest.Builder(
118
+ Priority.PRIORITY_BALANCED_POWER_ACCURACY, // cell tower + Wi-Fi — less battery than GPS
119
+ intervalMillis = 30_000 // 30 second interval
120
+ )
121
+ .setMinUpdateDistanceMeters(50f) // only update if moved 50m
122
+ .build()
123
+
124
+ // ✅ Use PRIORITY_PASSIVE for background — piggyback on other apps' location fixes
125
+ val passiveRequest = LocationRequest.Builder(
126
+ Priority.PRIORITY_PASSIVE,
127
+ intervalMillis = 60_000
128
+ ).build()
129
+
130
+ // ✅ Stop updates when not needed
131
+ override fun onStop() {
132
+ locationClient.removeLocationUpdates(locationCallback)
133
+ super.onStop()
134
+ }
135
+ ```
136
+
137
+ ---
138
+
139
+ ## Polling vs Push
140
+
141
+ ```kotlin
142
+ // ❌ Polling — wakes device repeatedly
143
+ fun startPolling() {
144
+ viewModelScope.launch {
145
+ while (true) {
146
+ fetchNewMessages() // unnecessary if no new messages
147
+ delay(30_000)
148
+ }
149
+ }
150
+ }
151
+
152
+ // ✅ FCM push — device wakes only when there's actual data
153
+ // Server sends FCM notification → app wakes → fetches only when needed
154
+ class MessagingService : FirebaseMessagingService() {
155
+ override fun onMessageReceived(message: RemoteMessage) {
156
+ // Triggered only when server sends a message
157
+ syncMessages()
158
+ }
159
+ }
160
+ ```
161
+
162
+ ---
163
+
164
+ ## Battery Profiling
165
+
166
+ ```
167
+ Android Studio → Profiler → Energy:
168
+ - Shows CPU, network, and location wakeups over time
169
+ - Identify: which operations cause the most wakeups
170
+ - Compare before/after optimization
171
+
172
+ adb shell dumpsys batterystats --reset // reset stats
173
+ adb shell dumpsys batterystats // dump current stats
174
+ ```
175
+
176
+ ---
177
+
178
+ ## Anti-Patterns
179
+
180
+ - Holding WakeLock without a timeout — device never sleeps
181
+ - Polling for updates instead of using FCM or WebSocket
182
+ - Using `PRIORITY_HIGH_ACCURACY` location for all use cases — drains GPS
183
+ - Running WorkManager without constraints — executes even on low battery/metered network
184
+ - Ignoring Doze mode — work silently skipped without the app knowing
185
+
186
+ ---
187
+
188
+ ## Related Skills
189
+ - `workmanager` — scheduling work with battery-aware constraints
190
+ - `background-processing` — choosing the right background mechanism
191
+ - `firebase-messaging` — push notifications to replace polling
192
+ - `foreground-service` — when continuous background work is truly needed
@@ -0,0 +1,182 @@
1
+ ---
2
+ name: benchmark
3
+ description: >
4
+ Microbenchmarking with Jetpack Benchmark in Android.
5
+ Load this skill when measuring method-level performance, comparing
6
+ algorithm implementations, benchmarking serialization or data processing,
7
+ or setting up the benchmark module.
8
+ ---
9
+
10
+ # Benchmark
11
+
12
+ ## Overview
13
+ Jetpack Benchmark (microbenchmark) measures the performance of individual code units — methods, algorithms, serialization — with statistical accuracy. It handles warm-up, clock stability, and GC pauses automatically. Use it to compare implementations and catch regressions.
14
+
15
+ ---
16
+
17
+ ## Core Principles
18
+
19
+ - Benchmark measures **one thing at a time** — isolate the code under test
20
+ - Run on a **physical device** — emulator results are unreliable
21
+ - Use `BenchmarkRule` — it handles warm-up and measurement automatically
22
+ - Benchmark in **release mode** — debug builds are significantly slower
23
+ - Compare relative results — absolute numbers vary by device
24
+
25
+ ---
26
+
27
+ ## Setup
28
+
29
+ ```toml
30
+ # libs.versions.toml
31
+ [libraries]
32
+ androidx-benchmark-junit4 = { module = "androidx.benchmark:benchmark-junit4", version = "1.2.4" }
33
+ ```
34
+
35
+ ```kotlin
36
+ // benchmark/build.gradle.kts (separate module)
37
+ plugins {
38
+ alias(libs.plugins.android.library)
39
+ }
40
+
41
+ android {
42
+ defaultConfig {
43
+ testInstrumentationRunner = "androidx.benchmark.junit4.AndroidBenchmarkRunner"
44
+ testInstrumentationRunnerArguments["androidx.benchmark.suppressErrors"] = "EMULATOR,LOW-BATTERY"
45
+ }
46
+
47
+ buildTypes {
48
+ release {
49
+ isMinifyEnabled = true
50
+ proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"))
51
+ }
52
+ create("benchmark") {
53
+ initWith(buildTypes.getByName("release"))
54
+ signingConfig = signingConfigs.getByName("debug")
55
+ matchingFallbacks += listOf("release")
56
+ }
57
+ }
58
+ }
59
+
60
+ dependencies {
61
+ androidTestImplementation(libs.androidx.benchmark.junit4)
62
+ }
63
+ ```
64
+
65
+ ---
66
+
67
+ ## Writing a Benchmark
68
+
69
+ ```kotlin
70
+ // ✅ Basic benchmark
71
+ @RunWith(AndroidJUnit4::class)
72
+ class SerializationBenchmark {
73
+
74
+ @get:Rule
75
+ val benchmarkRule = BenchmarkRule()
76
+
77
+ private val json = Json { ignoreUnknownKeys = true }
78
+ private val sampleJson = """{"id":"1","name":"John","email":"john@example.com"}"""
79
+
80
+ @Test
81
+ fun deserializeUser() = benchmarkRule.measureRepeated {
82
+ // ✅ Only the code under test inside measureRepeated
83
+ json.decodeFromString<UserDto>(sampleJson)
84
+ }
85
+
86
+ @Test
87
+ fun serializeUser() = benchmarkRule.measureRepeated {
88
+ val user = UserDto("1", "John", "john@example.com")
89
+ json.encodeToString(user)
90
+ }
91
+ }
92
+ ```
93
+
94
+ ---
95
+
96
+ ## Comparing Implementations
97
+
98
+ ```kotlin
99
+ // ✅ Compare two sorting algorithms
100
+ @RunWith(AndroidJUnit4::class)
101
+ class SortingBenchmark {
102
+
103
+ @get:Rule
104
+ val benchmarkRule = BenchmarkRule()
105
+
106
+ private val data = (1..10_000).shuffled()
107
+
108
+ @Test
109
+ fun sortWithSortedBy() = benchmarkRule.measureRepeated {
110
+ data.sortedBy { it }
111
+ }
112
+
113
+ @Test
114
+ fun sortWithSort() = benchmarkRule.measureRepeated {
115
+ val copy = data.toMutableList()
116
+ copy.sort()
117
+ copy
118
+ }
119
+ }
120
+ ```
121
+
122
+ ---
123
+
124
+ ## Setup and Teardown
125
+
126
+ ```kotlin
127
+ // ✅ runWithTimingDisabled for setup outside measurement
128
+ @Test
129
+ fun processLargeList() = benchmarkRule.measureRepeated {
130
+ val data = runWithTimingDisabled {
131
+ // Setup not counted in benchmark
132
+ generateLargeDataSet(1000)
133
+ }
134
+ // Only this is measured
135
+ processData(data)
136
+ }
137
+ ```
138
+
139
+ ---
140
+
141
+ ## Running Benchmarks
142
+
143
+ ```bash
144
+ # Run all benchmarks
145
+ ./gradlew :benchmark:connectedAndroidTest \
146
+ -Pandroid.testInstrumentationRunnerArguments.class=com.example.SerializationBenchmark
147
+
148
+ # Output in logcat:
149
+ # SerializationBenchmark.deserializeUser: min=45us, median=47us, max=62us
150
+ ```
151
+
152
+ ---
153
+
154
+ ## Allocation Benchmark
155
+
156
+ ```kotlin
157
+ // ✅ Measure allocations alongside time
158
+ @Test
159
+ fun checkAllocations() = benchmarkRule.measureRepeated {
160
+ // BenchmarkRule automatically tracks allocations
161
+ // Check "allocationCount" in results
162
+ val result = mapper.toDomain(userDto)
163
+ }
164
+ ```
165
+
166
+ ---
167
+
168
+ ## Anti-Patterns
169
+
170
+ - Running benchmarks on emulator — results are not representative
171
+ - Putting setup code inside `measureRepeated` — inflates measurement
172
+ - Benchmarking in debug build — JIT and debug overhead skew results
173
+ - Benchmarking with the screen on and other apps running — interference from system
174
+ - Comparing results across different devices — only compare on same device
175
+
176
+ ---
177
+
178
+ ## Related Skills
179
+ - `macrobenchmark` — end-to-end performance measurement
180
+ - `baseline-profile` — using benchmark results to generate profiles
181
+ - `allocation-optimization` — reducing allocations flagged by benchmark
182
+ - `rendering-performance` — frame-level performance measurement
@@ -0,0 +1,178 @@
1
+ ---
2
+ name: bitmap-optimization
3
+ description: >
4
+ Bitmap loading, scaling, and memory optimization in Android.
5
+ Load this skill when loading images efficiently, scaling bitmaps
6
+ to required dimensions, using inSampleSize, managing Bitmap recycling,
7
+ or reducing native heap usage from image decoding.
8
+ ---
9
+
10
+ # Bitmap Optimization
11
+
12
+ ## Overview
13
+ Bitmaps are the largest consumers of native heap on Android. A single 12MP photo decoded as ARGB_8888 uses ~48MB. Proper bitmap optimization involves decoding at the required size, choosing the right pixel format, and using a caching library (Coil, Glide) rather than manual management.
14
+
15
+ ---
16
+
17
+ ## Core Principles
18
+
19
+ - Never decode full-resolution bitmaps for thumbnail display — use `inSampleSize`
20
+ - Use **Coil** (or Glide) for image loading — they handle caching, sampling, and lifecycle
21
+ - Prefer `RGB_565` for images that don't need transparency — half the memory of `ARGB_8888`
22
+ - Recycle bitmaps manually only if not using a caching library
23
+ - Load bitmaps on a background thread — never on the main thread
24
+
25
+ ---
26
+
27
+ ## Coil — Recommended Image Loader
28
+
29
+ ```toml
30
+ # libs.versions.toml
31
+ [libraries]
32
+ coil = { module = "io.coil-kt:coil-compose", version = "2.7.0" }
33
+ ```
34
+
35
+ ```kotlin
36
+ // ✅ Load image in Compose with Coil
37
+ @Composable
38
+ fun UserAvatar(imageUrl: String?, modifier: Modifier = Modifier) {
39
+ AsyncImage(
40
+ model = ImageRequest.Builder(LocalContext.current)
41
+ .data(imageUrl)
42
+ .crossfade(true)
43
+ .size(64, 64) // ✅ decode at display size — not full res
44
+ .build(),
45
+ contentDescription = null,
46
+ contentScale = ContentScale.Crop,
47
+ placeholder = painterResource(R.drawable.avatar_placeholder),
48
+ error = painterResource(R.drawable.avatar_error),
49
+ modifier = modifier
50
+ .size(64.dp)
51
+ .clip(CircleShape)
52
+ )
53
+ }
54
+ ```
55
+
56
+ ---
57
+
58
+ ## Manual Bitmap Sampling (inSampleSize)
59
+
60
+ ```kotlin
61
+ // ✅ Decode at required size without loading full bitmap
62
+ fun decodeSampledBitmap(filePath: String, reqWidth: Int, reqHeight: Int): Bitmap {
63
+ // First pass: get dimensions only
64
+ val options = BitmapFactory.Options().apply {
65
+ inJustDecodeBounds = true
66
+ }
67
+ BitmapFactory.decodeFile(filePath, options)
68
+
69
+ // Calculate sample size
70
+ options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight)
71
+ options.inJustDecodeBounds = false
72
+
73
+ return BitmapFactory.decodeFile(filePath, options)
74
+ }
75
+
76
+ fun calculateInSampleSize(options: BitmapFactory.Options, reqWidth: Int, reqHeight: Int): Int {
77
+ val height = options.outHeight
78
+ val width = options.outWidth
79
+ var inSampleSize = 1
80
+
81
+ if (height > reqHeight || width > reqWidth) {
82
+ val halfHeight = height / 2
83
+ val halfWidth = width / 2
84
+ while (halfHeight / inSampleSize >= reqHeight && halfWidth / inSampleSize >= reqWidth) {
85
+ inSampleSize *= 2
86
+ }
87
+ }
88
+ return inSampleSize
89
+ }
90
+ ```
91
+
92
+ ---
93
+
94
+ ## Pixel Format Selection
95
+
96
+ ```kotlin
97
+ // ✅ RGB_565 — half memory, no transparency
98
+ val options = BitmapFactory.Options().apply {
99
+ inPreferredConfig = Bitmap.Config.RGB_565 // 2 bytes/pixel vs 4 bytes for ARGB_8888
100
+ }
101
+ val bitmap = BitmapFactory.decodeResource(resources, R.drawable.background, options)
102
+
103
+ // ✅ ARGB_8888 — full quality with alpha
104
+ val options = BitmapFactory.Options().apply {
105
+ inPreferredConfig = Bitmap.Config.ARGB_8888
106
+ }
107
+
108
+ // Memory comparison for 1920x1080 image:
109
+ // ARGB_8888 = 1920 * 1080 * 4 = ~8MB
110
+ // RGB_565 = 1920 * 1080 * 2 = ~4MB
111
+ ```
112
+
113
+ ---
114
+
115
+ ## Bitmap Reuse (inBitmap)
116
+
117
+ ```kotlin
118
+ // ✅ Reuse existing bitmap memory — avoids new allocation
119
+ val reusableBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
120
+
121
+ val options = BitmapFactory.Options().apply {
122
+ inMutable = true
123
+ inBitmap = reusableBitmap // reuse existing allocation
124
+ }
125
+ val newBitmap = BitmapFactory.decodeFile(filePath, options)
126
+ // newBitmap === reusableBitmap — same memory, new content
127
+ ```
128
+
129
+ ---
130
+
131
+ ## Compress Before Upload
132
+
133
+ ```kotlin
134
+ // ✅ Compress bitmap before sending to server
135
+ fun Bitmap.toCompressedByteArray(
136
+ format: Bitmap.CompressFormat = Bitmap.CompressFormat.JPEG,
137
+ quality: Int = 85
138
+ ): ByteArray {
139
+ return ByteArrayOutputStream().use { out ->
140
+ compress(format, quality, out)
141
+ out.toByteArray()
142
+ }
143
+ }
144
+
145
+ // ✅ Scale down before compressing
146
+ fun Uri.toScaledBitmap(context: Context, maxDimension: Int = 1024): Bitmap {
147
+ val original = BitmapFactory.decodeStream(context.contentResolver.openInputStream(this))
148
+ val scale = maxDimension.toFloat() / maxOf(original.width, original.height)
149
+ return if (scale < 1f) {
150
+ Bitmap.createScaledBitmap(
151
+ original,
152
+ (original.width * scale).toInt(),
153
+ (original.height * scale).toInt(),
154
+ true
155
+ ).also { original.recycle() }
156
+ } else {
157
+ original
158
+ }
159
+ }
160
+ ```
161
+
162
+ ---
163
+
164
+ ## Anti-Patterns
165
+
166
+ - Loading full-resolution images for small thumbnails — massive memory waste
167
+ - Decoding bitmaps on the main thread — ANR risk
168
+ - Not recycling bitmaps when done (if managing manually) — native heap leak
169
+ - Using `ARGB_8888` for all images including opaque backgrounds — unnecessary memory
170
+ - Storing decoded bitmaps in static fields — never GC'd
171
+
172
+ ---
173
+
174
+ ## Related Skills
175
+ - `heap-management` — overall heap and memory pressure management
176
+ - `memory-leak-prevention` — preventing bitmap reference leaks
177
+ - `loading-strategy` — showing placeholders while images load
178
+ - `allocation-optimization` — reducing GC pressure from bitmap operations