WMRouter开源库源码阅读笔记(Fragment跳转)


在使用WMRouter实现通过Path来跳转目标Fragment时,由于自己的问题给目标Fragment的构造函数传了一个Context参数,导致跳转不成功问题。在查找这个问题中熟悉了WMRouter的源码,在这里记录一下。WMRouter的详细介绍参考 WMRouter:美团外卖Android开源路由框架

1. APT注解处理

WMRouter采用APT来对注解进行处理并生成相应模版代码,下面看一下通过Path实现Fragment跳转的注解处理过程。首先对目标Fragment添加注解:

@RouterPage(path = ["/fragment/paging/test"])
class PagingTestFragment() 

生成的模版代码为:

//位置:com.sankuai.waimai.router.generated/
public class PageAnnotationInit_7e09d0c7c2c720bbbacddb0cedbfec26 implements IPageAnnotationInit {
  public void init(PageAnnotationHandler handler) {
    handler.register("/fragment/paging/test", new FragmentTransactionHandler("com.yy.oms.logistics.home.fragment.PagingTestFragment"));
  }
}

//位置:com.sankuai.waimai.router.generated.service/
public class ServiceInit_ac827e6cbb5a2489f812c8baa8e9ff42 {
  public static void init() {
    ServiceLoader.put(IPageAnnotationInit.class, "com.sankuai.waimai.router.generated.PageAnnotationInit_7e09d0c7c2c720bbbacddb0cedbfec26", com.sankuai.waimai.router.generated.PageAnnotationInit_7e09d0c7c2c720bbbacddb0cedbfec26.class, false);
  }
}

2. 初始化

对于自动生成代码的处理往往是通过Gradle插件来完成的。

2.1. Plugin+Transform编译处理

在业务中定义一个Plugin来注册一个Transform:

class XxxPlugin : Plugin<Project> {
    override fun apply(p0: Project) {
        val android = p0.extensions.getByType(AppExtension::class.java)
        p0.extensions.create(
            XxxPluginExtension.getExtensionName(),
            XxxPluginExtension::class.java
        )
        android.registerTransform(XxxTransform(p0, null))
    }
}

这里的XxxTransform类是用来处理通过SPI定义的类的,看一下其主要方法:

class XxxTransform(val project: Project, var extension: XxxPluginExtension?) : Transform() {
	//spi加载注册的类
	val transforms = ServiceLoaderUtils.loadClassTransforms(project)

    override fun transform(invocation: TransformInvocation) {
        //编译前
        val transformInvocation = XxxTransformInvocation(invocation, this)
        transforms.forEach {
            it.beforeTransform(transformInvocation.XxxContext, project)
        }
		//编译开始
        if (transformInvocation.isIncremental) {
            transformIncremental(transformInvocation)
        } else {
            invocation.outputProvider.deleteAll()
            transformFull(transformInvocation)
        }
        //编译后
        transformInvocation.onPostTransform()
        transforms.forEach {
            it.afterTransform(transformInvocation.XxxContext)
        }
    }
	//增量编译
    private fun transformIncremental(invocation: XxxTransformInvocation) {
        invocation.inputs.parallelStream().forEach {
            TransformJarUtils.transformJarInputs(it.jarInputs, invocation)
            TransformClassUtils.transformClassFile(invocation, it.directoryInputs)
        }
    }
	//全量编译
    private fun transformFull(invocation: XxxTransformInvocation) {
        invocation.inputs.parallelStream().forEach {
            TransformJarUtils.transformJarInputsFull(it.jarInputs, invocation)
            TransformClassUtils.transformClassFileFull(invocation, it.directoryInputs)
        }
    }
}

这里先调用loadClassTransforms()方法加载通过SPI注册的类,其定义在ServiceLoaderUtils静态类中:

//ServiceLoaderUtils.java
fun loadClassTransforms(project: Project): ArrayList<IClassTransform> {
    val list = ArrayList<IClassTransform>()
    ServiceLoader.load(IClassTransform::class.java).iterator().forEach {
        it.getTransformExtension()?.let {
            project.extensions.create(it.getExtensionName(), it.javaClass)
        }
        list.add(it)
    }
    list.sortBy {
        -it.priority()
    }
    return list
}

这里调用ServiceLoader来将META-INFO/services/下配置文件中的类实例化。

2.2. SPI处理

这里定义的接口为IClassTransform.kt,定义一个实现类WMRouterTransform.kt,并用AutoService注解:

