<lambda>null1package shark 2 3 import shark.HprofRecord.HeapDumpRecord.ObjectRecord.PrimitiveArrayDumpRecord.IntArrayDump 4 5 class AndroidResourceIdNames private constructor( 6 private val resourceIds: IntArray, 7 private val names: Array<String> 8 ) { 9 10 operator fun get(id: Int): String? { 11 val indexOfId = resourceIds.binarySearch(id) 12 return if (indexOfId >= 0) { 13 names[indexOfId] 14 } else { 15 null 16 } 17 } 18 19 companion object { 20 21 internal const val FIRST_APP_RESOURCE_ID = 0x7F010000 22 internal const val RESOURCE_ID_TYPE_ITERATOR = 0x00010000 23 24 @Volatile 25 @JvmStatic 26 private var holderField: AndroidResourceIdNames? = null 27 28 /** 29 * @param getResourceTypeName a function that delegates to Android 30 * Resources.getResourceTypeName but returns null when the name isn't found instead of 31 * throwing an exception. 32 * 33 * @param getResourceEntryName a function that delegates to Android 34 * Resources.getResourceEntryName but returns null when the name isn't found instead of 35 * throwing an exception. 36 */ 37 @Synchronized fun saveToMemory( 38 getResourceTypeName: (Int) -> String?, 39 getResourceEntryName: (Int) -> String? 40 ) { 41 if (holderField != null) { 42 return 43 } 44 45 // This is based on https://jebware.com/blog/?p=600 which itself is based on 46 // https://stackoverflow.com/a/6646113/703646 47 48 val idToNamePairs = mutableListOf<Pair<Int, String>>() 49 findIdTypeResourceIdStart(getResourceTypeName)?.let { idTypeResourceIdStart -> 50 var resourceId = idTypeResourceIdStart 51 while (true) { 52 val entry = getResourceEntryName(resourceId) ?: break 53 idToNamePairs += resourceId to entry 54 resourceId++ 55 } 56 } 57 val resourceIds = idToNamePairs.map { it.first } 58 .toIntArray() 59 val names = idToNamePairs.map { it.second } 60 .toTypedArray() 61 holderField = AndroidResourceIdNames(resourceIds, names) 62 } 63 64 private fun findIdTypeResourceIdStart(getResourceTypeName: (Int) -> String?): Int? { 65 var resourceTypeId = FIRST_APP_RESOURCE_ID 66 while (true) { 67 when (getResourceTypeName(resourceTypeId)) { 68 null -> return null 69 "id" -> return resourceTypeId 70 else -> resourceTypeId += RESOURCE_ID_TYPE_ITERATOR 71 } 72 } 73 } 74 75 fun readFromHeap(graph: HeapGraph): AndroidResourceIdNames? { 76 return graph.context.getOrPut(AndroidResourceIdNames::class.java.name) { 77 val className = AndroidResourceIdNames::class.java.name 78 val holderClass = graph.findClassByName(className) 79 holderClass?.let { 80 val holderField = holderClass["holderField"]!! 81 holderField.valueAsInstance?.let { instance -> 82 val resourceIdsField = instance[className, "resourceIds"]!! 83 val resourceIdsArray = resourceIdsField.valueAsPrimitiveArray!! 84 val resourceIds = 85 (resourceIdsArray.readRecord() as IntArrayDump).array 86 val names = instance[className, "names"]!!.valueAsObjectArray!!.readElements() 87 .map { it.readAsJavaString()!! } 88 .toList() 89 .toTypedArray() 90 AndroidResourceIdNames(resourceIds, names) 91 } 92 } 93 } 94 } 95 96 internal fun resetForTests() { 97 holderField = null 98 } 99 } 100 }