您好,登錄后才能下訂單哦!
這篇文章主要講解了“Android開(kāi)發(fā)中AsmClassVisitorFactory如何使用”,文中的講解內(nèi)容簡(jiǎn)單清晰,易于學(xué)習(xí)與理解,下面請(qǐng)大家跟著小編的思路慢慢深入,一起來(lái)研究和學(xué)習(xí)“Android開(kāi)發(fā)中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 }
簡(jiǎn)單的分析下這個(gè)接口,我們要做的就是在createClassVisitor
這個(gè)方法中返回一個(gè)ClassVisitor
,正常我們?cè)跇?gòu)造ClassVisitor
實(shí)例的時(shí)候是需要傳入下一個(gè)ClassVisitor
實(shí)例的,所以我們之后在new的時(shí)候傳入nextClassVisitor就行了。
另外就是isInstrumentable
,這個(gè)方法是判斷當(dāng)前類(lèi)是否要進(jìn)行掃描,因?yàn)槿绻蓄?lèi)都要通過(guò)ClassVisitor進(jìn)行掃描還是太耗時(shí)了,我們可以通過(guò)這個(gè)方法過(guò)濾掉很多我們不需要掃描的類(lèi)。
@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,所以其中包含的內(nèi)容相對(duì)來(lái)說(shuō)比較少,但是應(yīng)該也勉強(qiáng)夠用了。這部分大家簡(jiǎn)單看看就行了,就不多做介紹了呢。
AGP版本升級(jí)之后,應(yīng)該是為了區(qū)分新舊版的Extension
,所以在AppExtension
的基礎(chǔ)上,新增了一個(gè)AndroidComponentsExtension
出來(lái)。
我們的transformClassesWith
就需要注冊(cè)在這個(gè)上面。這個(gè)需要考慮到變種,和之前的Transform
還是有比較大的區(qū)別的,這樣我們就可以基于不同的變種增加對(duì)應(yīng)的適配工作了。
val androidComponents = project.extensions.getByType(AndroidComponentsExtension::class.java) androidComponents.onVariants { variant -> variant.transformClassesWith(PrivacyClassVisitorFactory::class.java, InstrumentationScope.ALL) {} variant.setAsmFramesComputationMode(FramesComputationMode.COPY_FRAMES) }
這次還是在之前的敏感權(quán)限api替換的字節(jié)碼替換工具的基礎(chǔ)上進(jìn)行測(cè)試開(kāi)發(fā)。
看看我們正常是如何寫(xiě)一個(gè)簡(jiǎn)單的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();
首先我們會(huì)構(gòu)造好一個(gè)空的ClassWriter
,接著會(huì)構(gòu)造一個(gè)ClassVisitor
實(shí)例,然后傳入這個(gè)ClassWriter
。然后我們構(gòu)造一個(gè)ClassReader
實(shí)例,然后將byte數(shù)組傳入,之后調(diào)用classReader.accept方法,之后我們就能在visitor中逐個(gè)訪(fǎng)問(wèn)數(shù)據(jù)了。
那么其實(shí)我們的類(lèi)信息,方法啥的都是通過(guò)ClassReader讀入的,然后由當(dāng)前的ClassVisitor
訪(fǎng)問(wèn)完之后交給我們最后一個(gè)ClassWriter
。
其中ClassWriter
也是一個(gè)ClassVisitor
對(duì)象,他復(fù)雜重新將修改過(guò)的類(lèi)轉(zhuǎn)化成byte數(shù)據(jù)。可以看得出來(lái)ClassVisitor
就有一個(gè)非常簡(jiǎn)單的鏈表結(jié)構(gòu),之后逐層向下訪(fǎng)問(wèn)。
介紹完了這個(gè)哦,我們做個(gè)大膽的假設(shè),如果我們這個(gè)ClassVisitor
鏈表前插入幾個(gè)不同的ClassVisitor
,那么我們是不是就可以讓asm修改逐個(gè)生效,然后也不需要多余的io操作了呢。這就是新的asm api 的設(shè)計(jì)思路了,也是我們這邊大佬的字節(jié)碼框架大佬的設(shè)計(jì)。另外bytex內(nèi)的設(shè)計(jì)思路也是如此。
tips ClassNode 因?yàn)槭窍壬傻恼Z(yǔ)法樹(shù),所以和一般的ClassVisitor有點(diǎn)小區(qū)別,需要在visitEnd方法內(nèi)調(diào)用accept(next)
接下來(lái)我們上實(shí)戰(zhàn)咯。我將之前的代碼套用到這次的邏輯上來(lái)。
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,其實(shí)我可以將掃描規(guī)則限定在特定包名內(nèi),這樣就可以加快構(gòu)建速度了。
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 }
這部分比較簡(jiǎn)單,把邏輯抽象定義在類(lèi)ClassNode
內(nèi),然后在visitEnd
方法的時(shí)候調(diào)用我之前說(shuō)的accept(nextVisitor)
方法。
感謝各位的閱讀,以上就是“Android開(kāi)發(fā)中AsmClassVisitorFactory如何使用”的內(nèi)容了,經(jīng)過(guò)本文的學(xué)習(xí)后,相信大家對(duì)Android開(kāi)發(fā)中AsmClassVisitorFactory如何使用這一問(wèn)題有了更深刻的體會(huì),具體使用情況還需要大家實(shí)踐驗(yàn)證。這里是億速云,小編將為大家推送更多相關(guān)知識(shí)點(diǎn)的文章,歡迎關(guān)注!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。