△ 插图作者: Virginia Poltrack

△ Illustrator: Virginia Poltrack

Author / Chet Haase, Android Development Technology Promotion Engineer

Caton (noun): Refers to poor application performance, which can lead to issues such as dropped frames, inconsistent interface animations, and poor user experience. See the "Unhappy User" entry.

Performance issues are hard to debug. Often we don't know where to start, what tools to use, what problems users encounter, and how those problems manifest on real-world devices.

Over the past few years, the Android team has been working hard to introduce more tools for debugging various issues, from analyzing boot performance to testing specific code paths to testing and optimizing specific use cases and Device , covering all fields. All of these tools are designed for testing during development to help you debug and fix problems found when running locally.

Meanwhile, Google Play's Android Vitals and Firebase both provide dashboards for developers to know how their apps are running on actual user devices.

Nevertheless, in practice, it is still difficult for us to find possible problems in the application, especially the problems that may occur on the user's device. This isn't a problem you can run into with a familiar development machine in your seat. While the Performance Dashboard can be helpful, it doesn't necessarily give you the full details of what's going on when a user encounters a problem.

JankStats born: This is the first AndroidX library built specifically to detect and report application performance issues on user devices.

JankStats is a relatively small-footprint API with three main goals: capturing per-frame performance information, running on user devices (not just development devices), and enabling detection when an application has performance issues, and reporting what happened .

per frame

The Android platform already provides several methods for obtaining frame performance data. For example, FrameMetrics has been available since API 24 to get relevant data, and subsequent releases have enriched this functionality to provide you with more detailed information. If you're running your app on an earlier version, there are a number of ways you can get time information, which are less accurate but still useful.

Therefore, if you want to ensure that your frame duration logic works for all versions, you need to implement different testing and reporting mechanisms in different API versions. Now you can use the unified JankStats API to implement these functions. Beyond that, it offers many more surprises (read on for this article!).

JankStats simplifies your work by providing a single API to report the time per frame, and will delegate the appropriate mechanism internally (eg API 24+ will delegate to FrameMetrics ). You don't have to care where this data comes from, just let JankStats tell you how long it took to complete certain things, and get that information in a callback.

Creating and listening to JankStats data is as simple as that: just finish creating, and then you can sit (well, your code "sits") and listen. Here is an example of the steps in the JankStats example JankLoggingActivity :

val jankFrameListener = JankStats.OnFrameListener { frameData ->
 // real app would do something more interesting than log this...
 Log.v("JankStatsSample", frameData.toString())
}
jankStats = JankStats.createAndTrack(
   window,
   Dispatchers.Default.asExecutor(),
   jankFrameListener,
)

The Log.v() call here is for example use only and is not what you should do in your application. In practice, you should probably aggregate/store/upload data for later analysis, rather than posting the data in a log. Anyway, here's an example of the output produced when running on an API 30 simulator (parts of the logcat output have been removed and blank lines added for readability):

JankStats.OnFrameListener: FrameData(frameStartNanos=827233150542009, frameDurationUiNanos=27779985, frameDurationCpuNanos=31296985, isJank=false, states=[Activity: JankLoggingActivity])

JankStats.OnFrameListener: FrameData(frameStartNanos=827314067288736, frameDurationUiNanos=89903592, frameDurationCpuNanos=94582592, isJank=true, states=[RecyclerView: Dragging, Activity: JankLoggingActivity])

JankStats.OnFrameListener: FrameData(frameStartNanos=827314167288732, frameDurationUiNanos=88641926, frameDurationCpuNanos=91526926, isJank=true, states=[RecyclerView: Settling, RecyclerView: Dragging, Activity: JankLoggingActivity])

JankStats.OnFrameListener: FrameData(frameStartNanos=827314183945923, frameDurationUiNanos=4731405, frameDurationCpuNanos=8283405, isJank=false, states=[RecyclerView: Settling, Activity: JankLoggingActivity])