@AutoService(IClassTransform::class)
class WMRouterTransform : IClassTransform {}

AutoService是Google提供的用于生成SPI配置文件的,可以参考Google AutoService。编译后会在同一根目录的build/libs/module名.jar/META-INFO/services/目录下生成配置文件:com.xx.xx.IClassTransform,其文件内容为:

com.yy.oms.plugin.transforms.router.WMRouterTransform

下面看一下WMRouterTransform是如何初始化Service的。

2.3. Service初始化

在业务中,会使用ASM对生成的代码进行收集并存储在内存中,然后可以对缓存的信息进行查找,该过程在WMRouterTransform类中完成:

//WMRouterTransform.kt
private var targetClasses: MutableSet<String> = Collections.newSetFromMap(ConcurrentHashMap())

private const val GEN_PKG_SERVICE = "$GEN_PKG/service"

//查找GEN_PKG_SERVICE路径下的文件并放在targetClasses中
override fun beforeTransform(context: TransformContext, project: Project) {
    val cost = measureTimeMillis {
        context.getClassNodeCache()
                .findClassNodeCache { it.name?.startsWith(GEN_PKG_SERVICE) ?: false }
                ?.forEach { targetClasses.add(it.name!!) }
    }
}

override fun afterTransform(context: TransformContext) {
    execute(context)
    super.afterTransform(context)
}

private fun execute(context: TransformContext) {
    if (targetClasses.isEmpty()) return

    val className: String = SERVICE_LOADER_INIT
    val cost = measureTimeMillis {
        try {
            // write
            val writer = ClassWriter(ClassWriter.COMPUTE_FRAMES or ClassWriter.COMPUTE_MAXS)
            val cv: ClassVisitor = object : ClassVisitor(Opcodes.ASM5, writer) {}
            cv.visit(50, Opcodes.ACC_PUBLIC, className, null, "java/lang/Object", null)
            val mv = cv.visitMethod(Opcodes.ACC_PUBLIC or Opcodes.ACC_STATIC, INIT_METHOD,
                    "()V", null, null)
            mv.visitCode()
            //插入代码:调用service的init()方法
            for (clazz in targetClasses) {
                mv.visitMethodInsn(Opcodes.INVOKESTATIC, clazz, "init", "()V", false)
            }
            mv.visitMaxs(0, 0)
            mv.visitInsn(Opcodes.RETURN)
            mv.visitEnd()
            cv.visitEnd()

            // output file
            val dest = File(context.getOutPutDirPath(), className + SdkConstants.DOT_CLASS).touch()
            writer.toByteArray().redirect(dest)
            getLogger().i("$TAG execute generated file $dest, length ${dest.length()}")
        } catch (e: Exception) {
            getLogger().i("$TAG execute error: $e")
        }
    }
    getLogger().i("$TAG execute cost ${cost}ms, generated class $className")
}

这里调用visitMethodInsn()方法实际调用的是ServiceInit_ac827e6cbb5a2489f812c8baa8e9ff42类的init()方法,在init()方法中会调用ServiceLoader的put()方法:

    public static void put(Class interfaceClass, String key, Class implementClass, boolean singleton) {
        ServiceLoader loader = SERVICES.get(interfaceClass);
        if (loader == null) {
            loader = new ServiceLoader(interfaceClass);
            SERVICES.put(interfaceClass, loader);
        }
        loader.putImpl(key, implementClass, singleton);
    }

这里的implementClass即是PageAnnotationInit_7e09d0c7c2c720bbbacddb0cedbfec26类的Class,进一步将implementClass为参数调用putImpl()方法:

private void putImpl(String key, Class implementClass, boolean singleton) {
    if (key != null && implementClass != null) {
        mMap.put(key, new ServiceImpl(key, implementClass, singleton));
    }
}

将implementClass包装成一个ServiceImpl对象并存放在mMap
中。

2.4. Annotation初始化

Annotation初始化的调用链路为:

XxApplication Router DefaultRootUriHandler PageAnnotationHandler LazyInitHelper RouterComponents DefaultAnnotationLoader init() lazyInit() lazyInit() lazyInit() performInit() initAnnotationConfig() loadAnnotation() load() XxApplication Router DefaultRootUriHandler PageAnnotationHandler LazyInitHelper RouterComponents DefaultAnnotationLoader

从调用链可以看到最终会调用DefaultAnnotationLoader的load()方法:

