中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Android開發中AsmClassVisitorFactory如何使用

發布時間:2022-06-22 09:30:59 來源:億速云 閱讀:296 作者:iii 欄目:開發技術

這篇文章主要講解了“Android開發中AsmClassVisitorFactory如何使用”,文中的講解內容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“Android開發中AsmClassVisitorFactory如何使用”吧!

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,所以其中包含的內容相對來說比較少,但是應該也勉強夠用了。這部分大家簡單看看就行了,就不多做介紹了呢。

新的Extension

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

看看我們正常是如何寫一個簡單的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如何使用這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關知識點的文章,歡迎關注!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

纳雍县| 九台市| 铁力市| 大连市| 怀远县| 大化| 微山县| 铁岭县| 贵州省| 凤山县| 资讯| 安多县| 科尔| 绥宁县| 绵竹市| 榆林市| 盐山县| 资讯| 施甸县| 镇康县| 炉霍县| 安塞县| 荔波县| 镇雄县| 安仁县| 嵊泗县| 基隆市| 阳西县| 景德镇市| 庄河市| 灵寿县| 衡阳市| 隆化县| 遵义市| 涞水县| 邓州市| 武山县| 石狮市| 桐梓县| 兰坪| 同江市|