继续上一篇的话题,滴滴booster功能分析讲解,本篇仍是以系统bug修复为主来说解。java
如下是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
这个主要是要防止由于多线程致使的保存错误问题,看下面的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
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主要是用来作预加载用的,在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框架