Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Memory Leaks & Performance Considerations: A Cookbook - Droidcon 2022 | Berlin | NYC

Memory Leaks & Performance Considerations: A Cookbook - Droidcon 2022 | Berlin | NYC

Out in the wild, Android faces real-life challenges that affect performance and battery life. For example, not everyone has unlimited data in their mobile plans, or reliable connectivity. The reality is that Android apps must compete with one another for limited resources. From the newly published O'Reilly book "Programming Android with Kotlin: Achieving Structured Concurrency with Coroutines", this talk examines a range of memory leaks in concurrency clashing lifecycles, network data format, and more. Performance considerations allow you to examine concerns that may impact your application’s ability to scale. If you can use any of these strategies as “low-hanging fruit” in your code base, it’s well worth going for the biggest win with the smallest amount of effort.

mvndy_hd

July 08, 2022
Tweet

More Decks by mvndy_hd

Other Decks in Technology

Transcript

  1. What are memory leaks? A memory leak occurs when: •

    When the heap holds to allocated memory longer than necessary • When an object is allocated in memory but is unreachable for the running program • Can be found in source code and 3rd party code and framework code @mvndy_hd #programmingandroidwithkotlin
  2. Memory leaks are everywhere! They're in your apps. They're in

    your IDEs. They're in your phones. We're going to creating them. And we're going to keep finding them and fixing them.
  3. Why do memory leaks matter? • As of 2019 smartphones

    running the Android operating system hold just over 87 percent share of the global market 1 • Android has very limited resources in hardware capacity • Application quality depends on user experience @mvndy_hd #programmingandroidwithkotlin 1https://www.statista.com/statistics/272307/market-share-forecast-for-smartphone-operating-systems/#:~:text=Smartphones%20running%20the%20Android%20o perating,percent%20share%20of%20the%20market.
  4. Lifting the hood will be a humbling experience - and

    it's ok! @mvndy_hd #programmingandroidwithkotlin
  5. Android components and clashing lifecycles (ノ°益°)ノ 1. Statically-saved Android UI

    components Remove static referencing 2. Android UI interaction/DI within non-Android classes Move UI interaction to Activity/Fragment level 3. Listeners and View members in Fragments Nullify references in Fragment::onDestroyView 4. Playing with Bitmaps Reduce, reuse, recycle! @mvndy_hd #programmingandroidwithkotlin What you're doing How to avoid problems
  6. 1. Statically-saved Android components remove • Statically-saved references are stored

    permanently in the JVM • Avoid accidentally saving an Activity | View | Context | Fragment ◦ Java - static ◦ Kotlin - companion object @mvndy_hd #programmingandroidwithkotlin
  7. 2. Android UI interaction within non-Android classes move UI interaction

    out to the UI layer class TileMapPresenter( val activity: TileMapActivity, */ NO! private val repository: TileMapRepository ) { ** removed for brevity */ fun onTileLoadingSuccess(url: String) { activity.tile_image.setImageUrl(url) */ ALSO NO! } } @mvndy_hd #programmingandroidwithkotlin
  8. 2. Android UI interaction within non-Android classes move UI interaction

    out to the UI layer interface TileMapView { fun onTileLoadingSuccess(url: String) } class TileMapActivity: AppCompatActivity, TileMapView { ** removed for brevity */ override fun onTileLoadingSuccess(url: String) { */ interact with View element directly in Activity tile_image.setImageUrl(url) } } @mvndy_hd #programmingandroidwithkotlin
  9. 2. Android UI interaction within non-Android classes move UI interaction

    out to the UI layer class TileMapPresenter( val view: TileMapView, private val repository: TileMapRepository ) { ** removed for brevity */ fun onTileLoadingSuccess(url: String) { view.onTileLoadingSuccess(url) } } @mvndy_hd #programmingandroidwithkotlin
  10. 3. Listener and View members in Fragments nullify @mvndy_hd #programmingandroidwithkotlin

    • Be wary of third-party libraries that may reference the use of an Android resource • The Fragment's View (but not the Fragment itself) is destroyed when a Fragment is put on the backstack² ◦ Developers are expected to clear refs for views in Fragment::onDestroyView ¹ “Issue 145468285: Memory Leak with Nested Fragments and Data Binding.” Issuetracker.google.com, Google, 2 Dec. 2019, 09:01am PT, issuetracker.google.com/issues/145468285#comment6.
  11. 3. Listener and View members in Fragments nullify @mvndy_hd #programmingandroidwithkotlin

    class MapFragment : Fragment() { private var mapView: MapView? = null private var positionMarker: PositionMarker? = null /* removed for brevity */ override fun onDestroyView() { super.onDestroyView() */ MEMORY LEAK! } }
  12. 3. Listener and View members in Fragments nullify @mvndy_hd #programmingandroidwithkotlin

    class MapFragment : Fragment() { private var mapView: MapView? = null private var positionMarker: PositionMarker? = null ** removed for brevity */ override fun onDestroyView() { super.onDestroyView() mapView*.destroy() mapView = null positionMarker = null } }
  13. 4. Playing with Bitmaps reduce, reuse, recycle class RenderMapTileMarkers: AppCompatActivity

    { private Bitmap tileMarkerBitmap; private Bitmap tileMarkerSelectedBitmap; ** removed for brevity */ override fun onDestroy() { tileMarkerBitmap*.recycle() tileMarkerSelectedBitmap*.recycle() super.onDestroy() } } @mvndy_hd #programmingandroidwithkotlin
  14. Clashing lifecycles and threading (┛ಠ_ಠ)┛彡┻━┻ 5. Android UI references in

    background threads Remove 6. Saving thread primitives as a static reference Remove static keyword 7. Non-static inner classes within Fragment | Activity | View Remove 8. Rx Subscriptions Clear subscriptions @mvndy_hd #programmingandroidwithkotlin What you're doing How to avoid problems
  15. UI Thread Activity A Activity B Worker Thread Activity A

    Timeline @mvndy_hd #programmingandroidwithkotlin 5. Android UI references to background threading
  16. UI Thread Activity A Activity B Activity A Worker Thread

    Activity A Timeline Device rotated Activity A Thread attempts to reference dead Activity @mvndy_hd #programmingandroidwithkotlin 5. Android UI references to background threading
  17. 6. Saving threading primitives as a static reference remove @mvndy_hd

    #programmingandroidwithkotlin class TopologicalProcessor { static ThreadPoolExecutor tileMapThreadPoolExecutor = new ThreadPoolExecutor( corePoolSize = 3, maximumPoolSize = 3, keepAliveTime = 0L, unit = TimeUnit.MILLISECONDS, workQueue = new LinkedBlockingQueue*>()); ** removed for brevity */ }
  18. 7. Non-static inner classes within View/Activity remove @mvndy_hd #programmingandroidwithkotlin class

    TileImageView : ImageView { ** removed for brevity */ private class TileImageLoadedCallback { fun onSuccess(source: ImageView) { ** threading kicked off */ } fun onError(source: ImageView) { ** removed for brevity */ } } }
  19. 7. Non-static inner classes within View/Activity remove @mvndy_hd #programmingandroidwithkotlin class

    TileImageView( val listener: TileImageView.TileImageLoadedCallback ) : ImageView { // make the listener calls here to trigger implementation in Activity interface TileImageLoadedCallback { fun onSuccess(source: ImageView) fun onError(source: ImageView) } }
  20. 7. Non-static inner classes within View/Activity remove @mvndy_hd #programmingandroidwithkotlin class

    MapTileActivity : AppCompatActivity, TileImageView.TileImageLoadedCallback { var listener: TileImageView.TileImageLoadedCallback? = null override fun onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); listener = this as TileImageView.TileImageLoadedCallback */ set the listener } ** removed for brevity */ override fun onSuccess(source: ImageView) { ** threading kicked off */ } override fun onError(source: ImageView) { ** removed for brevity */ } }
  21. Performance considerations 8. Application Context v. Global context Use the

    right context 9. View hierarchy Make as flat as possible or use Compose @mvndy_hd #programmingandroidwithkotlin What you're doing How to avoid problems
  22. 8. Activity context v. Application context @mvndy_hd #programmingandroidwithkotlin • Activity

    Context: UI context retrieved within the scope of an Activity or Fragment context ◦ Accessing UI-related components i.e. inflating views, showing dialogs • Application Context: global application context ◦ Accessing shared resources in the Android OS i.e. SharedPreferences, URI
  23. 9. View Hierarchy Make as flat as possible or use

    Compose @mvndy_hd #programmingandroidwithkotlin • If you got these in your source code, REPLACE THEM ◦ RelativeLayout ◦ LinearLayout (horizontal orientation) ◦ Gridlayout • Use ConstraintLayout instead
  24. Memory leaks as a grassroots movement • All hands on

    deck ◦ Involve your teammates and QA • Watch your analytics and record them ◦ How many crashes are users experiencing in a 30 day period? • Profile and record your app before your fixes and after the fixes ◦ Startup time ◦ UI rendering/response time ◦ Memory/CPU consummption @mvndy_hd #programmingandroidwithkotlin