@Override
public <T extends UriHandler> void load(T handler,
        Class<? extends AnnotationInit<T>> initClass) {
    List<? extends AnnotationInit<T>> services = Router.getAllServices(initClass);
    for (AnnotationInit<T> service : services) {
        service.init(handler);
    }
}

这里就会拿到Service初始化时记录在mMap中所有的ServiceImpl对象,然后执行其init()方法,这里就会执行PageAnnotationInit_7e09d0c7c2c720bbbacddb0cedbfec26类的init()方法,看一下“APT注解处理”一节的代码可知会调用PathHandler类的register()方法:

/**
 * 注册一个子节点
 *
 * @param path         path
 * @param target       支持ActivityClassName、ActivityClass、UriHandler
 * @param exported     是否允许外部跳转
 * @param interceptors 要添加的interceptor
 */
public void register(String path, Object target, boolean exported,
        UriInterceptor... interceptors) {
    if (!TextUtils.isEmpty(path)) {
        path = RouterUtils.appendSlash(path);
        UriHandler parse = UriTargetTools.parse(target, exported, interceptors);
        UriHandler prev = mMap.put(path, parse);
        if (prev != null) {
            Debugger.fatal("[%s] 重复注册path='%s'的UriHandler: %s, %s", this, path, prev, parse);
        }
    }
}

这里的target是一个FragmentTransactionHandler对象,将该对象转化成类型为UriHandler的parse对象并保存在一个Map中。

3. 跳转逻辑

显示一个Fragment必须先实例化该Fragment对象,通过一下代码实现:

FragmentBuildUriRequest(requireContext(),
                            "/fragment/paging/test".pageUri)
                            .getFragment()

suspend fun AbsFragmentUriRequest.getFragment() = suspendCancellableCoroutine<Fragment?> {
    onComplete(object : OnCompleteListener {
        override fun onSuccess(request: UriRequest) {
            val fragment = request.getField(Fragment::class.java, FragmentBuildUriRequest.FRAGMENT)
            it.resume(fragment)
        }

        override fun onError(request: UriRequest, resultCode: Int) {
            it.resume(null)
        }
    }).start()
}

onComplete()方法添加了一个监听对象,最终会调用FragmentBuildUriRequest的父类UriRequest的start()方法,其调用链如图:

UriRequest Router RootUriHandler UriHandler PageAnnotationHandler FragmentTransactionHandler startUri() startUri() handle() handleInternal() handleInternal() UriRequest Router RootUriHandler UriHandler PageAnnotationHandler FragmentTransactionHandler

在分析后面逻辑之前需要明白Handler链的结构:DefaultRootUriHandler下有三个次级Handler(如:PageAnnotationHandler、UriAnnotationHandler、RegexAnnotationHandler),各个次级Handler下又有子Handler(如:FragmentTransactionHandler)。看一下DefaultRootUriHandler注册次级Handle的代码:

public DefaultRootUriHandler(Context context,
                                 @Nullable String defaultScheme, @Nullable String defaultHost) {
        super(context);
        mPageAnnotationHandler = createPageAnnotationHandler();
        mUriAnnotationHandler = createUriAnnotationHandler(defaultScheme, defaultHost);
        mRegexAnnotationHandler = createRegexAnnotationHandler();

        // 处理RouterPage注解定义的内部页面跳转,如果注解没定义,直接结束分发
        addChildHandler(mPageAnnotationHandler, 300);
        // 处理RouterUri注解定义的URI跳转,如果注解没定义,继续分发到后面的Handler
        addChildHandler(mUriAnnotationHandler, 200);
        // 处理RouterRegex注解定义的正则匹配
        addChildHandler(mRegexAnnotationHandler, 100);
        // 添加其他用户自定义Handler...

        // 都没有处理,则尝试使用默认的StartUriHandler直接启动Uri
        addChildHandler(new StartUriHandler(), -100);
        // 全局OnCompleteListener,用于输出跳转失败提示信息
        setGlobalOnCompleteListener(DefaultOnCompleteListener.INSTANCE);
    }

因此上图的调用链中会先执行PageAnnotationHandler类的handleInternal()方法:

//PageAnnotationHandler.java
@Override
protected void handleInternal(@NonNull final UriRequest request,
        @NonNull final UriCallback callback) {
    UriHandler h = getChild(request);
    if (h != null) {
        h.handle(request, new UriCallback() {
            @Override
            public void onNext() {
                handleByDefault(request, callback);
            }

            @Override
            public void onComplete(int resultCode) {
                callback.onComplete(resultCode);
            }
        });
    } else {
        handleByDefault(request, callback);
    }
}

