1 /* 2 * Copyright 2021 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package androidx.metrics.performance 17 18 /** 19 * This class stores duration data for a single frame. 20 * 21 * @property frameStartNanos The time at which this frame began (in nanoseconds) 22 * @property frameDurationUiNanos The time spent in the UI portion of this frame (in nanoseconds). 23 * This is essentially the time spent on the UI thread to draw this frame, but does not include 24 * any time spent on the RenderThread. 25 * @property isJank Whether this frame was determined to be janky, meaning that its duration exceeds 26 * the duration determined by the system to indicate jank (@see 27 * [JankStats.jankHeuristicMultiplier]). 28 * @property states The UI/app state during this frame. This is the information set by the app, or 29 * by other library code, that can be analyzed later to determine the UI state that was current 30 * when jank occurred. 31 * @see JankStats.jankHeuristicMultiplier 32 * @see PerformanceMetricsState.putState 33 */ 34 open class FrameData( 35 frameStartNanos: Long, 36 frameDurationUiNanos: Long, 37 isJank: Boolean, 38 val states: List<StateInfo> 39 ) { 40 /** 41 * These backing fields are used to enable mutation of an existing FrameData object, to avoid 42 * allocating a new object on every frame for sending out to listeners. 43 */ 44 var frameStartNanos = frameStartNanos 45 private set 46 47 var frameDurationUiNanos = frameDurationUiNanos 48 private set 49 50 var isJank = isJank 51 private set 52 53 /** 54 * Utility method which makes a copy of the items in this object (including copying the items in 55 * `states` into a new List). This is used internally to create a copy to pass along to 56 * listeners to avoid having a reference to the internally-mutable FrameData object. 57 */ copynull58 open fun copy(): FrameData { 59 return FrameData(frameStartNanos, frameDurationUiNanos, isJank, ArrayList(states)) 60 } 61 62 /** 63 * Utility method for updating the internal values in this object. Externally, this object is 64 * immutable. Internally, we need the ability to update the values so that we can reuse it for a 65 * non-allocating listener model, to avoid having to re-allocate a new FrameData (and its states 66 * List). Note that the states object is not being updated here; internal can already use a 67 * Mutable list to update the contents of that list; they do not need to update this object with 68 * a new List, since any usage of FrameData to avoid allocations should not be creating a new 69 * state List anyway. 70 */ updatenull71 internal fun update(frameStartNanos: Long, frameDurationUiNanos: Long, isJank: Boolean) { 72 this.frameStartNanos = frameStartNanos 73 this.frameDurationUiNanos = frameDurationUiNanos 74 this.isJank = isJank 75 } 76 equalsnull77 override fun equals(other: Any?): Boolean { 78 if (this === other) return true 79 if (javaClass != other?.javaClass) return false 80 81 other as FrameData 82 83 if (frameStartNanos != other.frameStartNanos) return false 84 if (frameDurationUiNanos != other.frameDurationUiNanos) return false 85 if (isJank != other.isJank) return false 86 if (states != other.states) return false 87 88 return true 89 } 90 hashCodenull91 override fun hashCode(): Int { 92 var result = frameStartNanos.hashCode() 93 result = 31 * result + frameDurationUiNanos.hashCode() 94 result = 31 * result + isJank.hashCode() 95 result = 31 * result + states.hashCode() 96 return result 97 } 98 toStringnull99 override fun toString(): String { 100 return "FrameData(frameStartNanos=$frameStartNanos, " + 101 "frameDurationUiNanos=$frameDurationUiNanos, " + 102 "isJank=$isJank, " + 103 "states=$states)" 104 } 105 } 106 107 /** 108 * This class contains information about application state. 109 * 110 * @property key An arbitrary name used for this state, used as a key for storing the state value. 111 * @property value The value of this state. 112 */ 113 class StateInfo(val key: String, val value: String) { 114 equalsnull115 override fun equals(other: Any?): Boolean { 116 if (this === other) return true 117 if (javaClass != other?.javaClass) return false 118 119 other as StateInfo 120 121 if (key != other.key) return false 122 if (value != other.value) return false 123 124 return true 125 } 126 hashCodenull127 override fun hashCode(): Int { 128 var result = key.hashCode() 129 result = 31 * result + value.hashCode() 130 return result 131 } 132 toStringnull133 override fun toString(): String { 134 return "$key: $value" 135 } 136 137 /** 138 * This internal componion is used to manage a pool of reusable StateInfo objects. Rather than 139 * creating a new StateInfo object very time, the library requests an object for the given 140 * stateName/state pair. In general, requests will be common using the same pairs, thus reuse 141 * will be high and an object from the pool will be returned. When reuse is not necessary, a new 142 * StateInfo object will be created, added to the pool, and returned. 143 */ 144 internal companion object { 145 val pool = mutableMapOf<String, MutableMap<String, StateInfo>>() 146 getStateInfonull147 fun getStateInfo(stateName: String, state: String): StateInfo { 148 synchronized(pool) { 149 var poolItem = pool.get(stateName) 150 var stateInfo = poolItem?.get(state) 151 if (stateInfo != null) return stateInfo 152 else { 153 stateInfo = StateInfo(stateName, state) 154 if (poolItem != null) { 155 poolItem.put(state, stateInfo) 156 } else { 157 poolItem = mutableMapOf(state to stateInfo) 158 pool.put(stateName, poolItem) 159 } 160 return stateInfo 161 } 162 } 163 } 164 } 165 } 166