Common Memory Leaks in Pharmacy Apps: Causes and Fixes

These patterns are common in pharmacy apps because the domain frequently deals with high‑volume images (drug labels), persistent data (e.g., medication histories), and complex user flows (prescription

January 26, 2026 · 5 min read · Common Issues

1. Technical Root Causes of Memory Leaks in Pharmacy Apps

CategoryTypical FaultWhy it leaks
Image and media cachingStoring medication images in a global HashMap without eviction policyBitmaps consume large amounts of RAM; a global map grows indefinitely as users browse new drug listings
Database connectionsKeeping an SQLiteOpenHelper instance alive in a Service that never closes itSQLiteDatabase holds native pointers; an open connection prevents GC of related objects
Observer patternsLiveData or RxJava streams subscribed in Activity but never unsubscribed on onDestroyThe observer keeps a strong reference to the Activity, preventing its GC
Static references to ContextCaching a Context in a static field for convenienceThe Context (often an Activity) survives beyond its lifecycle, keeping the whole view hierarchy in memory
Anonymous inner classesUsing new View.OnClickListener() inside an Activity that holds a reference to the outer classThe listener holds an implicit reference to the Activity; if the listener stays alive (e.g., in a background thread) the Activity cannot be collected
Unreleased resources in background tasksAsyncTask that writes prescription data to a file but never closes the streamFile handles and buffers stay in memory until the OS forces a cleanup
Large data structures in global scopeStoring entire prescription history in a singleton ListThe list stays alive for the app’s lifetime, growing with each new prescription scan

These patterns are common in pharmacy apps because the domain frequently deals with high‑volume images (drug labels), persistent data (e.g., medication histories), and complex user flows (prescription refills, refill reminders).

---

2. Real‑World Impact

SymptomTypical User ComplaintBusiness Consequence
App hangs while loading a drug page“The app freezes every time I look at the drug details.”Users abandon the app → lower store rating
Crashes after a refill reminder triggers“My phone died when the reminder popped up.”Immediate loss of user trust, negative reviews
Slow search response“I wait a minute for the pharmacy search to finish.”Users may switch to competitor apps
Inconsistent prescription sync“My prescription list is missing my last refill.”Potential legal liability, regulatory penalties

A 2023 study of 1,200 pharmacy apps on the Google Play Store found that 18 % had at least one memory‑leak‑related crash. Stores with persistent crash rates saw a 12 % drop in monthly revenue compared to peers with stable performance.

---

3. Concrete Manifestations in Pharmacy Apps

  1. Infinite image cache – A global LruCache is used, but the size is set to the number of drugs in the catalog (hundreds), not the available memory.
  2. Unclosed prescription‑history cursor – A Cursor opened in a Service that tracks medication adherence is never closed.
  3. Stale RxJava subscription – A BehaviorSubject publishes pharmacy promotions, but the Activity never calls dispose() in onDestroy.
  4. Static Context leak – A helper class holds a static reference to an Activity to access resources, preventing the activity from being GC’d after navigation.
  5. Anonymous click listener leaking a Fragment – A Fragment registers a click listener on a global button that outlives the fragment’s view.
  6. Heavy AsyncTask for prescription download – Downloads large PDF labels but never calls outputStream.close().

Each of these patterns progressively fills the heap, causing the app to throw OutOfMemoryError or throttle UI updates, which manifests as the symptoms above.

---

4. Detecting Memory Leaks

Tool / TechniqueWhat to Look ForHow to Use
Android Studio Memory ProfilerHeap allocation trends, “Leaked Objects” paneCapture a full heap dump after a long session; filter by class names (Bitmap, Cursor)
LeakCanaryAutomatic leak detection on app startupAdd implementation "com.squareup.leakcanary:leakcanary-android:2.10"; run the app; review the notification
Systrace / TraceviewHigh CPU and memory usage spikesRecord a trace while performing pharmacy search or refill; look for long‑running tasks
SUSATest automated regressionAuto‑generated Appium/Playwright scripts that repeatedly navigate to prescription historyRun the agent in CI; review JUnit XML for “FAILURE – OutOfMemoryError”
Chrome DevTools (Web)Large DOM nodes or long‑running timers in the pharmacy web portalOpen Performance tab; look for “Long Tasks” > 50 ms
Static analysis (Detekt, Lint)Unclosed resources, static context usageConfigure rules to flag SQLiteDatabase not closed, Context stored in static fields

A useful practice is to combine a snapshot at app launch with a snapshot after a typical user session (search → view → refill). The delta reveals objects that survive longer than expected.

---

5. Fixing Each Example

LeakCode‑Level Fix
Infinite image cacheReplace HashMap with LruCache sized by Runtime.getRuntime().maxMemory() / 8. Use Glide or Coil to handle caching automatically.
Unclosed cursorWrap cursor usage in a try‑finally block: cursor.close(); or use Room which auto‑closes cursors.
Stale RxJava subscriptionStore disposables in a CompositeDisposable and call composite.clear() in onDestroy.
Static Context leakPass ApplicationContext or use WeakReference; never keep an Activity in a static field.
Anonymous click listener leaking FragmentRegister listener in onViewCreated; unregister in onDestroyView. Prefer lambda or a separate class that does not capture the fragment.
AsyncTask with open streamUse Kotlin use {} or try‑with‑resources: outputStream.use { /* write */ }. Ensure InputStream.close() is called.

Example: Closing a prescription cursor


fun loadPrescriptionHistory(db: SQLiteDatabase) {
    val cursor = db.rawQuery("SELECT * FROM prescriptions", null)
    try {
        while (cursor.moveToNext()) {
            // process record
        }
    } finally {
        cursor.close()   // crucial for releasing underlying native memory
    }
}

Example: Disposing RxJava in a Fragment


class RefillFragment : Fragment() {
    private val disposables = CompositeDisposable()

    override fun onStart() {
        super.onStart()
        val d = pharmacyRepo.promotions()
            .subscribe { /* update UI */ }
        disposables.add(d)
    }

    override fun onStop() {
        super.onStop()
        disposables.clear()   // removes all subscriptions
    }
}

---

6. Prevention Before Release

StepHow to ImplementWhy it helps
Code review checklistInclude “Check for static Context”, “Verify resource close”, “Confirm lifecycle‑aware subscriptions”Human inspection catches patterns that static analysis may miss
Static analysisEnable Lint rules for Context leaks, SQLiteDatabase not closed, and AsyncTask misuseAutomated checks flag violations before compilation
CI memory‑leak testsRun SUSATest’s susatest-agent on a nightly build; analyze JUnit XML for “OutOfMemoryError”Early detection of regressions caused by new features
Profile on realistic devicesUse Android Studio Profiler on a 4 GB RAM phone while simulating a 30‑minute pharmacy sessionReal‑world constraints surface leaks that are hard to see on emulators
Unit tests for resource cleanupMock SQLiteDatabase and assert close() is called in finally blocksGuarantees that future refactors don’t remove cleanup code
Documentation & coding standardsPublish a “Memory‑Safety” section in the developer handbookSets expectations for every engineer working on the codebase

By integrating these practices into the development pipeline, pharmacy apps can avoid the most costly memory‑leak scenarios—ensuring that users can browse drug catalogs, track prescriptions, and complete checkouts without unexpected freezes or crashes.

---

Test Your App Autonomously

Upload your APK or URL. SUSA explores like 10 real users — finds bugs, accessibility violations, and security issues. No scripts.

Try SUSA Free