//PathHandler.java
private UriHandler getChild(@NonNull UriRequest request) {
    String path = request.getUri().getPath();
    if (TextUtils.isEmpty(path)) {
        return null;
    }
    path = RouterUtils.appendSlash(path);
    if (TextUtils.isEmpty(mPathPrefix)) {
        return mMap.get(path);
    }
    if (path.startsWith(mPathPrefix)) {
        return mMap.get(path.substring(mPathPrefix.length()));
    }
    return null;
}

这里getChild()方法从mMap中取出的即是前面往其中添加的FragmentTransactionHandler对象,因此又会执行其handleInternal()方法:

@Override
protected void handleInternal(@NonNull UriRequest request, @NonNull UriCallback callback) {
    StartFragmentAction action = request.getField(StartFragmentAction.class,
            StartFragmentAction.START_FRAGMENT_ACTION);
    if (action == null) {
        Debugger.fatal("FragmentTransactionHandler.handleInternal()应返回的带有StartFragmentAction");
        callback.onComplete(UriResult.CODE_BAD_REQUEST);
        return;
    }

    if (!request.hasField(FRAGMENT_CLASS_NAME)) {
        //判断一下,便于被替换
        request.putField(FRAGMENT_CLASS_NAME, mClassName);
    }

    // Extra
    Bundle extra = request.getField(Bundle.class, FIELD_INTENT_EXTRA);
    //*关键代码,反射构造目标Fragment对象*
    boolean success = action.startFragment(request, extra);
    // 完成
    callback.onComplete(success ? UriResult.CODE_SUCCESS : UriResult.CODE_BAD_REQUEST);
}

先从request中获取一个StartFragmentAction对象,然后调用其startFragment()方法初始化目标Fragment并执行Fragment事务:

@Override
public boolean startFragment(@NonNull UriRequest request, @NonNull Bundle bundle)
        throws ActivityNotFoundException, SecurityException {
    String fragmentClassName =
            request.getStringField(FragmentTransactionHandler.FRAGMENT_CLASS_NAME);
    if (TextUtils.isEmpty(fragmentClassName)) {
        Debugger.fatal("FragmentTransactionHandler.handleInternal()应返回的带有ClassName");
        return false;
    }
    if (mContainerViewId == 0) {
        Debugger.fatal("FragmentTransactionHandler.handleInternal()mContainerViewId");
        return false;
    }

    try {
        Fragment fragment =
                Fragment.instantiate(request.getContext(), fragmentClassName, bundle);
        if (fragment == null) {
            Debugger.e("FragmentTransactionUriRequest fragment init error");
            return false;
        }
        Debugger.i("FragmentTransactionUriRequest fragment: ", fragment);


        FragmentTransaction transaction = mFragmentManager.beginTransaction();
        switch (mStartType) {
            case TYPE_ADD:
                transaction.add(mContainerViewId, fragment, mTag);
                break;
            case TYPE_REPLACE:
                transaction.replace(mContainerViewId, fragment, mTag);
                break;
            default:
                Debugger.e("BuildStartFragmentAction:" + mStartType);
        }
        if (mAllowingStateLoss) {
            transaction.commitAllowingStateLoss();
        } else {
            transaction.commit();
        }
        return true;
    } catch (Exception e) {
    }
}
}

这里的代码很熟悉了,我们看一个关键点:反射构造目标Fragment实例。

//Fragment.java
public static Fragment instantiate(@NonNull Context context, @NonNull String fname,
        @Nullable Bundle args) {
    try {
        Class<? extends Fragment> clazz = FragmentFactory.loadFragmentClass(
                context.getClassLoader(), fname);
        //通过构造器构造目标Fragment
        Fragment f = clazz.getConstructor().newInstance();
        if (args != null) {
            args.setClassLoader(f.getClass().getClassLoader());
            f.setArguments(args);
        }
        return f;
    } catch (java.lang.InstantiationException e) {}
}

//Class.java
public Constructor<T> getConstructor(Class<?>... parameterTypes)
    throws NoSuchMethodException, SecurityException {
    return getConstructor0(parameterTypes, Member.PUBLIC);
}

