您好,登錄后才能下訂單哦!
這篇文章主要講解了“Android開發中AsmClassVisitorFactory如何使用”,文中的講解內容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“Android開發中AsmClassVisitorFactory如何使用”吧!
@Incubating interface AsmClassVisitorFactory<ParametersT : InstrumentationParameters> : Serializable { /** * The parameters that will be instantiated, configured using the given config when registering * the visitor, and injected on instantiation. * * This field must be left unimplemented. */ @get:Nested val parameters: Property<ParametersT> /** * Contains parameters to help instantiate the visitor objects. * * This field must be left unimplemented. */ @get:Nested val instrumentationContext: InstrumentationContext /** * Creates a class visitor object that will visit a class with the given [classContext]. The * returned class visitor must delegate its calls to [nextClassVisitor]. * * The given [classContext] contains static information about the classes before starting the * instrumentation process. Any changes in interfaces or superclasses for the class with the * given [classContext] or for any other class in its classpath by a previous visitor will * not be reflected in the [classContext] object. * * [classContext] can also be used to get the data for classes that are in the runtime classpath * of the class being visited. * * This method must handle asynchronous calls. * * @param classContext contains information about the class that will be instrumented by the * returned class visitor. * @param nextClassVisitor the [ClassVisitor] to which the created [ClassVisitor] must delegate * method calls. */ fun createClassVisitor( classContext: ClassContext, nextClassVisitor: ClassVisitor ): ClassVisitor /** * Whether or not the factory wants to instrument the class with the given [classData]. * * If returned true, [createClassVisitor] will be called and the returned class visitor will * visit the class. * * This method must handle asynchronous calls. */ fun isInstrumentable(classData: ClassData): Boolean }
簡單的分析下這個接口,我們要做的就是在createClassVisitor
這個方法中返回一個ClassVisitor
,正常我們在構造ClassVisitor
實例的時候是需要傳入下一個ClassVisitor
實例的,所以我們之后在new的時候傳入nextClassVisitor就行了。
另外就是isInstrumentable
,這個方法是判斷當前類是否要進行掃描,因為如果所有類都要通過ClassVisitor進行掃描還是太耗時了,我們可以通過這個方法過濾掉很多我們不需要掃描的類。
@Incubating interface ClassData { /** * Fully qualified name of the class. */ val className: String /** * List of the annotations the class has. */ val classAnnotations: List<String> /** * List of all the interfaces that this class or a superclass of this class implements. */ val interfaces: List<String> /** * List of all the super classes that this class or a super class of this class extends. */ val superClasses: List<String> }
ClassData
并不是asm的api,所以其中包含的內容相對來說比較少,但是應該也勉強夠用了。這部分大家簡單看看就行了,就不多做介紹了呢。
AGP版本升級之后,應該是為了區分新舊版的Extension
,所以在AppExtension
的基礎上,新增了一個AndroidComponentsExtension
出來。
我們的transformClassesWith
就需要注冊在這個上面。這個需要考慮到變種,和之前的Transform
還是有比較大的區別的,這樣我們就可以基于不同的變種增加對應的適配工作了。
val androidComponents = project.extensions.getByType(AndroidComponentsExtension::class.java) androidComponents.onVariants { variant -> variant.transformClassesWith(PrivacyClassVisitorFactory::class.java, InstrumentationScope.ALL) {} variant.setAsmFramesComputationMode(FramesComputationMode.COPY_FRAMES) }
這次還是在之前的敏感權限api替換的字節碼替換工具的基礎上進行測試開發。
看看我們正常是如何寫一個簡單的ClassVisitor的。
ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_MAXS); ClassVisitor methodFilterCV = new ClassFilterVisitor(classWriter); ClassReader cr = new ClassReader(srcClass); cr.accept(methodFilterCV, ClassReader.SKIP_DEBUG); return classWriter.toByteArray();
首先我們會構造好一個空的ClassWriter
,接著會構造一個ClassVisitor
實例,然后傳入這個ClassWriter
。然后我們構造一個ClassReader
實例,然后將byte數組傳入,之后調用classReader.accept方法,之后我們就能在visitor中逐個訪問數據了。
那么其實我們的類信息,方法啥的都是通過ClassReader讀入的,然后由當前的ClassVisitor
訪問完之后交給我們最后一個ClassWriter
。
其中ClassWriter
也是一個ClassVisitor
對象,他復雜重新將修改過的類轉化成byte數據。可以看得出來ClassVisitor
就有一個非常簡單的鏈表結構,之后逐層向下訪問。
介紹完了這個哦,我們做個大膽的假設,如果我們這個ClassVisitor
鏈表前插入幾個不同的ClassVisitor
,那么我們是不是就可以讓asm修改逐個生效,然后也不需要多余的io操作了呢。這就是新的asm api 的設計思路了,也是我們這邊大佬的字節碼框架大佬的設計。另外bytex內的設計思路也是如此。
tips ClassNode 因為是先生成的語法樹,所以和一般的ClassVisitor有點小區別,需要在visitEnd方法內調用accept(next)
接下來我們上實戰咯。我將之前的代碼套用到這次的邏輯上來。
demo地址
abstract class PrivacyClassVisitorFactory : AsmClassVisitorFactory<InstrumentationParameters.None> { override fun createClassVisitor(classContext: ClassContext, nextClassVisitor: ClassVisitor): ClassVisitor { return PrivacyClassNode(nextClassVisitor) } override fun isInstrumentable(classData: ClassData): Boolean { return true } }
我在isInstrumentable都返回的是true,其實我可以將掃描規則限定在特定包名內,這樣就可以加快構建速度了。
class PrivacyClassNode(private val nextVisitor: ClassVisitor) : ClassNode(Opcodes.ASM5) { override fun visitEnd() { super.visitEnd() PrivacyHelper.whiteList.let { val result = it.firstOrNull { whiteName -> name.contains(whiteName, true) } result }.apply { if (this == null) { // println("filter: $name") } } PrivacyHelper.whiteList.firstOrNull { name.contains(it, true) }?.apply { val iterator: Iterator<MethodNode> = methods.iterator() while (iterator.hasNext()) { val method = iterator.next() method.instructions?.iterator()?.forEach { if (it is MethodInsnNode) { it.isPrivacy()?.apply { println("privacy transform classNodeName: ${name@this}") it.opcode = code it.owner = owner it.name = name it.desc = desc } } } } } accept(nextVisitor) } } private fun MethodInsnNode.isPrivacy(): PrivacyAsmEntity? { val pair = PrivacyHelper.privacyList.firstOrNull { val first = it.first first.owner == owner && first.code == opcode && first.name == name && first.desc == desc } return pair?.second }
這部分比較簡單,把邏輯抽象定義在類ClassNode
內,然后在visitEnd
方法的時候調用我之前說的accept(nextVisitor)
方法。
感謝各位的閱讀,以上就是“Android開發中AsmClassVisitorFactory如何使用”的內容了,經過本文的學習后,相信大家對Android開發中AsmClassVisitorFactory如何使用這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關知識點的文章,歡迎關注!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。