Slide 1

Slide 1 text

Memory Leaks & Performance Considerations: An Android Cookbook Amanda Hinchman-Dominguez Kotlin GDE

Slide 2

Slide 2 text

Loosely based on Programming Android with Kotlin

Slide 3

Slide 3 text

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

Slide 4

Slide 4 text

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.

Slide 5

Slide 5 text

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.

Slide 6

Slide 6 text

Lifting the hood will be a humbling experience - and it's ok! @mvndy_hd #programmingandroidwithkotlin

Slide 7

Slide 7 text

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

Slide 8

Slide 8 text

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

Slide 9

Slide 9 text

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

Slide 10

Slide 10 text

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

Slide 11

Slide 11 text

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

Slide 12

Slide 12 text

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.

Slide 13

Slide 13 text

3. Listener and View members in Fragments nullify @mvndy_hd #programmingandroidwithkotlin

Slide 14

Slide 14 text

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! } }

Slide 15

Slide 15 text

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 } }

Slide 16

Slide 16 text

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

Slide 17

Slide 17 text

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

Slide 18

Slide 18 text

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

Slide 19

Slide 19 text

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

Slide 20

Slide 20 text

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 */ }

Slide 21

Slide 21 text

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 */ } } }

Slide 22

Slide 22 text

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) } }

Slide 23

Slide 23 text

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 */ } }

Slide 24

Slide 24 text

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

Slide 25

Slide 25 text

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

Slide 26

Slide 26 text

No content

Slide 27

Slide 27 text

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

Slide 28

Slide 28 text

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

Slide 29

Slide 29 text

Available on amazon + O'Reilly