可以看到通过目标Fragment的无参构造器来初始化目标Fragment的,因此在文章开头提到对于含有参数的目标Fragment来说,WMRouter是不能成功构造出该Fragment实例并完成Fragment显示功能的。

4. 自动获取intent/aegument数据原理

先看一下使用姿势,先在一个Fragment中定义用@Autowired注解一个参数,该参数名也是argument中的key,如下:

@RouterPage(path = ["/fragment/test/router"])
class TestRouterFragment : Fragment() {

    @JvmField
    @Autowired
    var value1: Boolean? = false

    @JvmField
    @Autowired
    var value2: Byte? = 1

    @JvmField
    @Autowired
    var value3: Short? = 1
}

然后通过APT会生成一个TestRouterFragment$$WMRouter$$Autowired类,该类中只有一个inject()方法,该方法的作用是获取intent/argument中的数据:

public class TestRouterFragment$$WMRouter$$Autowired implements ISyringe {
  @Override
  public void inject(Object target) {
    TestRouterFragment substitute = (TestRouterFragment)target;
    substitute.value1 = substitute.getArguments().getBoolean("value1", substitute.value1);
    substitute.value2 = substitute.getArguments().getByte("value2", substitute.value2);
    substitute.value3 = substitute.getArguments().getShort("value3", substitute.value3);
  }
}

下面看一下如何通过APT来生成上面的类,源码在WMRouter库AutowiredProcessor类中:

@AutoService(Processor.class)
@SupportedSourceVersion(SourceVersion.RELEASE_7)
public class AutowiredProcessor extends BaseProcessor {
    @Override
    public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
        if (set != null && !set.isEmpty()) {
            try {
                categories(roundEnvironment.getElementsAnnotatedWith(Autowired.class));
                generateHelper();
            } catch (Exception error) { }
            return true;
        }
        return false;
    }

    private void generateHelper() throws IOException, IllegalAccessException {
        TypeElement typeISyringe = elements.getTypeElement(ISYRINGE);
        TypeMirror activityTm = elements.getTypeElement(ACTIVITY_CLASS).asType();
        TypeMirror fragmentTm = elements.getTypeElement(FRAGMENT_CLASS).asType();
        TypeMirror fragmentTmV4 = elements.getTypeElement(FRAGMENT_V4_CLASS).asType();

        // Build input param name.
        ParameterSpec objectParamSpec = ParameterSpec.builder(TypeName.OBJECT, "target").build();

        if (parentAndChild != null && !parentAndChild.isEmpty()) {
            for (Map.Entry<TypeElement, List<Element>> entry : parentAndChild.entrySet()) {
                // Build method : 'inject'
                MethodSpec.Builder injectMethodBuilder = MethodSpec.methodBuilder(METHOD_INJECT)
                        .addAnnotation(Override.class)
                        .addModifiers(PUBLIC)
                        .addParameter(objectParamSpec);

                TypeElement parent = entry.getKey();
                List<Element> childs = entry.getValue();

                String qualifiedName = parent.getQualifiedName().toString();
                String packageName = qualifiedName.substring(0, qualifiedName.lastIndexOf("."));
                //生成的类名,NAME_OF_AUTOWIRED = $$ + WMRouter + $$ + "Autowired";
                String fileName = parent.getSimpleName() + NAME_OF_AUTOWIRED;

                // todo 改成static void
                TypeSpec.Builder helper = TypeSpec.classBuilder(fileName)
                        .addJavadoc(WARNING_TIPS)
                        .addJavadoc("\n")
                        .addJavadoc(WARNING_TIPS2)
                        .addSuperinterface(ClassName.get(typeISyringe))
                        .addModifiers(PUBLIC);

                injectMethodBuilder
                        .addStatement("$T substitute = ($T)target", ClassName.get(parent),
                                ClassName.get(parent));

                // Generate method body, start inject.
                //有多少个注解就有多少个element
                for (Element element : childs) {
                    Autowired fieldConfig = element.getAnnotation(Autowired.class);
                    String fieldName = element.getSimpleName().toString();
                    String originalValue = "substitute." + fieldName;
                    String statement =
                            "substitute." + fieldName + " = " + buildCastCode(element,
                                    fieldConfig.typeKind()) +
                                    "substitute.";
                    boolean isActivity = false;
                    if (types.isSubtype(parent.asType(),
                            activityTm)) {  // Activity, then use getIntent()
                        isActivity = true;
                        statement += "getIntent().";
                    } else if (types.isSubtype(parent.asType(), fragmentTm) ||
                            types.isSubtype(parent.asType(),
                                    fragmentTmV4)) {   // Fragment, then use getArguments()
                        statement += "getArguments().";
                    } else {
                        throw new IllegalAccessException("The field [" + fieldName +
                                "] need autowired from intent, its parent must be activity or fragment!");
                    }

                    statement = buildStatement(originalValue, statement,
                            typeUtils.typeExchange(element, fieldConfig.typeKind()), isActivity);
                    injectMethodBuilder.addStatement(statement,
                            fieldConfig.name().isEmpty() ? fieldName : fieldConfig.name());

                    // Validator
                    if (fieldConfig.required() && !element.asType().getKind()
                            .isPrimitive()) {  // Primitive wont be check.
                        injectMethodBuilder
                                .beginControlFlow("if (null == substitute." + fieldName + ")");
                        injectMethodBuilder.addStatement(
                                "$T.e(\"" + TAG + "\", \"The field '" + fieldName +
                                        "' is null, in class '\" + $T.class.getName() + \"!\")",
                                AndroidLog, ClassName.get(parent));
                        injectMethodBuilder.endControlFlow();
                    }
                }

                helper.addMethod(injectMethodBuilder.build());

                // Generate autowire helper
                JavaFile.builder(packageName, helper.build()).build().writeTo(filer);
            }
        }
    }
}

