preface

I wrote a previous article about how to write Transform deltas. This time I want to expand and talk about something else.

Let’s start with some weird questions.

  1. If you want to writeR File inlineIn the case of, how will you operate, what problems might you encounter?
  2. If the Transform is out of date, then it would be better to abstract that way.

Finally, some simple ASM-related stuff I’ve been playing with lately.

AndroidAutoTrack

Here we go

What problems might you encounter if you want to inline R files?

In Android AGP version 4.1.0, R8 has already done this part of the inlining operation, so we do not need to write this transformation

Since during the Transform operation, we access each class file as a file path, there is no guarantee that the access is orderly and syntactic.

Since we first fetch the R file, then collect the contents of the R file, and then go to all the classes that use the R file, this might not work with the normal Transform flow.

The cache data is then incrementally compiled through file or class information, because the last R file needs to be recorded at the time of incremental compilation, otherwise there will be a missing problem in the incremental case. This part is particularly prone to occur during routing table increments.

Second scan

If we split the operation of file scanning into two parts, the first time is only to collect data, and the second time is to modify the data on the basis of the first time, is it ok?

R file inline this complex ASM operation, we can also be very perfect to solve this part of the problem.

For the first scan, we only perform ASM file access, not ASM modification. In this process, we will only collect the data we need, of course, we will not do any ASM replacement and file write operations, only to convert files to ASM syntax. I don’t know if I’m going to use the Tree API or the core API.

There are two kinds of TREE API and core API that ASM depends on. The difference is that tree API is based on node syntax tree, while core API is faster.

API 'org. Ow2. Asm: asm: 9.2' API 'org. Ow2. Asm: asm - tree: 9.2'Copy the code

For the second ASM operation, we will copy files and replace classes, etc. For the second time, we will collect the class data for the first time, and then restore the original class or method replacement.

You can carefully taste, is basically any complex can be based on this logic to operate. But I still don’t particularly recommend you to write this kind of, after all, is also relatively easy to get blamed.

Incremental cache

In this part I feel that the deltas in the routing table are easy to explain.

When we do the second incremental compilation, due to the nature of incremental compilation, only the change file needs to be modified, but the previous routing table needs to be restored at this time.

This part can refer to the Transform or DRouter of the route that I wrote myself. I have analyzed it before, because the registration class of my routing table is class, not JAR. So I used ASM to read the current class for the last routing table traceback. And then I’m just going to apply the add, delete, modify operation to this registration class.

DRouter generates a JSON file that records the previous routing table, modifs the routing table during incremental compilation, and overwrites the JSON file after the compilation.

And we beep beep curry miles since research BRouter increment of cache implementation is even more bizarre, we are based on the javac task, is also based on the json file cache.

The Transform is out of date

If you’re interested in trying out the AGP 700 version, you’ll notice that Transform has been deprecated. Before Senge also introduced this part, you can refer to the following quote oh.

What does the AGP Transform API deprecate mean

TransformAction is an API built after version 700, but I haven’t learned it yet.

But booster and Bytex, the two great open source frameworks, are actually isolated from Transform, including the bytecode framework we use internally.

The nice thing about this abstraction is that when the AGP API expires, we can replace the adjustment without all the people who wrote the Transform doing it at the same time.

Only need the bottom of the corresponding adaptation work, then let the upper development students to upgrade the bottom of the library version on the line.

TODO

I also plan to abstract and develop this part together in the future. Together with SPI, it can become a dynamic framework.

Automatic exposure

My previous article covered effective exposure monitoring for Views (part 1). I have recently made a small improvement and attempt on this, and I plan to try to adjust it with ASM.

View.OnAttachStateChangeListener

Previously we needed to customize many layouts, then modify the root node in the XML layout, and then use KT’s extension function to find the specific layout.

Because there are two parts of the code involved, my personal opinion is that it is relatively cumbersome to use. After my bosses give directions under the API, I actually provides a addOnAttachStateChangeListener View itself, It does the same thing as onViewAttachedToWindow and onViewDetachedFromWindow.

So instead of writing a custom View substitution, we can just insert a call to the itemView.

viewTreeObserver.addOnWindowFocusChangeListener(this)
        view.addOnAttachStateChangeListener(this)}override fun onViewAttachedToWindow(v: View?). {
        exposeChecker.updateStartTime()
    }

    override fun onViewDetachedFromWindow(v: View?). {
        onExpose()
        // exposeChecker.updateStartTime()
    }

    override fun onWindowFocusChanged(hasFocus: Boolean) {
        if (hasFocus) {
            exposeChecker.updateStartTime()
        } else {
            onExpose()
        }
    }


    private fun onExpose(a) {
        if(exposeChecker.isViewExpose(view)) { mListener? .onExpose(exposeChecker.exposeTime) } } }Copy the code

To put it simply, the gallbladder code is lost. If you are interested, you can go to the project AutoTrack to have a look.

Auto insert exposure listener

Now that we’re done with the XML, all that’s left is to insert our exposure monitoring class into the recyclerView.viewholder initializer.

This part of the logic is relatively simple, so I’ll just say it briefly.


class RecyclerViewHolderImp(classNode: ClassNode) {

    init {
        if (isRecyclerViewHolder(classNode.superName)) {
            classNode.methods.firstOrNull {
                it.name == "<init>"}? .apply { instructions.forEach {if (it.methodEnd()) {
                        instructions.insertBefore(it, VarInsnNode(Opcodes.ALOAD, 1))
                        instructions.insertBefore(it, TypeInsnNode(Opcodes.NEW,
                                "com/wallstreetcn/sample/viewexpose/AutoExposeImp"))
                        instructions.insertBefore(it, InsnNode(Opcodes.DUP))
                        instructions.insertBefore(it, VarInsnNode(Opcodes.ALOAD, 0))
                        instructions.insertBefore(it, MethodInsnNode(Opcodes.INVOKESPECIAL, "com/wallstreetcn/sample/viewexpose/AutoExposeImp"."<init>"."(Landroidx/recyclerview/widget/RecyclerView\$ViewHolder;) V".false))
                        instructions.insertBefore(it, MethodInsnNode(Opcodes.INVOKESTATIC, "com/wallstreetcn/sample/viewexpose/ItemViewExtensionKt"."addExposeListener"."(Landroid/view/View; Lcom/wallstreetcn/sample/viewexpose/OnExposeListener;) V".false)}}}}}}Copy the code

class AutoExposeImp(private val viewHolder: RecyclerView.ViewHolder) : OnExposeListener {

    override fun onExpose(exposeTime: Float){}}fun View.addExposeListener(listener: OnExposeListener?). {
    val exposeDelegate = ExposeViewDelegate(this, listener)
}
Copy the code

Check whether the current class is an implementation of RecyclerView.ViewHolder. If so, insert static code for an AutoExposeImp at the end of the

method.

This part of the specific source code is in the AndroidAutoTrack this internal sample, interested brother can refer to learn it.

conclusion

In fact, ASM difficulty is not particularly high bar, as long as you try to write more debugging, do not need you have how smart, you can master this skill, and reverse tool to help oh, in fact, more operation is similar to copy and paste it.

Another is the opportunity to learn more about some of the strong open source frameworks and what their implementations are. It helps us understand and abstract code.

Booster Sengo’s Gradle open source framework is written to be the best in the universe

The ByteX byte open source framework is also an awesome framework

The Lancet was also an extremely ambitious bytecode framework for our early work

BRouter our blip blip routing framework is really awesome

DRouter didi routing is actually a little bit interesting so it’s all transform-based to save compilation time