You can see something interesting in frameData of the log:

  • Some of the frames are marked with isJank=true . This log is taken from a running example app JankLoggingActivity , you can see full example for more. The app forces some long frames (yes, Thread.sleep() !), causing JankStats to call it stuttering.
  • The frame's timing information includes both interface and CPU data, but in versions prior to API 24 (the version where FrameMetrics was introduced), this information only included the interface duration.
  • This log is from when I start swiping the RecyclerView in my app. When the RecyclerView starts to move ("dragged") and the RecyclerView starts to scroll freely ("dropped"), we can see information related to the interface state before it starts (only Activity states are listed). For details on these interface states, read below.

real data

Unlike recent benchmark libraries, JankStats was created to give you results from user devices. It's great to be able to debug problems on the development machine, but in reality, users will use your app on different devices with very different constraints, and in such cases, local debugging may not find and fix the problem.

JankStats provides APIs to instrument your application to provide the performance data and reporting mechanisms you need so you can upload this data and analyze it offline.

Application Status

Finally (note that this is what's new with the JankStats library), JankStats provides a way to see what's actually going on in your app when performance issues arise. A common complaint we hear is that existing tools, dashboards, and methodologies don't provide enough context to let you know what performance issues your users are actually experiencing.

For example, the FrameMetrics API (introduced in API 24, also used internally by JankStats) can tell you how long it took to draw a frame, and you can get stutter information from it, but it doesn't tell you what's going on in your app at the time. When you try to instrument your code and integrate it with FrameMetrics or other performance measurement tools, it's up to the developer to figure it out. But unless you have to build this infrastructure in-house, everyone has a lot of other work to do. Therefore, the stutter problem is usually not quantitatively tested, and the performance problem naturally cannot be solved.

Likewise, the Android Vitals dashboard can tell you that your app has performance issues, but it can't tell you exactly how your app was doing when the problem occurred. Therefore, it is difficult for you to use this information to know how to deal with the problems that arise.

JankStats introduces the PerformanceMetricsState API, a simple way to tell the system what's going on with your app at any given moment via a pair of strings. For example, you might want to know when a particular Activity or Fragment is active, or when a RecyclerView is scrolling.

For example, here is the code from the JankStats example showing how the tool detects RecyclerView to provide this information to JankStats:

val scrollListener = object : RecyclerView.OnScrollListener() {
 override fun onScrollStateChanged(recyclerView: RecyclerView,
                                   newState: Int)
 {
   val metricsState = metricsStateHolder?.state ?: return
     when (newState) {
       RecyclerView.SCROLL_STATE_DRAGGING -> {
         metricsState.addState("RecyclerView", "Dragging")
       }
       RecyclerView.SCROLL_STATE_SETTLING -> {
         metricsState.addState("RecyclerView", "Settling")
       }
       else -> {
         metricsState.removeState("RecyclerView")
       }
    }
 }
}

This status can be injected anywhere in your app (even from other libraries) and will be picked up by JankStats when it reports results. That way, when you get a report from JankStats, you'll not only know how long various events took in each frame, but also what the user did during that frame, which can be quite useful information.

resource

The following resources can help you learn more about JankStats:

AndroidX Project : JankStats in library 16225d7b12b43a AndroidX.

Documentation : A new Developer's Guide is available on the developer site, which describes the usage of JankStats.

sample code : sample project shows how to instantiate and listen to a JankStats object, and how to monitor the application for important UI status information.

bug report : If you have any questions about this library, or want to make API requests, please submit bug report

Alpha -> 1.0

JankStats has just released its first alpha release, and the intent of this release is: "We think this API and functionality will be useful for the 1.0 release, please try it out and share your feedback with us."

There are other things we want to do with JankStats going forward, including adding some kind of aggregation mechanism or even syncing with the existing upload service. However, before rolling out the first version, we want to hear about your usage and gather other features you want. We hope this release is helpful in its current basic state. Just the function of easily detecting and recording interface status information should be able to provide some convenience for everyone.

Now please to get and try this version, we are waiting for Most importantly, we hope you can find and fix performance issues with JankStats! Your users are waiting for you, don't make them wait too long!

You are welcome here to submit feedback to us, or share your favorite content and problems found. Your feedback is very important to us, thank you for your support!


Android开发者
404 声望2k 粉丝

Android 最新开发技术更新,包括 Kotlin、Android Studio、Jetpack 和 Android 最新系统技术特性分享。更多内容,请关注 官方 Android 开发者文档。