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

Android development - Best Practices

Android development - Best Practices

Best Practices for Android developer.
Performance decisions.

Andrew Drobyazko

March 25, 2016
Tweet

More Decks by Andrew Drobyazko

Other Decks in Programming

Transcript

  1. The mobile context • It’s critical to understand that mobile

    devices have severe limitations that are completely different from the world of desktop and server computing.
  2. The mobile context • Moreover, failure to take these constraints

    into account when developing application can lead to poor performance not only for given application, but for the entire device.
  3. 1: Memory • Memory is very limited on mobile devices.

    • This is not true across all devices, it is certainly true on a large portion of mobile ecosystem. • Remember that devices that we, the application developers, use are probably significantly faster than the majority of devices that user either own. • Remember that android uses multiple Activities and Services running in parallel.
  4. 1: Memory • Use as little memory as you can,

    because the entire ecosystem suffers, if you don’t. • Memory == Performance
  5. 2: CPU • Even the high-end mobile CPUs are significantly

    slower than average CPUs on desktop. • Remember that most of users are using slower devices than you own.
  6. 3: GPU • Advices similar to CPU • Uploads can

    be expensive - uploading large Bitmaps can be expensive to the system. • High-Density devices. Lot’s of pixels per inch. Fill rate can be slowed down.
  7. Smooth Frame Rate • The jank-free experience we shoot for

    on Android is sub-16 millisecond (ms) frame times. • That is, applications need to be able to process input, layout, draw, and do anything else required to display the next frame on the screen within that 16 ms window. • This speed allows the system to render at 60 frames per second (fps) during animations and input gestures. Animations must be able to repaint the necessary parts of the screen at 60 fps to achieve perceptibly smooth motion. Less than this rate is detectable by the user as slow, or jerky animation. • Hitting a smooth frame rate means any particular frame needs to execute all rendering code (including the framework processing the rendering commands to send them to the GPU and the GPU drawing them into the buffer which is then shown on the screen) within 16 ms.
  8. Smooth Frame Rate • Note that missing the 16 ms

    barrier doesn’t mean that the app can hit 17ms and thus achieve a slightly smaller rate like 59 fps. • So if your app misses the window for one frame, it will wait until the next. So if it takes your app 17 ms to render a frame, this will be visible to the user as taking twice as long, or only hitting 30 fps for that frame.
  9. Runtime • Prior to the Lollipop release, Android used the

    Dalvik runtime. Although ART was available as a developer option for testing purposes in KitKat, it became the only runtime in Lollipop.
  10. Runtime • Dalvik is a Just-in-Time (JIT) compiler which is

    able to perform some micro-optimizations, but not nearly as many as some other JIT compilers. • ART is an Ahead-of-Time compiler which is able to optimize more aggressively than Dalvik. • However, neither of these runtimes offer the level of optimizations that are common to server and desktop runtime platforms, such as method-inlining and escape analysis. • Since app developers will need to support older releases of Android for some time to come, they should continue to care about the constraints of the current and previous compilers.
  11. Runtime • In general, ART performance over Dalvik is improved

    by anywhere from 30–200+% through several mechanisms. • The compiler in ART performs additional optimizations, for example, improving interface dispatch substantially. • The scope of optimization is a bit larger for ART (it has a method compiler vs. a trace-based one), and allocations are much faster. • Finally, mutator/application threads contend less with garbage collection and are paused less frequently for shorter periods of time.
  12. GC • Garbage Collection (GC) is the process by which

    the runtime frees memory for objects which are no longer referenced. • GC can also be the source of significant performance problems if the amount of work that the GC has to do exceeds the time that it has to do that work while allowing the application to hit a smooth frame rate.
  13. GC • The garbage collectors in Dalvik and ART differ

    substantially. • One major difference is that Dalvik is not a moving collector. This means that all allocated objects will stay at the same location in the heap, which makes it harder and more time-consuming for Dalvik to find free memory for new objects. • Heap fragmentation can also lead to more frequent GC pauses as Dalvik attempts to clear the heap from dead objects. These GC pauses are quite expensive and can easily take 10–20ms on fast devices under normal circumstances. • It’s important to note that the duration of garbage collections is proportional to the number of objects in the heap, which is another reason to avoid allocating objects when possible.
  14. GC • ART brought improved garbage collection dynamics. • For

    one thing, ART is a moving collector; it is able to compact the heap when a long pause in the application won’t impact user experience (for example, when the app is in the background and is not playing audio). • Also, there is a separate heap for large objects like bitmaps, making it faster to find memory for these large objects without wading through the potentially fragmented regular heap. • Pauses in ART are regularly in the realm of 2–3ms.
  15. UI Thread • Many of the performance/jank problems that we

    have seen come from the problem of doing too much on the UI thread. • Android is a single-threaded UI system, where all operations that happen on views, including drawing those views, happen on the single UI thread in the activity. • Anything else that happens on this same thread while the view hierarchy is trying to draw can cause jank because it simply doesn’t have time to do it all in the limited 16 ms it has to achieve a smooth framerate.
  16. UI Thread • In the Lollipop release, the framework introduced

    the “Render Thread,” which is now responsible for sending the actual rendering operations to the GPU. • This thread offloads some of the processing off of the UI thread. • But input, scrolling, and animations still happen on the UI thread, so that thread must still be responsive, even on this more recent release.
  17. Storage • Storage metrics differ on the wide variety of

    Android devices, but it can be slow and limited. • An application that takes up 500MB might be considered small in a desktop environment, but on a device that has only 8GB (which is common for low to medium level devices in 2015), or even 4GB with a removable SD card, to store the entire OS, all applications, and all media downloaded by the user, it’s a significant chunk.
  18. Storage • Storage performance is also a concern. • It

    may be tempting to think that flash memory on a mobile device behaves like a desktop SSD, but these media devices can have drastically different performance.
  19. Network • It is easy for many software developers in

    cities with modern infrastructure to make assumptions about the speed and capabilities of wifi and carrier networks that do not apply in many parts of the world. • Rather than the LTE or 4G data that many of us may expect, or faster wifi speeds from pervasive access points at home and work, much of the world commonly operates on 2G networks and may incur heavy data charges for data transactions. • This leads to one of the two common problems that apps have:
  20. Network • Reliance on fast network speeds: Apps that are

    heavily dependent on large media sources (video, audio, images) may have no choice when those objects are requested. But if there are parts of the app experience that can avoid downloads until specifically requested, that creates a better experience when network capabilities aren’t up to the downloads. • Over-syncing: Just because your app wants up-to-date information, that doesn’t mean that the user needs it, or, more importantly, that doesn’t mean that the device needs to suffer the experience of all apps interacting with the network constantly. This dynamic can easily make the device work too hard as well as prevent it from going to sleep, which leads to an overall horrible battery life experience.
  21. 1 - Memory The use of memory in an application

    can be the single-most important determinant of how well that application behaves, how good the user experience of that application is, and how good the overall device experience is as well.
  22. 1.1 - Avoid allocation in Inner Loops • Allocations are

    unavoidable. • But avoid them when possible, particularly in code paths that will be called frequently, such as during the drawing code that may execute on every frame that a view is rendered.
  23. 1.1 - Avoid allocation in Inner Loops • For example,

    animations may call the onDraw() method of your custom view, so you should avoid allocating objects in that method. • Consider, instead, allocating cached objects that are used temporarily in places where otherwise a new allocation would be necessary for a temporary object.
  24. 1.2 - Avoid allocation when possible • Cached objects: Reused

    objects are useful in situations where the alternative involves constant re-allocations, such as in the example in the Avoid Allocations in Inner Loops section. • For example, if some frequently-called method will always need a Rect object to store an intermediate value, it is better to allocate that Rect once for the class instance, or even statically for the entire process, rather than every time the method is called.
  25. 1.2 - Avoid allocation when possible • Object pools: If

    your code is in constant need for one or more objects of a certain type on a temporary basis, consider using a pool of objects instead of allocating on the fly. • Note that object pools can be tricky to manage. Be wary of concurrency issues here if the objects are stateful and may be accessed from arbitrary threads. • There are also issues with memory pressure (mitigated by using a caching strategy like LruCache), object aliasing, and increased potential for leaks, so be aware of these problems if you do use object pools, and only use them if they are beneficial for your particular situation. • If this strategy is more helpful on older releases or devices with lower memory configurations, consider limiting the use of object pools based on API version and/or checks against isLowRamDevice(). • Example : ContentValues
  26. 1.2 - Avoid allocation when possible • Arrays: ArrayList is

    a convenient collection to use, and does not suffer too much overhead. • It does re-allocate and copy the underlying array as items are added, but setting a reasonable initial capacity can alleviate that overhead. • If your collection does not need to be constantly dynamically resized, consider using an array instead.
  27. 1.2 - Avoid allocation when possible • Android collections classes:

    Unless you require a map for a large set of items, consider using ArrayMap or SimpleArrayMap for data structures instead of HashMap. • These classes are optimized to be more memory efficient and less GC-heavy than HashMap, better matching common use cases for such structures on mobile. • (As a bonus, they also support iterating over entries without an Iterator.) • Also, consider setting the initial capacity of your collection to avoid automatic resizing as it grows to the appropriate size.
  28. 1.2 - Avoid allocation when possible • Methods with mutated

    objects: Instead of having a method return a new object (which would have to be allocated), consider taking an object parameter of that type and mutating that object.
  29. 1.2 - Avoid allocation when possible • Avoid object types

    when primitive types will do: Using Integer instead of int, or Float instead of float, leads to allocation, autoboxing, and more memory allocated for the object itself. • For example, if your method takes an Integer, then code that calls it with an int will automatically incur an allocation due to autoboxing. • Object types are unavoidable with traditional collections classes (thus the advice above to prefer Android collections classes) and generics, but otherwise they should be avoided when similar primitive types exist. • Note that there are some collections classes in Android like SparseIntArray and SparseLongArray that use primitive types instead of object types, to avoid these problems.
  30. 1.3 - Avoid iterators • Explicit (e.g., List.iterator()) or implicit

    (e.g., for (Object o : myObjects)) iteration causes the small allocation of an Iterator object (with the exception of arrays, which can be used with the foreach syntax without causing an Iterator allocation). • This single allocation is not a big deal in practice, but should be avoided in inner loops for the same reasons discussed above. • Meanwhile, iterating more explicitly through the indices of a collection avoids any allocations.
  31. 1.3 - Avoid iterators List<String> data = getData();
 for (int

    i = 0, size = data.size(); i < size; i++) { String s = data.get(i); }
  32. 1.3 - Avoid Enums • Dex is loaded into RAM

    in runtime • 3 Constants «public static final int» takes 124 bytes of Dex
  33. 1.3 - Avoid Enums • Dex is loaded into RAM

    in runtime • 3 Constants «public static final int» takes 124 bytes of Dex • Enum with 3 objects takes 1622 bytes
  34. 1.3 - Avoid Enums • Dex is loaded into RAM

    in runtime • 3 Constants «public static final int» takes 124 bytes of Dex • Enum with 3 objects takes 1622 bytes • 13 times more than constants
  35. 1.3 - Avoid Enums • Dex is loaded into RAM

    in runtime • 3 Constants «public static final int» takes 124 bytes of Dex • Enum with 3 objects takes 1622 bytes • 13 times more than constants • + 20 bytes per every enumeration
  36. 1.3 - Avoid Enums • Enums are typically used to

    represent constants, but they are much more expensive than primitive-type representations, in terms of the code size and the memory allocated for the enum objects. • An occasional enum is not a big deal in terms of the memory it consumes or its allocation costs. And Proguard can, in some situations where it can statically analyze all usages, optimize enums to int values. But enums become a problem when used widely across a large application or, even worse, when used broadly in a library or an API that is then used by many other applications. • Note that using the @IntDef annotation, which is supported by Android Studio and Gradle 1.3+, will give your code build-time type safety (when lint errors are enabled), while retaining the size and performance benefits of using int variables.
  37. 1.4 - Avoid Frameworks and Libraries Not Written for Mobile

    Applications • It is tempting to use frameworks that you may be familiar with from other Java environments. For example, dependency-injection frameworks like Guice is a commonly used Java library. • But since it was not written or optimized specifically for mobile, and for the constraints talked about in this document, then applications using libraries like this will suffer due to the problems described herein.
  38. 1.5 - Avoid static leaks • While static objects can

    be a useful means to avoid temporary allocations, beware of using static variables to cache objects that should not actually persist for the life of the entire process. • Note, in particular, that the lifetime of a static variable (which is equal to the lifetime of the underlying process) is not the same as the lifetime of an Activity. This misunderstanding can, and has, led to leaking Activity objects across configuration changes (in which activities are destroyed and recreated) with static maps that held onto activity instances (directly or indirectly). • Activities are quite expensive and this kind of leak can quickly lead to your application, and the system, running out of memory.
  39. 1.6 - Avoid Excess Static Initialization • Expensive initialization can

    cause performance problems at crucial times in your application’s lifecycle, such as startup, contributing to poor user experience. • Perform initialization on demand to avoid loading code in memory until you actually need it. • @Lazy dependency injections.
  40. 1.7 - Avoid Requesting a Large Heap • Applications can

    request a large heap from the system in the application tag of their manifest… but they shouldn’t. • Requesting a larger heap may be necessary in some rare situations where the type of media content regularly needed by the user easily swamps the default heap limit. • But applications that use this just to avoid having to more carefully manage their memory and resources are only causing problems for the overall user experience of the device. • Applications requesting a larger heap will necessarily cause less memory to be available for other processes on the device, necessitating other applications being killed and restarted as the user switches between activities on the device.
  41. 1.8 - Avoid Running Services Longer than Necessary • Every

    process on the system takes up limited resources. If you don’t need your service to run all the time (and ideally you don’t), shut it down whenever possible. • Use IntentService to implement a service that automatically shuts down when its work queue is empty.
  42. 1.9 - Optimize for Code Size • Lean applications are

    fast applications. The less code you have to load, the less time it will take to download your application and the faster your application will start and initialize. Here are a few suggestions: • Use Proguard to strip out unused code. If you are building with Gradle, you can also strip out unused resources, including those from libraries on which your application depends. • Carefully consider your library dependencies. For example, don’t use a large library full of different collections options when all you need is a single, specific kind of data structure. • Make sure to understand the cost of any code which is automatically generated. • Prefer simple, direct solutions to problems rather than creating a lot of infrastructure and abstractions to solve those problems.
  43. 2.1 - Avoid Expensive Operations During Animations and User Interaction

    • As mentioned in the UI Thread section in the Context chapter, expensive operations which happen on the UI thread can cause hiccups in the rendering process. • This, in turn, causes problems for animations, which are dependent on this rendering process for every frame. • This means that it is even more important than usual to avoid expensive operations on the UI thread while there are active animations.
  44. 2.1 - Avoid Expensive Operations During Animations and User Interaction

    • Here are some common situations to be avoided: • Layout: Measurement and layout is an expensive operation, and the more complicated the view hierarchy, the more expensive it can be. Measurement and layout happens on the UI thread (as does any operation that needs to manipulate the views of an activity). This means that an application that is currently trying to run a smooth animation and is then told to layout will do both things on the same thread, and the animation will suffer. • To avoid this situation when layout needs to occur, either run the layout operations before animations start or delay them until the animations are complete. Also, try to animate only properties that do not trigger layout. For example, View’s translationX and translationY properties affect post-layout properties. • LayoutParams properties, on the other hand, require a layout to take effect, so animating those properties will cause jank in reasonably complex UIs.
  45. 2.1 - Avoid Expensive Operations During Animations and User Interaction

    • Here are some common situations to be avoided: • Inflation: View inflation can only happen on the UI thread, and it can be a expensive operation (the larger the view hierarchy, the more expensive the operation). Inflation can happen by manually inflating a view or view hierarchy, or by implicitly inflating it by launching a separate activity. This will happen on the same UI thread as your main activity, and will cause animations to halt while that new activity is inflated. • To avoid this situation, wait to inflate the views or launch the activities until the current animation is finished. Or for avoiding inflation-related jank while scrolling a list with different view types, consider pre- inflating views of those types. For example, RecyclerView supports priming the RecycledViewPool with item views of specific types.
  46. 2.2 - Launch Fast • View inflation is not cheap.

    It’s not just the parsing of the resource data, but also the instantiation of potentially many views and their potentially expensive content along the way, including decoding bitmaps, running layout, and drawing for the first time. • If you have parts of your UI that do not need to be visible on first launch, don’t inflate them. Use ViewStub as a placeholder for sub-hierarchies that can be optionally inflated later. • Avoid expensive operations like decoding large bitmaps whenever possible. • Avoid memory churn due to allocations and garbage collections when possible. • Use the tools to monitor your startup time and eliminate bottlenecks when you find them.
  47. 2.3 - Avoid Complex View Hierarchies • The more views

    that are in your hierarchy, the longer it will take to do common operations, including inflation, layout, and rendering (in addition to the potentially expensive memory implications of unused content; Views are expensive by themselves, in addition to any additional data brought in by custom views). • Find cheaper ways to represent nested content. • One approach to avoiding complex nested hierarchies is to use custom views or custom layouts in some situations; it may be cheaper for a single view to draw several pieces of text and icons rather than have a series of nested ViewGroups to accomplish this. • One rule of thumb for deciding how to combine multiple elements lies in the interaction model: if the user can interact with an element (e.g., via touch, focus, etc.), then that element should be its own view rather than being combined with other, separate elements.
  48. 2.4 - Avoid RelativeLayout Near the Top of the View

    Hierarchy • RelativeLayout is a very convenient layout to use, because it allows developers to specify how content should be placed relative to other content. • In many situations, this is necessary and may be the best solution for the job. • However, it is important to understand that RelativeLayout is an expensive solution, because it requires two measurement passes to ensure that it has handled all of the layout relationships correctly. • Moreover, this problem compounds with every additional RelativeLayout throughout the hierarchy. • Imagine a RelativeLayout at the top of your view hierarchy; this essentially doubles the measurement work on your entire view hierarchy. Now imagine another RelativeLayout as one of the children of that first one — this doubles again the measurement passes that happen under it, requiring four measurement passes for all of the views in its sub-hierarchy. • Use a different type of layout for situations that do not require the capabilities of RelativeLayout, such as LinearLayout or even a custom layout. Or for situations in which relative alignment of child views is necessary, consider the more optimized GridLayout, which pre-processes the child view relationships and avoids the double-measurement problem.
  49. 2.5 - Minimize Wakeups • BroadcastReceivers are used to receive

    information and events from other applications that your application may want to respond to. • But responding to more of these items than your application actually needs will cause your application to wake up too often, causing overall system performance problems and resource drain. • Consider disabling BroadcastReceivers when your application does not care about the results, and carefully choose the Intents that your application really needs to respond to.
  50. 2.6 - Develop for the Low End • Most of

    your users will have lower-end devices than you probably carry for your everyday phone, by virtue of their being either older or cheaper than yours. • It is important to develop for this market, and not miss important performance nuances due to higher performance metrics of your development device glossing over elements of your application that would be hard to miss on lower- end devices. • Your primary device should not be the fastest/latest one available, and you should always have a variety of devices to be able to try out different speeds and form factors to ensure that your application runs adequately across all of them. • Other factors of low-end devices that are important to test against include small RAM sizes and small screen sizes. For example, 512MB is a common low-end configuration, and many Android devices have screen resolutions of 768x480 or less.
  51. 2.7 - Measure Performance • There is a plethora of

    tools available on Android. • Use them to track important performance-related information about your application, including rendering performance (are you able to hit 60 fps?), memory allocations (are constant allocations triggering garbage collections leading to jank during animations?), and startup performance (are you doing too much at startup leading to long wait times for the user when your app first comes up?). Find the problems. Fix them.
  52. 3.1 - Don’t over-sync • Sending data to and from

    the cloud is one of the largest consumers of battery. • The problem is not that any particular network transaction kills the device, but rather the sheer number of background applications and services making these requests and the side effect on the inability of the device to sleep (or for the radio to enter low power mode) for any reasonable length of time causes a significant drain on the battery in general. • If you don’t need the data immediately, don’t get it. • If you need the data sometime in the future, batch it up with other system requests with JobScheduler or GCM Network Manager.
  53. 3.1 - Don’t over-sync • Use Google Cloud Messaging (GCM).

    Do not establish new persistent connections to the device. • Use JobScheduler (on API 21 and later) or GCM Network Manager (which works across many releases, using JobScheduler internally on API 21 and later) to benefit from batching and coalescing from other sync requests on the device. These APIs can also be used to ensure that some operations only happen when the device is in an appropriate idle state. • Do not poll. Ever. • Only sync what you need to. Data syncing has been found to be one of the biggest culprits of battery usage and overall system health, so app developers need to think carefully about the type of data that really needs to be synced, as well as how often it should be synced for a good user experience.
  54. 3.2 - Don’t Make Assumptions about the Network • When

    making network calls, be sure to call NetworkInfo.isConnected() to ensure connectivity.
  55. 3.3 - Develop for low-end networks. • The Develop for

    the Low End rule in the Performance chapter is as true for network performance as it is for raw device hardware performance. • If your application is dependent upon network performance for content (such as a video or music application), then you should develop on a device that has a slower network connection, just to understand the implications in markets that do not have easy access to fast network speeds. • If your application can get much of the content across with text (such as messaging or social apps), it is important to be able to make that work without being dependent on slower network times for associated media components. • 2G networks, in particular, are important to test against since this is a slow network speed that is very prevalent in some markets.
  56. 4.1 - Serialization • Parcelable • Parcelable is Android’s IPC

    serialization format, or rather, it is an interface for writing out your data so it can be sent over Binder. It does have limits: • It is not safe to write Parcels to disk. • You can implement your own Parcelables, but if the receiving process doesn’t have access to the same classes, unparceling will fail. (This is true of Parcels you send to the framework, too.) • Some types of objects (bitmaps and CursorWindows) placed into Parcels are instead written to ashmem (Android shared memory) files, and a file descriptor is written into the Parcel in their place. This is an important performance optimization but can hide the true memory cost of big Parcels (and will take up space in ashmem until unparceled).
  57. 4.2 - Avoid Java Serialization • Serializable, ObjectOutputStream, and friends

    will all work, but these approaches have very significant overhead, which increases with the raw number of fields being serialized. • Use Parcelable for anything that needs to be exchanged between running processes. • SharedPreferences is a good, quick key-value store for situations that do not require large numbers of elements. • Use SQLite for more complex, row-oriented data.
  58. 4.3 - Use primitive types • Always use primitive types

    (int, float, boolean, etc.) instead of their Object equivalents (Integer, Float, Boolean, etc.) when you have the choice. • The overhead in terms of memory (object instances always cost more than their primitive type equivalents) and performance (object instances require more runtime overhead to access the underlying value) means that you should always use primitive types when you can on Android. • Generics and some data structures (e.g., the traditional collections classes) require object types. But note that there are various collections classes on Android (ArrayMap, SimpleArrayMap, SparseArray, SparseBooleanArray, SparseIntArray, SparseLongArray, and LongSparseArray) that avoid the object type requirement for specific use cases.
  59. Storage This section covers many practices that are specific to

    how data is stored and accessed on the platform.
  60. 5.1 - Avoid Hard-coded File Paths • Don’t hardcode global

    paths like “/sdcard”, use Environment.getExternalStorageDirectory() and related methods instead. • Don’t hardcode application paths like “/data/ data/myapp/databases”, use Context.getDatabasePath(), Context.getFilesDir() and related methods instead.
  61. 5.2 - Persist relative path only • If you need

    to persist a path, persist only its relative location to protect against path changes. For example, when your app is backed up and restored onto a new device, the data paths may be slightly different. • For example, the path returned by Context.getFilesDir() may change between devices, users, or configurations. Thus, it’s safest to persist only relative paths and construct the absolute path only when needed at runtime.
  62. 5.3 - Let User Choose Content Storage Location • Devices

    often have more than one storage location, including multiple SD cards, USB drives, and cloud storage backends. Give the user the choice of where to open/save their data by using the Storage Access Framework. • You can launch simple intents to prompt the user to open or save a file, and receive back a content:// URI ready to be used for data storage. The DocumentFile support library class can make it easier to adapt existing code that expects a traditional File-style API.
  63. User Interface This section covers important details for patterns in

    UI development, specifically around performance and user experience.
  64. Cache data locally • Caching data locally is one of

    the most important things you can do to make your users feel that your application runs fast. • Aside from caching big files like images, you should also cache the data fetched from the server. When feasible, you should create a local database that keeps the data in a meaningful way (not just as a serialized server response). • Whenever possible, update your UI from local data. This will help with data consistency and will also greatly improve user experience as your app will be less dependent on the network.
  65. Systrace • Systrace is a powerful tool that shows the

    occurrence and duration of various events in the system on a timeline. • You run the tool to capture data, then open the resulting trace file in a browser to analyze the results. • The tool can run from Android Studio or from the command line via $SDK_ROOT/platform-tools/ systrace/systrace.py.
  66. AllocationTracker • AllocationTracker can be run from the ddms tool

    or directly from inside Android Studio. • It allows you to find out about all allocations that occur between the times when you start and then capture the data. • This tool is invaluable for finding out whether and where you are allocating during times when you generally should not. Any regular allocations during animation frames should be investigated.
  67. Traceview • Traceview, run from either ddms or Android Studio,

    is a method profiler than can be run in either trace mode (which traces every method call) or sample mode (which samples at specific time intervals). • Trace mode is a good way to find out the entire code path that your application took during the traced interval, along with a rough idea of the relative time that the various methods took. • However, the process of instrumentation is relatively expensive, so this view can give an inflated impression of how long simple method calls took. Sample mode avoids most of that overhead since it is not measuring every call, but it will miss details of exact code flow since it only samples at specific intervals. • Also, it’s important to note that sample mode intervals will not always be evenly spaced (because the runtime can only sample at GC safe points), it cannot sample in inlined methods, and it may sample JNI methods proportionally more than Java methods.
  68. Hierarchy Viewer • Hierarchy Viewer, run either as a standalone

    tool or from Android Studio, shows the entire view hierarchy of the selected application, and allows you to see the property values for any view. • This tool is a great way to see how complex your hierarchy is. Are you keeping around too many views that are not visible much of the time? Do you have a deep container hierarchy with single-purpose layouts that could be combined to avoid this complexity? Using this tool is a good way to diagnose these problems.
  69. • Think twice before adding any third party library, it’s

    a really serious commitment; • If the user can’t see it, don’t draw it!; • Don’t use a database unless you really need to; • Hitting the 65k method count mark is gonna happen fast, I mean really fast! And multidexing can save you; • RxJava is the best alternative to AsyncTasks and so much more; • Retrofit is the best networking library there is; • Shorten your code with Retrolambda; • Combine RxJava with Retrofit and Retrolambda for maximum awesomeness!; • I use EventBus and it’s great, but I don’t use it too much because the codebase would get really messy; • Package by Feature, not layers; • Move everything off the application thread;
  70. • lint your views to help you optimize the layouts

    and layout hierarchies so you can identify redundant views that could perhaps be removed; • If you’re using gradle, speed it up anyway you can; • Do profile reports of your builds to see what is taking the build time; • Use a well known architecture; • Testing takes time but it’s faster and more robust than coding without tests once you’ve got the hang of it; • Use dependency injection to make your app more modular and therefore easier to test; • Listening to fragmented podcast will be great for you; • Never use your personal email for your android market publisher account; • Always use appropriate input types; • Use analytics to find usage patterns and isolate bugs; • Stay on top of new libraries (use dryrun to test them out faster); • Your services should do what they need to do and die as quickly as possible;
  71. • Use Account Manager to suggest login usernames and email

    addresses; • Use CI (Continuous Integration) to build and distribute your beta and production .apk’s; • Don’t run your own CI server, maintaining the server is time consuming because of disk space/security issues/updating the server to protect from SSL attacks, etc. Use circleci, travis or shippable, they’re cheap and it’s one less thing to worry about; • Automate your deployments to the playstore; • If a library is massive and you are only using a small subset of its functions you should find an alternative smaller option (rely on proguard for instance); • Don’t use more modules than you actually need. If that modules are not constantly modified, it’s important to have into consideration that the time needed to compile them from scratch (CI builds are a good example), or even to check if the previous individual module build is up-to-date, can be up to almost 4x greater than to simply load that dependency as a binary .jar/.aar. • Start thinking about ditching PNGs for SVGs;