{"id":29081,"date":"2017-01-26T10:52:58","date_gmt":"2017-01-26T18:52:58","guid":{"rendered":"https:\/\/blog.xamarin.com\/?p=29081"},"modified":"2017-01-26T10:52:58","modified_gmt":"2017-01-26T18:52:58","slug":"tips-for-creating-a-smooth-and-fluid-android-ui","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/xamarin\/tips-for-creating-a-smooth-and-fluid-android-ui\/","title":{"rendered":"Tips for Creating a Smooth and Fluid Android UI"},"content":{"rendered":"<p>\t\t\t\tAs developers, we want to build mobile apps that are not only beautiful and feature-rich, but also high-performance. We often think about ways we can optimize our network calls and background operations to be speedy, but users typically equate performance with the main component of a mobile app they interact with: the user interface. When it comes to UI performance, a smooth experience can often be defined as a consistent 60 frames per second (fps), meaning we have to render a frame every 16 ms. In this blog post, you&#8217;ll learn some tips and tricks for identifying and debugging your app&#8217;s user interface for maximum performance.<\/p>\n<h2>Why 60 fps?<\/h2>\n<p>App animations and transitions are just a series of still images, or frames. The trick is to display these individual frames fast enough to trick the human brain into thinking a fluid motion is occurring. 60 frames per second is the sweet spot for smooth motion, but drops in framerates will be obvious to the human eye, which is explained more by Colt McAnlis in <a href=\"https:\/\/www.youtube.com\/watch?v=CaMTIgxCSqU\" target=\"_blank\">this video<\/a>. This gives our whole frame process a little less than <em>16 ms<\/em> to fully draw a frame on the screen. Now that we know what we want to achieve, how do we go about ensuring our app meets this performance requirement?<\/p>\n<h2>Identifying the Problem<\/h2>\n<p>Have you ever experienced anything like this with your application?<\/p>\n<p><img decoding=\"async\" class=\"aligncenter\" src=\"http:\/\/i.imgur.com\/e7imzwm.gif\" alt=\"\" \/>Credit: <a href=\"https:\/\/github.com\/dougsillars\">Doug Sillars<\/a> for the great example.Scrolling is extremely choppy and, as a result, the user interface doesn&#8217;t seem responsive to the user. During the time that the application is unresponsive and choppy, if the user attempts to interact with the application and it takes longer than <strong>5000 ms (5 seconds)<\/strong> for the application to respond, the Android OS will give us this lovely message:<\/p>\n<p><img decoding=\"async\" class=\"aligncenter\" title=\"\" src=\"http:\/\/i.imgur.com\/9iMFlmB.png\" alt=\"\" \/><\/p>\n<p>Or maybe you&#8217;ve seen the following in your logs:<\/p>\n<pre>I\/Choreographer(1200): Skipped 60 frames! The application may be doing too much work on its main thread.<\/pre>\n<p>This is the Android Choreographer warning you in advance that your application is not performing with the buttery smooth experience we&#8217;re aiming for. In fact, it&#8217;s telling you that you&#8217;re skipping frames and not providing your users a <strong>60 FPS<\/strong> experience. We can do better! Here&#8217;s the basic process for identifying and fixing UI performance issues:<\/p>\n<ul>\n<li>Measure overall UI performance with <strong>Systrace<\/strong> to get a baseline.<\/li>\n<li>Use <strong>Hierarchy Viewer<\/strong> to identify and flatten view hierarchies.<\/li>\n<li>Reduce overdraw by flattening layouts and drawing less pixels on screen.<\/li>\n<li>Enable <strong>StrictMode<\/strong> to identify and make fewer potentially blocking calls on the main UI thread.<\/li>\n<\/ul>\n<h2>Getting Started<\/h2>\n<h3>Counting Skipped Frames<\/h3>\n<p>Android refers to these skipped or delayed frames as <a href=\"https:\/\/developer.android.com\/training\/testing\/performance.html\" target=\"_blank\">janky frames<\/a>. After your application has been running and interacted with to reproduce janky frames, run the following command:<\/p>\n<p><code>adb shell dumpsys gfxinfo [PACKAGE_NAME]<\/code><\/p>\n<p>This command will output something similar to the following:<\/p>\n<pre>Stats since: 524615985046231ns\nTotal frames rendered: 8325\nJanky frames: 729 (8.76%)\n90th percentile: 13ms\n95th percentile: 20ms\n99th percentile: 73ms\nNumber Missed Vsync: 294\nNumber High input latency: 47\nNumber Slow UI thread: 502\nNumber Slow bitmap uploads: 44\nNumber Slow issue draw commands: 135\n<\/pre>\n<p>Numbers never lie, so it&#8217;s apparent we have some janky frames (~9%). Let&#8217;s dig in further with the Systrace tool.<\/p>\n<h2>Using the Systrace Tool<\/h2>\n<p>Systrace gives you an overview of the whole Android system and tells you what&#8217;s going on at specific intervals\u00a0of time.<\/p>\n<h3>Getting Started<\/h3>\n<p>Let&#8217;s start a Systrace on our device. First open <a href=\"https:\/\/developer.android.com\/studio\/profile\/monitor.html\" target=\"_blank\">Android Device Monitor<\/a> to get started. Once inside, we see the option to start a Systrace:<\/p>\n<p><img decoding=\"async\" class=\"alignnone size-full wp-image-29566\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/44\/2019\/03\/12.08.2016-13.30.png\" alt=\"12.08.2016-13.30\" width=\"572\" height=\"84\" \/><\/p>\n<p>We&#8217;re then\u00a0prompted with a request for what we would like to trace. Selecting all of the <strong>Commonly Used Tags<\/strong> and leaving the <strong>Advanced Options<\/strong> deselected will generate a <code>trace.html<\/code> file for the Trace Duration that you specify.<\/p>\n<p>Here&#8217;s an example of a <code>Systrace<\/code> over 30 seconds:<\/p>\n<p><img decoding=\"async\" class=\"aligncenter size-full wp-image-29570\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/44\/2019\/03\/jank1.png\" alt=\"jank1\" width=\"1920\" height=\"703\" \/><\/p>\n<p>Okay great! But what does this all mean? Let&#8217;s take it a step at a time.<\/p>\n<p>We can see that we have a number of alerts identified by the circular warning signs in our Alerts row. Taking a look at our process, we can see a row of frames. Frames colored yellow or red are those that exceed our 16 ms render time.<\/p>\n<p>Clicking on the alert will show us an overview of the issue at the bottom of our trace window. We can drill down to the specific frame(s) in question by clicking on the correlating frame icon in the process, or by simply clicking <em>Frame<\/em> in the alert description. This will show us the amount of time spent during this step:<\/p>\n<p><img decoding=\"async\" class=\"aligncenter\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/44\/2019\/03\/blankjank.jpg\" alt=\"\" \/><\/p>\n<p>Finally, you can mark that frame using the <code>m<\/code> hotkey and zoom in to the affected frame to see what work is being done on various threads, such as <code>CPU Threads<\/code>, the\u00a0<code>UI Thread,<\/code> and the\u00a0<code>RenderThread<\/code>.<\/p>\n<p><img decoding=\"async\" class=\"aligncenter\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/44\/2019\/03\/jank6-1.jpg\" alt=\"\" \/><\/p>\n<p>This example is showing a <strong>yellow<\/strong> frame, but we can get a general idea of what a performant\u00a0frame might look like.<\/p>\n<p>From our <strong>Alert<\/strong>, we can see that we might want to avoid significant work in <code>View.OnDraw()<\/code> or <code>Drawable.Draw()<\/code>, especially allocations or drawing to <code>Bitmaps<\/code>. Google gives us a tip to watch in <a href=\"https:\/\/www.youtube.com\/watch?v=HAK5acHQ53E\" target=\"_blank\">this video<\/a>.<\/p>\n<p>For our <strong>Frame<\/strong>, we can see that our <code>Adapter.GetView()<\/code> should recycle the incoming <code>View<\/code> instead of creating a new one.<\/p>\n<h3>Systrace Terms<\/h3>\n<h4>Frame Color<\/h4>\n<ul>\n<li>Green: Great performance<\/li>\n<li>Yellow: Less than ideal performance<\/li>\n<li>Red: Bad performance<\/li>\n<\/ul>\n<h5>Scheduling Delay<\/h5>\n<p>Scheduling delays happen when the thread that is processing a specific slice wasn&#8217;t scheduled on the CPU for a long amount of time. Thus it takes longer for this thread to fire up and complete.<\/p>\n<h5>Wall Duration<\/h5>\n<p>The amount of time that passed from the moment a slice is started until it&#8217;s finished.<\/p>\n<h5>CPU Duration<\/h5>\n<p>The amount of time the CPU spent processing that slice.<\/p>\n<h2>Overdraw<\/h2>\n<p>Overdraw is excessive redraw of our user interface, resulting in a slow user interface experience (for layout inflation, animations, etc.). Debugging overdraw is fairly easy; we can enable diagnostics for this in your Android device&#8217;s settings by going to Settings -&gt; Developer Options -&gt; Debug GPU overdraw -&gt; Show overdraw areas.<\/p>\n<p>Once enabled, you will see many different colors on your layouts.<\/p>\n<ul>\n<li>White: No overdraw<\/li>\n<li>Blue: Pixels that are 1x overdrawn<\/li>\n<li>Green: Pixels that are 2x overdrawn<\/li>\n<li>Pink: Pixels that are 3x overdrawn<\/li>\n<li>Red: Pixels that are 4x overdrawn<\/li>\n<\/ul>\n<p>Bad Layout Performance:\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0Good Layout Performance:\n<img decoding=\"async\" style=\"width: 302px;height: 533px;margin: 20px 20px\" title=\"\" src=\"http:\/\/i.imgur.com\/Nm9esfG.png\" alt=\"\" align=\"left\" \/><img decoding=\"async\" style=\"width: 302px;height: 533px;margin: 20px 20px\" title=\"\" src=\"http:\/\/i.imgur.com\/KuoINc2.png\" alt=\"\" \/><\/p>\n<p>You can then identify why this layout might be overdrawing so much via a tool such as <a href=\"https:\/\/developer.android.com\/studio\/profile\/optimize-ui.html#HierarchyViewer\" target=\"_blank\">Hierarchy Viewer<\/a>.<\/p>\n<h2>Hierarchy Viewer<\/h2>\n<p>The first thing we want to know regarding our View Hierarchy is how deep or nested our layouts are. The more nested layouts are, the more GPU overdraw we&#8217;ll typically have.<\/p>\n<p>Let&#8217;s take the previous example of bad layout performance:<\/p>\n<p><img decoding=\"async\" class=\"aligncenter title=\" src=\"http:\/\/i.imgur.com\/VjPu4P1.png\" alt=\"\" width=\"503\" height=\"444\" \/><\/p>\n<p>We can see that we are four layers deep, which is less than ideal for our <code>ListView<\/code>. We really need to flatten this user interface out.<\/p>\n<p><img decoding=\"async\" class=\"aligncenter title=\" src=\"http:\/\/i.imgur.com\/VWDa4ux.png\" alt=\"\" width=\"500\" height=\"444\" \/><\/p>\n<p>Okay that&#8217;s a little better! We&#8217;re only three layers deep now. However, UI performance still isn&#8217;t great. Let&#8217;s try to remove one more layer.<\/p>\n<p><img decoding=\"async\" class=\"aligncenter title=\" src=\"http:\/\/i.imgur.com\/m1XHLRi.png\" alt=\"\" width=\"500\" height=\"444\" \/><\/p>\n<p>Much better! We just optimized our whole view hierarchy and will reap the performance benefits. Let&#8217;s take a look at the overall <code>Layout<\/code> and <code>Draw<\/code> timings for proof.<\/p>\n<ul>\n<li>Bad Layout: 31 views \/ <strong>Layout: 0.754 ms<\/strong> \/ <strong>Draw: 7.273 ms<\/strong><\/li>\n<li>Better Layout: 26 views \/ <strong>Layout: 0.474 ms<\/strong> \/ <strong>Draw: 6.191 ms<\/strong><\/li>\n<li>Good Layout: 17 views \/ <strong>Layout: 0.474 ms<\/strong> \/ <strong>Draw: 1.888 ms<\/strong><\/li>\n<\/ul>\n<h2>StrictMode<\/h2>\n<p>StrictMode is a very useful tool for battle testing your application. Enabling StrictMode ensures you&#8217;re not putting extra work in certain places in your application, such as disk reads, disk writes, and network calls, to help you deliver a great user experience. In a nutshell, StrictMode does the following:<\/p>\n<ol>\n<li>Logs a message to <code>LogCat<\/code> under the <code>StrictMode<\/code> tag.<\/li>\n<li>Displays a dialog (If <code>PenaltyLog()<\/code> is enabled).<\/li>\n<li>Crashes your application (If <code>PenaltyDeath()<\/code> is enabled).<\/li>\n<\/ol>\n<p>This can help you determine what type of policies you&#8217;d like to battle test your application against.<\/p>\n<p>You can enable StrictMode in your Android application by adding something like the following to your <code>Activity<\/code>:<\/p>\n<pre><code>protected override void OnCreate(Bundle bundle)\n{\n    StrictMode.SetThreadPolicy(new StrictMode.ThreadPolicy.Builder().DetectAll().PenaltyLog().Build());\n    StrictMode.SetVmPolicy(new StrictMode.VmPolicy.Builder().DetectLeakedSqlLiteObjects().DetectLeakedClosableObjects().PenaltyLog().PenaltyDeath().Build());\n    base.OnCreate(bundle);\n}<\/code><\/pre>\n<h2>Wrapping Up<\/h2>\n<p>In this blog post, we covered tools including <strong>Systrace<\/strong>, <strong>GPU overdraw<\/strong>, <strong>Hierarchy Viewer<\/strong>, and <strong>StrictMode<\/strong> to pinpoint performance related issues in your application and fix them. By using these tools, we can help ensure we&#8217;re delivering Android applications to our users with excellent rendering performance that achieves a buttery-smooth 60fps.\t\t<\/p>\n","protected":false},"excerpt":{"rendered":"<p>As developers, we want to build mobile apps that are not only beautiful and feature-rich, but also high-performance. We often think about ways we can optimize our network calls and background operations to be speedy, but users typically equate performance with the main component of a mobile app they interact with: the user interface. When [&hellip;]<\/p>\n","protected":false},"author":551,"featured_media":29564,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[2],"tags":[5,4],"class_list":["post-29081","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-developers","tag-android","tag-xamarin-platform"],"acf":[],"blog_post_summary":"<p>As developers, we want to build mobile apps that are not only beautiful and feature-rich, but also high-performance. We often think about ways we can optimize our network calls and background operations to be speedy, but users typically equate performance with the main component of a mobile app they interact with: the user interface. When [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/xamarin\/wp-json\/wp\/v2\/posts\/29081","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/xamarin\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/xamarin\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/xamarin\/wp-json\/wp\/v2\/users\/551"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/xamarin\/wp-json\/wp\/v2\/comments?post=29081"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/xamarin\/wp-json\/wp\/v2\/posts\/29081\/revisions"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/xamarin\/wp-json\/wp\/v2\/media?parent=29081"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/xamarin\/wp-json\/wp\/v2\/categories?post=29081"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/xamarin\/wp-json\/wp\/v2\/tags?post=29081"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}