booster功能分析(2)

继续上一篇的话题,滴滴booster功能分析讲解,本篇仍是以系统bug修复为主来说解。java

Logcat

如下是transform的主要代码android

if (context.isDebuggable || klass.name.startsWith(INSTRUMENT)) {
            return klass
        }
        klass.methods.forEach { method ->
            method.instructions?.iterator()?.asIterable()?.filter {
                when (it.opcode) {
                    INVOKESTATIC -> (it as MethodInsnNode).owner == LOGCAT && SHADOW_LOG_METHODS.contains(it.name)
                    INVOKEVIRTUAL -> (it as MethodInsnNode).name == "printStackTrace" && it.desc == "()V" && context.klassPool.get(THROWABLE).isAssignableFrom(it.owner)
                    GETSTATIC -> (it as FieldInsnNode).owner == SYSTEM && (it.name == "out" || it.name == "err")
                    else -> false
                }
            }?.forEach {
                when (it.opcode) {
                    INVOKESTATIC -> {
                        logger.println(" * ${(it as MethodInsnNode).owner}.${it.name}${it.desc} => $SHADOW_LOG.${it.name}${it.desc}: ${klass.name}.${method.name}${method.desc}")
                        it.owner = SHADOW_LOG
                    }
                    INVOKEVIRTUAL -> {
                        logger.println(" * ${(it as MethodInsnNode).owner}.${it.name}${it.desc} => $SHADOW_LOG.${it.name}${it.desc}: ${klass.name}.${method.name}${method.desc}")
                        it.apply {
                            itf = false
                            owner = SHADOW_THROWABLE
                            desc = "(Ljava/lang/Throwable;)V"
                            opcode = INVOKESTATIC
                        }
                    }
                    GETSTATIC -> {
                        logger.println(" * ${(it as FieldInsnNode).owner}.${it.name}${it.desc} => $SHADOW_LOG.${it.name}${it.desc}: ${klass.name}.${method.name}${method.desc}")
                        it.owner = SHADOW_SYSTEM
                    }
                }
            }
        }
        return klass
复制代码

主要是对Log,System,Throwable这三个类的实现方法进行替换成本地的实现方式,对代码中的日志进行屏蔽,好比正式环境下的包就不须要日志输出这样的功能。web

shareprefenence

这个主要是要防止由于多线程致使的保存错误问题,看下面的transform面试

klass.methods.forEach { method ->
            method.instructions?.iterator()?.asIterable()?.filterIsInstance(MethodInsnNode::class.java)?.filter {
                it.opcode == Opcodes.INVOKEINTERFACE && it.owner == SHARED_PREFERENCES_EDITOR
            }?.forEach { invoke ->
                when ("${invoke.name}${invoke.desc}") {
                    "commit()Z" -> if (Opcodes.POP == invoke.next?.opcode) {
                        // if the return value of commit() does not used
                        // use asynchronous commit() instead
                        invoke.optimize(klass, method)
                        method.instructions.remove(invoke.next)
                    }
                    "apply()V" -> invoke.optimize(klass, method)
                }
            }
        }
        return klass
复制代码

当方法为commit的时候,替换commit的调用,使用本地的调用方式,实现以下:设计模式

public static void apply(final SharedPreferences.Editor editor) {
        if (Looper.myLooper() == Looper.getMainLooper()) {
            AsyncTask.SERIAL_EXECUTOR.execute(new Runnable() {
                @Override
                public void run() {
                    editor.commit();
                }
            });
        } else {
            editor.commit();
        }
    }
复制代码

有没有看到,就是这么简单,之后面试的时候能够吹一波牛皮了,说解决了sharepreference的同步调用问题,哈哈bash

MediaPlayer

klass.methods?.forEach { method ->
            method.instructions?.iterator()?.asIterable()?.filter {
                when (it.opcode) {
                    Opcodes.INVOKESTATIC -> (it as MethodInsnNode).owner == MEDIA_PLAYER && it.name == "create"
                    Opcodes.NEW -> (it as TypeInsnNode).desc == MEDIA_PLAYER
                    else -> false
                }
            }?.forEach {
                if (it.opcode == Opcodes.INVOKESTATIC) {
                    logger.println(" * ${(it as MethodInsnNode).owner}.${it.name}${it.desc} => $SHADOW_MEDIA_PLAYER.${it.name}${it.desc}: ${klass.name}.${method.name}${method.desc}")
                    it.owner = SHADOW_MEDIA_PLAYER
                } else if (it.opcode == Opcodes.NEW) {
                    (it as TypeInsnNode).transform(klass, method, it, SHADOW_MEDIA_PLAYER)
                    logger.println(" * new ${it.desc}() => $SHADOW_MEDIA_PLAYER.newMediaPlayer:()L$MEDIA_PLAYER: ${klass.name}.${method.name}${method.desc}")
                }
            }
        }
复制代码

对于调用了media.create 或者是new MediaPlayer() 的地方进行方法替换,经过反射获取到mEventHandler,来对这个设置一个callback来捕获异常。多线程

private static MediaPlayer workaround(final MediaPlayer player) {
        try {
            final Handler handler = getEventHandler(player);
            if (null == handler || !setFieldValue(handler, "mCallback", new CaughtCallback(handler))) {
                Log.i(TAG, "Hook MediaPlayer.mEventHandler.mCallback failed");
            }
        } catch (final Throwable t) {
            Log.e(TAG, "Hook MediaPlayer.mEventHandler.mCallback failed", t);
        }

        return player;
    }
复制代码

webview

webview主要是用来作预加载用的,在application.create的地方作插庄处理。app

val method = klass.methods?.find {
            "${it.name}${it.desc}" == "onCreate()V"
        } ?: klass.defaultOnCreate.also {
            klass.methods.add(it)
        }

        method.instructions?.let { insn ->
            insn.findAll(RETURN, ATHROW).forEach { ret ->
                insn.insertBefore(ret, VarInsnNode(ALOAD, 0))
                insn.insertBefore(ret, MethodInsnNode(INVOKESTATIC, SHADOW_WEBVIEW, "preloadWebView", "(Landroid/app/Application;)V", false))
                logger.println(" + $SHADOW_WEBVIEW.preloadWebView(Landroid/app/Application;)V before @${if (ret.opcode == ATHROW) "athrow" else "return"}: ${klass.name}.${method.name}${method.desc} ")
            }
        }
复制代码

插入的代码以下:框架

private static void startChromiumEngine() {
        try {
            final long t0 = SystemClock.uptimeMillis();
            final Object provider = invokeStaticMethod(Class.forName("android.webkit.WebViewFactory"), "getProvider");
            invokeMethod(provider, "startYourEngines", new Class[]{boolean.class}, new Object[]{true});
            Log.i(TAG, "Start chromium engine complete: " + (SystemClock.uptimeMillis() - t0) + " ms");
        } catch (final Throwable t) {
            Log.e(TAG, "Start chromium engine error", t);
        }
    }
复制代码

总结

上面讲到的内容,包括第一篇所讲到的内容,都是比较简单容易理解的,接下来的篇幅中,会对如下几点进行深刻讲解:async

一、res-check

二、lint

三、shrink

四、里面所涉及到的设计模式

五、class类结构

六、asm知识点

七、如何搭建一个apm框架

相关文章
相关标签/搜索