这里又是使用AutoService来处理SPI类的,会在MATR-INF/services/下生成名为javax.annotation.processing.Processor的配置文件,文件内容有:com.sankuai.waimai.router.compiler.AutowiredProcessor。以上APT代码即会生成TestRouterFragment W M R o u t e r WMRouter WMRouterAutowired类,下面需要在Activity/Fragment的onCreate()方法中调用该类的inject()方法。这里可以使用和WMRouterTransform相类似的处理办法:

@AutoService(IClassTransform::class)
class AutowiredTransform : IClassTransform {
    override fun transform(context: TransformContext, classNode: ClassNode): ClassNode {
        if (isTargetClassNode(classNode)) {
            val injector = "${classNode.name}$AUTO_WIRED_CLASS_SUFFIX"
            classNode.methods.find {
                it.name == "onCreate" && it.desc == "(Landroid/os/Bundle;)V"
            }?.let {
                // 下面的 ASM 代码对应的 java代码:(插件生成)
                // (new TestActivity$$WMRouter$$Autowired()).inject(this);
                val mv = MethodNode()
                mv.visitTypeInsn(Opcodes.NEW, injector)
                mv.visitInsn(Opcodes.DUP)
                mv.visitMethodInsn(Opcodes.INVOKESPECIAL, injector, "<init>", "()V", false)
                mv.visitVarInsn(Opcodes.ALOAD, 0)
                mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, injector, "inject", "(Ljava/lang/Object;)V", false)
                it.instructions.insert(mv.instructions)
                return classNode
            }

            getLogger().i("$TAG transform ${classNode.name} find OnCreate failed, create onCreate and inject $injector")
            // 下面的 ASM 代码对应的 java代码:(插件生成)
            // public void onCreate(Bundle bundle) {
            //     (new TestActivity$$WMRouter$$Autowired()).inject(this);
            //     super.onCreate(bundle);
            // }
            val mv: MethodVisitor = classNode.visitMethod(Opcodes.ACC_PUBLIC, "onCreate",
                    "(Landroid/os/Bundle;)V", null, null)
            mv.visitCode()
            mv.visitTypeInsn(Opcodes.NEW, injector)
            mv.visitInsn(Opcodes.DUP)
            mv.visitMethodInsn(Opcodes.INVOKESPECIAL, injector, "<init>", "()V", false)
            mv.visitVarInsn(Opcodes.ALOAD, 0)
            mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, injector, "inject", "(Ljava/lang/Object;)V", false)

            mv.visitVarInsn(Opcodes.ALOAD, 0)
            mv.visitVarInsn(Opcodes.ALOAD, 1)
            mv.visitMethodInsn(Opcodes.INVOKESPECIAL, classNode.superName, "onCreate", "(Landroid/os/Bundle;)V",
                    false)
            mv.visitInsn(Opcodes.RETURN)
            mv.visitMaxs(2, 2)
            mv.visitEnd()
            classNode.visitEnd()
            return classNode
        }
        return classNode
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值