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); } }对于自动生成代码的处理往往是通过Gradle插件来完成的。
在业务中定义一个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/下配置文件中的类实例化。
这里定义的接口为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的。
在业务中,会使用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 中。
Annotation初始化的调用链路为:
#mermaid-svg-Peauh0FL01XGU1q4 .label{font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family);fill:#333;color:#333}#mermaid-svg-Peauh0FL01XGU1q4 .label text{fill:#333}#mermaid-svg-Peauh0FL01XGU1q4 .node rect,#mermaid-svg-Peauh0FL01XGU1q4 .node circle,#mermaid-svg-Peauh0FL01XGU1q4 .node ellipse,#mermaid-svg-Peauh0FL01XGU1q4 .node polygon,#mermaid-svg-Peauh0FL01XGU1q4 .node path{fill:#ECECFF;stroke:#9370db;stroke-width:1px}#mermaid-svg-Peauh0FL01XGU1q4 .node .label{text-align:center;fill:#333}#mermaid-svg-Peauh0FL01XGU1q4 .node.clickable{cursor:pointer}#mermaid-svg-Peauh0FL01XGU1q4 .arrowheadPath{fill:#333}#mermaid-svg-Peauh0FL01XGU1q4 .edgePath .path{stroke:#333;stroke-width:1.5px}#mermaid-svg-Peauh0FL01XGU1q4 .flowchart-link{stroke:#333;fill:none}#mermaid-svg-Peauh0FL01XGU1q4 .edgeLabel{background-color:#e8e8e8;text-align:center}#mermaid-svg-Peauh0FL01XGU1q4 .edgeLabel rect{opacity:0.9}#mermaid-svg-Peauh0FL01XGU1q4 .edgeLabel span{color:#333}#mermaid-svg-Peauh0FL01XGU1q4 .cluster rect{fill:#ffffde;stroke:#aa3;stroke-width:1px}#mermaid-svg-Peauh0FL01XGU1q4 .cluster text{fill:#333}#mermaid-svg-Peauh0FL01XGU1q4 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family);font-size:12px;background:#ffffde;border:1px solid #aa3;border-radius:2px;pointer-events:none;z-index:100}#mermaid-svg-Peauh0FL01XGU1q4 .actor{stroke:#ccf;fill:#ECECFF}#mermaid-svg-Peauh0FL01XGU1q4 text.actor>tspan{fill:#000;stroke:none}#mermaid-svg-Peauh0FL01XGU1q4 .actor-line{stroke:grey}#mermaid-svg-Peauh0FL01XGU1q4 .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333}#mermaid-svg-Peauh0FL01XGU1q4 .messageLine1{stroke-width:1.5;stroke-dasharray:2, 2;stroke:#333}#mermaid-svg-Peauh0FL01XGU1q4 #arrowhead path{fill:#333;stroke:#333}#mermaid-svg-Peauh0FL01XGU1q4 .sequenceNumber{fill:#fff}#mermaid-svg-Peauh0FL01XGU1q4 #sequencenumber{fill:#333}#mermaid-svg-Peauh0FL01XGU1q4 #crosshead path{fill:#333;stroke:#333}#mermaid-svg-Peauh0FL01XGU1q4 .messageText{fill:#333;stroke:#333}#mermaid-svg-Peauh0FL01XGU1q4 .labelBox{stroke:#ccf;fill:#ECECFF}#mermaid-svg-Peauh0FL01XGU1q4 .labelText,#mermaid-svg-Peauh0FL01XGU1q4 .labelText>tspan{fill:#000;stroke:none}#mermaid-svg-Peauh0FL01XGU1q4 .loopText,#mermaid-svg-Peauh0FL01XGU1q4 .loopText>tspan{fill:#000;stroke:none}#mermaid-svg-Peauh0FL01XGU1q4 .loopLine{stroke-width:2px;stroke-dasharray:2, 2;stroke:#ccf;fill:#ccf}#mermaid-svg-Peauh0FL01XGU1q4 .note{stroke:#aa3;fill:#fff5ad}#mermaid-svg-Peauh0FL01XGU1q4 .noteText,#mermaid-svg-Peauh0FL01XGU1q4 .noteText>tspan{fill:#000;stroke:none}#mermaid-svg-Peauh0FL01XGU1q4 .activation0{fill:#f4f4f4;stroke:#666}#mermaid-svg-Peauh0FL01XGU1q4 .activation1{fill:#f4f4f4;stroke:#666}#mermaid-svg-Peauh0FL01XGU1q4 .activation2{fill:#f4f4f4;stroke:#666}#mermaid-svg-Peauh0FL01XGU1q4 .mermaid-main-font{font-family:"trebuchet ms", verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-Peauh0FL01XGU1q4 .section{stroke:none;opacity:0.2}#mermaid-svg-Peauh0FL01XGU1q4 .section0{fill:rgba(102,102,255,0.49)}#mermaid-svg-Peauh0FL01XGU1q4 .section2{fill:#fff400}#mermaid-svg-Peauh0FL01XGU1q4 .section1,#mermaid-svg-Peauh0FL01XGU1q4 .section3{fill:#fff;opacity:0.2}#mermaid-svg-Peauh0FL01XGU1q4 .sectionTitle0{fill:#333}#mermaid-svg-Peauh0FL01XGU1q4 .sectionTitle1{fill:#333}#mermaid-svg-Peauh0FL01XGU1q4 .sectionTitle2{fill:#333}#mermaid-svg-Peauh0FL01XGU1q4 .sectionTitle3{fill:#333}#mermaid-svg-Peauh0FL01XGU1q4 .sectionTitle{text-anchor:start;font-size:11px;text-height:14px;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-Peauh0FL01XGU1q4 .grid .tick{stroke:#d3d3d3;opacity:0.8;shape-rendering:crispEdges}#mermaid-svg-Peauh0FL01XGU1q4 .grid .tick text{font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-Peauh0FL01XGU1q4 .grid path{stroke-width:0}#mermaid-svg-Peauh0FL01XGU1q4 .today{fill:none;stroke:red;stroke-width:2px}#mermaid-svg-Peauh0FL01XGU1q4 .task{stroke-width:2}#mermaid-svg-Peauh0FL01XGU1q4 .taskText{text-anchor:middle;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-Peauh0FL01XGU1q4 .taskText:not([font-size]){font-size:11px}#mermaid-svg-Peauh0FL01XGU1q4 .taskTextOutsideRight{fill:#000;text-anchor:start;font-size:11px;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-Peauh0FL01XGU1q4 .taskTextOutsideLeft{fill:#000;text-anchor:end;font-size:11px}#mermaid-svg-Peauh0FL01XGU1q4 .task.clickable{cursor:pointer}#mermaid-svg-Peauh0FL01XGU1q4 .taskText.clickable{cursor:pointer;fill:#003163 !important;font-weight:bold}#mermaid-svg-Peauh0FL01XGU1q4 .taskTextOutsideLeft.clickable{cursor:pointer;fill:#003163 !important;font-weight:bold}#mermaid-svg-Peauh0FL01XGU1q4 .taskTextOutsideRight.clickable{cursor:pointer;fill:#003163 !important;font-weight:bold}#mermaid-svg-Peauh0FL01XGU1q4 .taskText0,#mermaid-svg-Peauh0FL01XGU1q4 .taskText1,#mermaid-svg-Peauh0FL01XGU1q4 .taskText2,#mermaid-svg-Peauh0FL01XGU1q4 .taskText3{fill:#fff}#mermaid-svg-Peauh0FL01XGU1q4 .task0,#mermaid-svg-Peauh0FL01XGU1q4 .task1,#mermaid-svg-Peauh0FL01XGU1q4 .task2,#mermaid-svg-Peauh0FL01XGU1q4 .task3{fill:#8a90dd;stroke:#534fbc}#mermaid-svg-Peauh0FL01XGU1q4 .taskTextOutside0,#mermaid-svg-Peauh0FL01XGU1q4 .taskTextOutside2{fill:#000}#mermaid-svg-Peauh0FL01XGU1q4 .taskTextOutside1,#mermaid-svg-Peauh0FL01XGU1q4 .taskTextOutside3{fill:#000}#mermaid-svg-Peauh0FL01XGU1q4 .active0,#mermaid-svg-Peauh0FL01XGU1q4 .active1,#mermaid-svg-Peauh0FL01XGU1q4 .active2,#mermaid-svg-Peauh0FL01XGU1q4 .active3{fill:#bfc7ff;stroke:#534fbc}#mermaid-svg-Peauh0FL01XGU1q4 .activeText0,#mermaid-svg-Peauh0FL01XGU1q4 .activeText1,#mermaid-svg-Peauh0FL01XGU1q4 .activeText2,#mermaid-svg-Peauh0FL01XGU1q4 .activeText3{fill:#000 !important}#mermaid-svg-Peauh0FL01XGU1q4 .done0,#mermaid-svg-Peauh0FL01XGU1q4 .done1,#mermaid-svg-Peauh0FL01XGU1q4 .done2,#mermaid-svg-Peauh0FL01XGU1q4 .done3{stroke:grey;fill:#d3d3d3;stroke-width:2}#mermaid-svg-Peauh0FL01XGU1q4 .doneText0,#mermaid-svg-Peauh0FL01XGU1q4 .doneText1,#mermaid-svg-Peauh0FL01XGU1q4 .doneText2,#mermaid-svg-Peauh0FL01XGU1q4 .doneText3{fill:#000 !important}#mermaid-svg-Peauh0FL01XGU1q4 .crit0,#mermaid-svg-Peauh0FL01XGU1q4 .crit1,#mermaid-svg-Peauh0FL01XGU1q4 .crit2,#mermaid-svg-Peauh0FL01XGU1q4 .crit3{stroke:#f88;fill:red;stroke-width:2}#mermaid-svg-Peauh0FL01XGU1q4 .activeCrit0,#mermaid-svg-Peauh0FL01XGU1q4 .activeCrit1,#mermaid-svg-Peauh0FL01XGU1q4 .activeCrit2,#mermaid-svg-Peauh0FL01XGU1q4 .activeCrit3{stroke:#f88;fill:#bfc7ff;stroke-width:2}#mermaid-svg-Peauh0FL01XGU1q4 .doneCrit0,#mermaid-svg-Peauh0FL01XGU1q4 .doneCrit1,#mermaid-svg-Peauh0FL01XGU1q4 .doneCrit2,#mermaid-svg-Peauh0FL01XGU1q4 .doneCrit3{stroke:#f88;fill:#d3d3d3;stroke-width:2;cursor:pointer;shape-rendering:crispEdges}#mermaid-svg-Peauh0FL01XGU1q4 .milestone{transform:rotate(45deg) scale(0.8, 0.8)}#mermaid-svg-Peauh0FL01XGU1q4 .milestoneText{font-style:italic}#mermaid-svg-Peauh0FL01XGU1q4 .doneCritText0,#mermaid-svg-Peauh0FL01XGU1q4 .doneCritText1,#mermaid-svg-Peauh0FL01XGU1q4 .doneCritText2,#mermaid-svg-Peauh0FL01XGU1q4 .doneCritText3{fill:#000 !important}#mermaid-svg-Peauh0FL01XGU1q4 .activeCritText0,#mermaid-svg-Peauh0FL01XGU1q4 .activeCritText1,#mermaid-svg-Peauh0FL01XGU1q4 .activeCritText2,#mermaid-svg-Peauh0FL01XGU1q4 .activeCritText3{fill:#000 !important}#mermaid-svg-Peauh0FL01XGU1q4 .titleText{text-anchor:middle;font-size:18px;fill:#000;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-Peauh0FL01XGU1q4 g.classGroup text{fill:#9370db;stroke:none;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family);font-size:10px}#mermaid-svg-Peauh0FL01XGU1q4 g.classGroup text .title{font-weight:bolder}#mermaid-svg-Peauh0FL01XGU1q4 g.clickable{cursor:pointer}#mermaid-svg-Peauh0FL01XGU1q4 g.classGroup rect{fill:#ECECFF;stroke:#9370db}#mermaid-svg-Peauh0FL01XGU1q4 g.classGroup line{stroke:#9370db;stroke-width:1}#mermaid-svg-Peauh0FL01XGU1q4 .classLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5}#mermaid-svg-Peauh0FL01XGU1q4 .classLabel .label{fill:#9370db;font-size:10px}#mermaid-svg-Peauh0FL01XGU1q4 .relation{stroke:#9370db;stroke-width:1;fill:none}#mermaid-svg-Peauh0FL01XGU1q4 .dashed-line{stroke-dasharray:3}#mermaid-svg-Peauh0FL01XGU1q4 #compositionStart{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-Peauh0FL01XGU1q4 #compositionEnd{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-Peauh0FL01XGU1q4 #aggregationStart{fill:#ECECFF;stroke:#9370db;stroke-width:1}#mermaid-svg-Peauh0FL01XGU1q4 #aggregationEnd{fill:#ECECFF;stroke:#9370db;stroke-width:1}#mermaid-svg-Peauh0FL01XGU1q4 #dependencyStart{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-Peauh0FL01XGU1q4 #dependencyEnd{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-Peauh0FL01XGU1q4 #extensionStart{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-Peauh0FL01XGU1q4 #extensionEnd{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-Peauh0FL01XGU1q4 .commit-id,#mermaid-svg-Peauh0FL01XGU1q4 .commit-msg,#mermaid-svg-Peauh0FL01XGU1q4 .branch-label{fill:lightgrey;color:lightgrey;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-Peauh0FL01XGU1q4 .pieTitleText{text-anchor:middle;font-size:25px;fill:#000;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-Peauh0FL01XGU1q4 .slice{font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-Peauh0FL01XGU1q4 g.stateGroup text{fill:#9370db;stroke:none;font-size:10px;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-Peauh0FL01XGU1q4 g.stateGroup text{fill:#9370db;fill:#333;stroke:none;font-size:10px}#mermaid-svg-Peauh0FL01XGU1q4 g.statediagram-cluster .cluster-label text{fill:#333}#mermaid-svg-Peauh0FL01XGU1q4 g.stateGroup .state-title{font-weight:bolder;fill:#000}#mermaid-svg-Peauh0FL01XGU1q4 g.stateGroup rect{fill:#ECECFF;stroke:#9370db}#mermaid-svg-Peauh0FL01XGU1q4 g.stateGroup line{stroke:#9370db;stroke-width:1}#mermaid-svg-Peauh0FL01XGU1q4 .transition{stroke:#9370db;stroke-width:1;fill:none}#mermaid-svg-Peauh0FL01XGU1q4 .stateGroup .composit{fill:white;border-bottom:1px}#mermaid-svg-Peauh0FL01XGU1q4 .stateGroup .alt-composit{fill:#e0e0e0;border-bottom:1px}#mermaid-svg-Peauh0FL01XGU1q4 .state-note{stroke:#aa3;fill:#fff5ad}#mermaid-svg-Peauh0FL01XGU1q4 .state-note text{fill:black;stroke:none;font-size:10px}#mermaid-svg-Peauh0FL01XGU1q4 .stateLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.7}#mermaid-svg-Peauh0FL01XGU1q4 .edgeLabel text{fill:#333}#mermaid-svg-Peauh0FL01XGU1q4 .stateLabel text{fill:#000;font-size:10px;font-weight:bold;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-Peauh0FL01XGU1q4 .node circle.state-start{fill:black;stroke:black}#mermaid-svg-Peauh0FL01XGU1q4 .node circle.state-end{fill:black;stroke:white;stroke-width:1.5}#mermaid-svg-Peauh0FL01XGU1q4 #statediagram-barbEnd{fill:#9370db}#mermaid-svg-Peauh0FL01XGU1q4 .statediagram-cluster rect{fill:#ECECFF;stroke:#9370db;stroke-width:1px}#mermaid-svg-Peauh0FL01XGU1q4 .statediagram-cluster rect.outer{rx:5px;ry:5px}#mermaid-svg-Peauh0FL01XGU1q4 .statediagram-state .divider{stroke:#9370db}#mermaid-svg-Peauh0FL01XGU1q4 .statediagram-state .title-state{rx:5px;ry:5px}#mermaid-svg-Peauh0FL01XGU1q4 .statediagram-cluster.statediagram-cluster .inner{fill:white}#mermaid-svg-Peauh0FL01XGU1q4 .statediagram-cluster.statediagram-cluster-alt .inner{fill:#e0e0e0}#mermaid-svg-Peauh0FL01XGU1q4 .statediagram-cluster .inner{rx:0;ry:0}#mermaid-svg-Peauh0FL01XGU1q4 .statediagram-state rect.basic{rx:5px;ry:5px}#mermaid-svg-Peauh0FL01XGU1q4 .statediagram-state rect.divider{stroke-dasharray:10,10;fill:#efefef}#mermaid-svg-Peauh0FL01XGU1q4 .note-edge{stroke-dasharray:5}#mermaid-svg-Peauh0FL01XGU1q4 .statediagram-note rect{fill:#fff5ad;stroke:#aa3;stroke-width:1px;rx:0;ry:0}:root{--mermaid-font-family: '"trebuchet ms", verdana, arial';--mermaid-font-family: "Comic Sans MS", "Comic Sans", cursive}#mermaid-svg-Peauh0FL01XGU1q4 .error-icon{fill:#522}#mermaid-svg-Peauh0FL01XGU1q4 .error-text{fill:#522;stroke:#522}#mermaid-svg-Peauh0FL01XGU1q4 .edge-thickness-normal{stroke-width:2px}#mermaid-svg-Peauh0FL01XGU1q4 .edge-thickness-thick{stroke-width:3.5px}#mermaid-svg-Peauh0FL01XGU1q4 .edge-pattern-solid{stroke-dasharray:0}#mermaid-svg-Peauh0FL01XGU1q4 .edge-pattern-dashed{stroke-dasharray:3}#mermaid-svg-Peauh0FL01XGU1q4 .edge-pattern-dotted{stroke-dasharray:2}#mermaid-svg-Peauh0FL01XGU1q4 .marker{fill:#333}#mermaid-svg-Peauh0FL01XGU1q4 .marker.cross{stroke:#333} :root { --mermaid-font-family: "trebuchet ms", verdana, arial;} #mermaid-svg-Peauh0FL01XGU1q4 { color: rgba(0, 0, 0, 0.75); font: ; } 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中。
显示一个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()方法,其调用链如图:
#mermaid-svg-8XpwHNmsSZeN162P .label{font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family);fill:#333;color:#333}#mermaid-svg-8XpwHNmsSZeN162P .label text{fill:#333}#mermaid-svg-8XpwHNmsSZeN162P .node rect,#mermaid-svg-8XpwHNmsSZeN162P .node circle,#mermaid-svg-8XpwHNmsSZeN162P .node ellipse,#mermaid-svg-8XpwHNmsSZeN162P .node polygon,#mermaid-svg-8XpwHNmsSZeN162P .node path{fill:#ECECFF;stroke:#9370db;stroke-width:1px}#mermaid-svg-8XpwHNmsSZeN162P .node .label{text-align:center;fill:#333}#mermaid-svg-8XpwHNmsSZeN162P .node.clickable{cursor:pointer}#mermaid-svg-8XpwHNmsSZeN162P .arrowheadPath{fill:#333}#mermaid-svg-8XpwHNmsSZeN162P .edgePath .path{stroke:#333;stroke-width:1.5px}#mermaid-svg-8XpwHNmsSZeN162P .flowchart-link{stroke:#333;fill:none}#mermaid-svg-8XpwHNmsSZeN162P .edgeLabel{background-color:#e8e8e8;text-align:center}#mermaid-svg-8XpwHNmsSZeN162P .edgeLabel rect{opacity:0.9}#mermaid-svg-8XpwHNmsSZeN162P .edgeLabel span{color:#333}#mermaid-svg-8XpwHNmsSZeN162P .cluster rect{fill:#ffffde;stroke:#aa3;stroke-width:1px}#mermaid-svg-8XpwHNmsSZeN162P .cluster text{fill:#333}#mermaid-svg-8XpwHNmsSZeN162P div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family);font-size:12px;background:#ffffde;border:1px solid #aa3;border-radius:2px;pointer-events:none;z-index:100}#mermaid-svg-8XpwHNmsSZeN162P .actor{stroke:#ccf;fill:#ECECFF}#mermaid-svg-8XpwHNmsSZeN162P text.actor>tspan{fill:#000;stroke:none}#mermaid-svg-8XpwHNmsSZeN162P .actor-line{stroke:grey}#mermaid-svg-8XpwHNmsSZeN162P .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333}#mermaid-svg-8XpwHNmsSZeN162P .messageLine1{stroke-width:1.5;stroke-dasharray:2, 2;stroke:#333}#mermaid-svg-8XpwHNmsSZeN162P #arrowhead path{fill:#333;stroke:#333}#mermaid-svg-8XpwHNmsSZeN162P .sequenceNumber{fill:#fff}#mermaid-svg-8XpwHNmsSZeN162P #sequencenumber{fill:#333}#mermaid-svg-8XpwHNmsSZeN162P #crosshead path{fill:#333;stroke:#333}#mermaid-svg-8XpwHNmsSZeN162P .messageText{fill:#333;stroke:#333}#mermaid-svg-8XpwHNmsSZeN162P .labelBox{stroke:#ccf;fill:#ECECFF}#mermaid-svg-8XpwHNmsSZeN162P .labelText,#mermaid-svg-8XpwHNmsSZeN162P .labelText>tspan{fill:#000;stroke:none}#mermaid-svg-8XpwHNmsSZeN162P .loopText,#mermaid-svg-8XpwHNmsSZeN162P .loopText>tspan{fill:#000;stroke:none}#mermaid-svg-8XpwHNmsSZeN162P .loopLine{stroke-width:2px;stroke-dasharray:2, 2;stroke:#ccf;fill:#ccf}#mermaid-svg-8XpwHNmsSZeN162P .note{stroke:#aa3;fill:#fff5ad}#mermaid-svg-8XpwHNmsSZeN162P .noteText,#mermaid-svg-8XpwHNmsSZeN162P .noteText>tspan{fill:#000;stroke:none}#mermaid-svg-8XpwHNmsSZeN162P .activation0{fill:#f4f4f4;stroke:#666}#mermaid-svg-8XpwHNmsSZeN162P .activation1{fill:#f4f4f4;stroke:#666}#mermaid-svg-8XpwHNmsSZeN162P .activation2{fill:#f4f4f4;stroke:#666}#mermaid-svg-8XpwHNmsSZeN162P .mermaid-main-font{font-family:"trebuchet ms", verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-8XpwHNmsSZeN162P .section{stroke:none;opacity:0.2}#mermaid-svg-8XpwHNmsSZeN162P .section0{fill:rgba(102,102,255,0.49)}#mermaid-svg-8XpwHNmsSZeN162P .section2{fill:#fff400}#mermaid-svg-8XpwHNmsSZeN162P .section1,#mermaid-svg-8XpwHNmsSZeN162P .section3{fill:#fff;opacity:0.2}#mermaid-svg-8XpwHNmsSZeN162P .sectionTitle0{fill:#333}#mermaid-svg-8XpwHNmsSZeN162P .sectionTitle1{fill:#333}#mermaid-svg-8XpwHNmsSZeN162P .sectionTitle2{fill:#333}#mermaid-svg-8XpwHNmsSZeN162P .sectionTitle3{fill:#333}#mermaid-svg-8XpwHNmsSZeN162P .sectionTitle{text-anchor:start;font-size:11px;text-height:14px;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-8XpwHNmsSZeN162P .grid .tick{stroke:#d3d3d3;opacity:0.8;shape-rendering:crispEdges}#mermaid-svg-8XpwHNmsSZeN162P .grid .tick text{font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-8XpwHNmsSZeN162P .grid path{stroke-width:0}#mermaid-svg-8XpwHNmsSZeN162P .today{fill:none;stroke:red;stroke-width:2px}#mermaid-svg-8XpwHNmsSZeN162P .task{stroke-width:2}#mermaid-svg-8XpwHNmsSZeN162P .taskText{text-anchor:middle;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-8XpwHNmsSZeN162P .taskText:not([font-size]){font-size:11px}#mermaid-svg-8XpwHNmsSZeN162P .taskTextOutsideRight{fill:#000;text-anchor:start;font-size:11px;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-8XpwHNmsSZeN162P .taskTextOutsideLeft{fill:#000;text-anchor:end;font-size:11px}#mermaid-svg-8XpwHNmsSZeN162P .task.clickable{cursor:pointer}#mermaid-svg-8XpwHNmsSZeN162P .taskText.clickable{cursor:pointer;fill:#003163 !important;font-weight:bold}#mermaid-svg-8XpwHNmsSZeN162P .taskTextOutsideLeft.clickable{cursor:pointer;fill:#003163 !important;font-weight:bold}#mermaid-svg-8XpwHNmsSZeN162P .taskTextOutsideRight.clickable{cursor:pointer;fill:#003163 !important;font-weight:bold}#mermaid-svg-8XpwHNmsSZeN162P .taskText0,#mermaid-svg-8XpwHNmsSZeN162P .taskText1,#mermaid-svg-8XpwHNmsSZeN162P .taskText2,#mermaid-svg-8XpwHNmsSZeN162P .taskText3{fill:#fff}#mermaid-svg-8XpwHNmsSZeN162P .task0,#mermaid-svg-8XpwHNmsSZeN162P .task1,#mermaid-svg-8XpwHNmsSZeN162P .task2,#mermaid-svg-8XpwHNmsSZeN162P .task3{fill:#8a90dd;stroke:#534fbc}#mermaid-svg-8XpwHNmsSZeN162P .taskTextOutside0,#mermaid-svg-8XpwHNmsSZeN162P .taskTextOutside2{fill:#000}#mermaid-svg-8XpwHNmsSZeN162P .taskTextOutside1,#mermaid-svg-8XpwHNmsSZeN162P .taskTextOutside3{fill:#000}#mermaid-svg-8XpwHNmsSZeN162P .active0,#mermaid-svg-8XpwHNmsSZeN162P .active1,#mermaid-svg-8XpwHNmsSZeN162P .active2,#mermaid-svg-8XpwHNmsSZeN162P .active3{fill:#bfc7ff;stroke:#534fbc}#mermaid-svg-8XpwHNmsSZeN162P .activeText0,#mermaid-svg-8XpwHNmsSZeN162P .activeText1,#mermaid-svg-8XpwHNmsSZeN162P .activeText2,#mermaid-svg-8XpwHNmsSZeN162P .activeText3{fill:#000 !important}#mermaid-svg-8XpwHNmsSZeN162P .done0,#mermaid-svg-8XpwHNmsSZeN162P .done1,#mermaid-svg-8XpwHNmsSZeN162P .done2,#mermaid-svg-8XpwHNmsSZeN162P .done3{stroke:grey;fill:#d3d3d3;stroke-width:2}#mermaid-svg-8XpwHNmsSZeN162P .doneText0,#mermaid-svg-8XpwHNmsSZeN162P .doneText1,#mermaid-svg-8XpwHNmsSZeN162P .doneText2,#mermaid-svg-8XpwHNmsSZeN162P .doneText3{fill:#000 !important}#mermaid-svg-8XpwHNmsSZeN162P .crit0,#mermaid-svg-8XpwHNmsSZeN162P .crit1,#mermaid-svg-8XpwHNmsSZeN162P .crit2,#mermaid-svg-8XpwHNmsSZeN162P .crit3{stroke:#f88;fill:red;stroke-width:2}#mermaid-svg-8XpwHNmsSZeN162P .activeCrit0,#mermaid-svg-8XpwHNmsSZeN162P .activeCrit1,#mermaid-svg-8XpwHNmsSZeN162P .activeCrit2,#mermaid-svg-8XpwHNmsSZeN162P .activeCrit3{stroke:#f88;fill:#bfc7ff;stroke-width:2}#mermaid-svg-8XpwHNmsSZeN162P .doneCrit0,#mermaid-svg-8XpwHNmsSZeN162P .doneCrit1,#mermaid-svg-8XpwHNmsSZeN162P .doneCrit2,#mermaid-svg-8XpwHNmsSZeN162P .doneCrit3{stroke:#f88;fill:#d3d3d3;stroke-width:2;cursor:pointer;shape-rendering:crispEdges}#mermaid-svg-8XpwHNmsSZeN162P .milestone{transform:rotate(45deg) scale(0.8, 0.8)}#mermaid-svg-8XpwHNmsSZeN162P .milestoneText{font-style:italic}#mermaid-svg-8XpwHNmsSZeN162P .doneCritText0,#mermaid-svg-8XpwHNmsSZeN162P .doneCritText1,#mermaid-svg-8XpwHNmsSZeN162P .doneCritText2,#mermaid-svg-8XpwHNmsSZeN162P .doneCritText3{fill:#000 !important}#mermaid-svg-8XpwHNmsSZeN162P .activeCritText0,#mermaid-svg-8XpwHNmsSZeN162P .activeCritText1,#mermaid-svg-8XpwHNmsSZeN162P .activeCritText2,#mermaid-svg-8XpwHNmsSZeN162P .activeCritText3{fill:#000 !important}#mermaid-svg-8XpwHNmsSZeN162P .titleText{text-anchor:middle;font-size:18px;fill:#000;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-8XpwHNmsSZeN162P g.classGroup text{fill:#9370db;stroke:none;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family);font-size:10px}#mermaid-svg-8XpwHNmsSZeN162P g.classGroup text .title{font-weight:bolder}#mermaid-svg-8XpwHNmsSZeN162P g.clickable{cursor:pointer}#mermaid-svg-8XpwHNmsSZeN162P g.classGroup rect{fill:#ECECFF;stroke:#9370db}#mermaid-svg-8XpwHNmsSZeN162P g.classGroup line{stroke:#9370db;stroke-width:1}#mermaid-svg-8XpwHNmsSZeN162P .classLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5}#mermaid-svg-8XpwHNmsSZeN162P .classLabel .label{fill:#9370db;font-size:10px}#mermaid-svg-8XpwHNmsSZeN162P .relation{stroke:#9370db;stroke-width:1;fill:none}#mermaid-svg-8XpwHNmsSZeN162P .dashed-line{stroke-dasharray:3}#mermaid-svg-8XpwHNmsSZeN162P #compositionStart{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-8XpwHNmsSZeN162P #compositionEnd{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-8XpwHNmsSZeN162P #aggregationStart{fill:#ECECFF;stroke:#9370db;stroke-width:1}#mermaid-svg-8XpwHNmsSZeN162P #aggregationEnd{fill:#ECECFF;stroke:#9370db;stroke-width:1}#mermaid-svg-8XpwHNmsSZeN162P #dependencyStart{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-8XpwHNmsSZeN162P #dependencyEnd{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-8XpwHNmsSZeN162P #extensionStart{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-8XpwHNmsSZeN162P #extensionEnd{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-8XpwHNmsSZeN162P .commit-id,#mermaid-svg-8XpwHNmsSZeN162P .commit-msg,#mermaid-svg-8XpwHNmsSZeN162P .branch-label{fill:lightgrey;color:lightgrey;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-8XpwHNmsSZeN162P .pieTitleText{text-anchor:middle;font-size:25px;fill:#000;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-8XpwHNmsSZeN162P .slice{font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-8XpwHNmsSZeN162P g.stateGroup text{fill:#9370db;stroke:none;font-size:10px;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-8XpwHNmsSZeN162P g.stateGroup text{fill:#9370db;fill:#333;stroke:none;font-size:10px}#mermaid-svg-8XpwHNmsSZeN162P g.statediagram-cluster .cluster-label text{fill:#333}#mermaid-svg-8XpwHNmsSZeN162P g.stateGroup .state-title{font-weight:bolder;fill:#000}#mermaid-svg-8XpwHNmsSZeN162P g.stateGroup rect{fill:#ECECFF;stroke:#9370db}#mermaid-svg-8XpwHNmsSZeN162P g.stateGroup line{stroke:#9370db;stroke-width:1}#mermaid-svg-8XpwHNmsSZeN162P .transition{stroke:#9370db;stroke-width:1;fill:none}#mermaid-svg-8XpwHNmsSZeN162P .stateGroup .composit{fill:white;border-bottom:1px}#mermaid-svg-8XpwHNmsSZeN162P .stateGroup .alt-composit{fill:#e0e0e0;border-bottom:1px}#mermaid-svg-8XpwHNmsSZeN162P .state-note{stroke:#aa3;fill:#fff5ad}#mermaid-svg-8XpwHNmsSZeN162P .state-note text{fill:black;stroke:none;font-size:10px}#mermaid-svg-8XpwHNmsSZeN162P .stateLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.7}#mermaid-svg-8XpwHNmsSZeN162P .edgeLabel text{fill:#333}#mermaid-svg-8XpwHNmsSZeN162P .stateLabel text{fill:#000;font-size:10px;font-weight:bold;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-8XpwHNmsSZeN162P .node circle.state-start{fill:black;stroke:black}#mermaid-svg-8XpwHNmsSZeN162P .node circle.state-end{fill:black;stroke:white;stroke-width:1.5}#mermaid-svg-8XpwHNmsSZeN162P #statediagram-barbEnd{fill:#9370db}#mermaid-svg-8XpwHNmsSZeN162P .statediagram-cluster rect{fill:#ECECFF;stroke:#9370db;stroke-width:1px}#mermaid-svg-8XpwHNmsSZeN162P .statediagram-cluster rect.outer{rx:5px;ry:5px}#mermaid-svg-8XpwHNmsSZeN162P .statediagram-state .divider{stroke:#9370db}#mermaid-svg-8XpwHNmsSZeN162P .statediagram-state .title-state{rx:5px;ry:5px}#mermaid-svg-8XpwHNmsSZeN162P .statediagram-cluster.statediagram-cluster .inner{fill:white}#mermaid-svg-8XpwHNmsSZeN162P .statediagram-cluster.statediagram-cluster-alt .inner{fill:#e0e0e0}#mermaid-svg-8XpwHNmsSZeN162P .statediagram-cluster .inner{rx:0;ry:0}#mermaid-svg-8XpwHNmsSZeN162P .statediagram-state rect.basic{rx:5px;ry:5px}#mermaid-svg-8XpwHNmsSZeN162P .statediagram-state rect.divider{stroke-dasharray:10,10;fill:#efefef}#mermaid-svg-8XpwHNmsSZeN162P .note-edge{stroke-dasharray:5}#mermaid-svg-8XpwHNmsSZeN162P .statediagram-note rect{fill:#fff5ad;stroke:#aa3;stroke-width:1px;rx:0;ry:0}:root{--mermaid-font-family: '"trebuchet ms", verdana, arial';--mermaid-font-family: "Comic Sans MS", "Comic Sans", cursive}#mermaid-svg-8XpwHNmsSZeN162P .error-icon{fill:#522}#mermaid-svg-8XpwHNmsSZeN162P .error-text{fill:#522;stroke:#522}#mermaid-svg-8XpwHNmsSZeN162P .edge-thickness-normal{stroke-width:2px}#mermaid-svg-8XpwHNmsSZeN162P .edge-thickness-thick{stroke-width:3.5px}#mermaid-svg-8XpwHNmsSZeN162P .edge-pattern-solid{stroke-dasharray:0}#mermaid-svg-8XpwHNmsSZeN162P .edge-pattern-dashed{stroke-dasharray:3}#mermaid-svg-8XpwHNmsSZeN162P .edge-pattern-dotted{stroke-dasharray:2}#mermaid-svg-8XpwHNmsSZeN162P .marker{fill:#333}#mermaid-svg-8XpwHNmsSZeN162P .marker.cross{stroke:#333} :root { --mermaid-font-family: "trebuchet ms", verdana, arial;} #mermaid-svg-8XpwHNmsSZeN162P { color: rgba(0, 0, 0, 0.75); font: ; } 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显示功能的。
先看一下使用姿势,先在一个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 } }