APP路由框架与组件化简析,腾讯架构师深入讲解Android开发

2022/3/2 23:17:10

本文主要是介绍APP路由框架与组件化简析,腾讯架构师深入讲解Android开发,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

第二步:调用

void jumpSecondActivityUseFilter() {

Intent intent = new Intent();

intent.setAction(“com.snail.activityforresultexample.SecondActivity”);

startActivity(intent);

}

如果牵扯到数据传递写法上会更复杂一些,隐式调用的缺点有如下几点:

  • 首先manifest中定义复杂,相对应的会导致暴露的协议变的复杂,不易维护扩展。

  • 其次,不同Activity都要不同的action配置,每次增减修改Activity都会很麻烦,对比开发者非常不友好,增加了协作难度。

  • 最后,Activity的export属性并不建议都设置成True,这是降低风险的一种方式,一般都是收归到一个Activity,DeeplinkActivitiy统一处理跳转,这种场景下,DeeplinkActivitiy就兼具路由功能,隐式调用的场景下,新Activitiy的增减势必每次都要调整路由表,这会导致开发效率降低,风险增加。

可以看到系统原生的路由框架,并没太多考虑团队协同的开发模式,多限定在一个模块内部多个业务间直接相互引用,基本都要代码级依赖,对于代码及业务隔离很不友好。如不考虑之前Dex方法树超限制,可以认为三方路由框架完全是为了团队协同而创建的

APP三方路由框架需具备的能力


目前市面上大部分的路由框架都能搞定上述问题,简单整理下现在三方路由的能力,可归纳如下:

  • 路由表生成能力:业务组件**[UI业务及服务]**自动扫描及注册逻辑,需要扩展性好,无需入侵原有代码逻辑

  • scheme与业务映射逻辑 :无需依赖具体实现,做到代码隔离

  • 基础路由跳转能力 :页面跳转能力的支持

  • 服务类组件的支持 :如去某个服务组件获取一些配置等

  • [扩展]路由拦截逻辑:比如登陆,统一鉴权

  • 可定制的降级逻辑:找不到组件时的兜底

可以看下一个典型的Arouter用法,第一步:对新增页面添加Router Scheme 声明,

@Route(path = “/test/activity2”)

public class Test2Activity extends AppCompatActivity {

}

build阶段会根据注解搜集路由scheme,生成路由表。第二步使用

ARouter.getInstance()

.build("/test/activity2")

.navigation(this);

如上,在ARouter框架下,仅需要字符串scheme,无需依赖任何Test2Activity就可实现路由跳转。

APP路由框架的实现


路由框架实现的核心是建立scheme和组件**[Activity或者其他服务]**的映射关系,也就是路由表,并能根据路由表路由到对应组件的能力。其实分两部分,第一部分路由表的生成,第二部分,路由表的查询

路由表的自动生成

生成路由表的方式有很多,最简单的就是维护一个公共文件或者类,里面映射好每个实现组件跟scheme,

image.png

不过,这种做法缺点很明显:每次增删修改都要都要修改这个表,对于协同非常不友好,不符合解决协同问题的初衷。不过,最终的路由表倒是都是这条路,就是将所有的Scheme搜集到一个对象中,只是实现方式的差别,目前几乎所有的三方路由框架都是借助注解+APT[Annotation Processing Tool]工具+AOP(Aspect-Oriented Programming,面向切面编程)来实现的,基本流程如下:

其中牵扯的技术有注解、APT(Annotation Processing Tool)、AOP(Aspect-Oriented Programming,面向切面编程)。APT常用的有JavaPoet,主要是遍历所有类,找到被注解的Java类,然后聚合生成路由表,由于组件可能有很多,路由表可能也有也有多个,之后,这些生成的辅助类会跟源码一并被编译成class文件,之后利用AOP技术【如ASM或者JavaAssist】,扫描这些生成的class,聚合路由表,并填充到之前的占位方法中,完成自动注册的逻辑。

JavaPoet如何搜集并生成路由表集合?

以ARouter框架为例,先定义Router框架需要的注解如:

@Target({ElementType.TYPE})

@Retention(RetentionPolicy.CLASS)

public @interface Route {

/**

  • Path of route

*/

String path();

该注解用于标注需要路由的组件,用法如下:

@Route(path = “/test/activity1”, name = “测试用 Activity”)

public class Test1Activity extends BaseActivity {

@Autowired

int age = 10;

之后利用APT扫描所有被注解的类,生成路由表,实现参考如下:

@Override

public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {

if (CollectionUtils.isNotEmpty(annotations)) {

Set<? extends Element> routeElements = roundEnv.getElementsAnnotatedWith(Route.class);

this.parseRoutes(routeElements);

return false;

}

private void parseRoutes(Set<? extends Element> routeElements) throws IOException {

// Generate groups

String groupFileName = NAME_OF_GROUP + groupName;

JavaFile.builder(PACKAGE_OF_GENERATE_FILE,

TypeSpec.classBuilder(groupFileName)

.addJavadoc(WARNING_TIPS)

.addSuperinterface(ClassName.get(type_IRouteGroup))

.addModifiers(PUBLIC)

.addMethod(loadIntoMethodOfGroupBuilder.build())

.build()

).build().writeTo(mFiler);

产物如下:包含路由表,及局部注册入口。

自动注册:ASM搜集上述路由表并聚合插入Init代码区

为了能够插入到Init代码区,首先需要预留一个位置,一般定义一个空函数,以待后续填充:

public class RouterInitializer {

public static void init(boolean debug, Class webActivityClass, IRouterInterceptor… interceptors) {

loadRouterTables();

}

//自动注册代码

public static void loadRouterTables() {

}

}

首先利用AOP工具,遍历上述APT中间产物,聚合路由表,并注册到预留初始化位置,遍历的过程牵扯是gradle transform的过程,

  • 搜集目标,聚合路由表

/*扫描jar/

fun scanJar(jarFile: File, dest: File?) {

val file = JarFile(jarFile)

var enumeration = file.entries()

while (enumeration.hasMoreElements()) {

val jarEntry = enumeration.nextElement()

if (jarEntry.name.endsWith(“XXRouterTable.class”)) {

val inputStream = file.getInputStream(jarEntry)

val classReader = ClassReader(inputStream)

if (Arrays.toString(classReader.interfaces)

.contains(“IHTRouterTBCollect”)

) {

tableList.add(

Pair(

classReader.className,

dest?.absolutePath

)

)

}

inputStream.close()

} else if (jarEntry.name.endsWith(“HTRouterInitializer.class”)) {

registerInitClass = dest

}

}

file.close()

}

  • 对目标Class注入路由表初始化代码

fun asmInsertMethod(originFile: File?) {

val optJar = File(originFile?.parent, originFile?.name + “.opt”)

if (optJar.exists())

optJar.delete()

val jarFile = JarFile(originFile)

val enumeration = jarFile.entries()

val jarOutputStream = JarOutputStream(FileOutputStream(optJar))

while (enumeration.hasMoreElements()) {

val jarEntry = enumeration.nextElement()

val entryName = jarEntry.getName()

val zipEntry = ZipEntry(entryName)

val inputStream = jarFile.getInputStream(jarEntry)

//插桩class

if (entryName.endsWith(“RouterInitializer.class”)) {

//class文件处理

jarOutputStream.putNextEntry(zipEntry)

val classReader = ClassReader(IOUtils.toByteArray(inputStream))

val classWriter = ClassWriter(classReader, ClassWriter.COMPUTE_MAXS)

val cv = RegisterClassVisitor(Opcodes.ASM5, classWriter,tableList)

classReader.accept(cv, EXPAND_FRAMES)

val code = classWriter.toByteArray()

jarOutputStream.write(code)

} else {

jarOutputStream.putNextEntry(zipEntry)

jarOutputStream.write(IOUtils.toByteArray(inputStream))

}

jarOutputStream.closeEntry()

}

//结束

jarOutputStream.close()

jarFile.close()

if (originFile?.exists() == true) {

Files.delete(originFile.toPath())

}

optJar.renameTo(originFile)

}

最终RouterInitializer.class的 loadRouterTables会被修改成如下填充好的代码:

public static void loadRouterTables() {

register(“com.alibaba.android.arouter.routes.ARouter R o o t Root Rootmodulejava”);

register(“com.alibaba.android.arouter.routes.ARouter R o o t Root Rootmodulekotlin”);

register(“com.alibaba.android.arouter.routes.ARouter R o o t Root Rootarouterapi”);

register(“com.alibaba.android.arouter.routes.ARouter I n t e r c e p t o r s Interceptors Interceptorsmodulejava”);

分享读者

作者2013年java转到Android开发,在小厂待过,也去过华为,OPPO等大厂待过,18年四月份进了阿里一直到现在。

被人面试过,也面试过很多人。深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长,不成体系的学习效果低效漫长,而且极易碰到天花板技术停滞不前!

我们整理了一份阿里P7级别的Android架构师全套学习资料,特别适合有3-5年以上经验的小伙伴深入学习提升。

主要包括腾讯,以及字节跳动,阿里,华为,小米,等一线互联网公司主流架构技术。如果你有需要,尽管拿走好了。

腾讯T3架构师学习专题资料

如果你觉得自己学习效率低,缺乏正确的指导,可以点击加入资源丰富,学习氛围浓厚的技术圈一起学习交流吧!

群内有许多来自一线的技术大牛,也有在小厂或外包公司奋斗的码农,我们致力打造一个平等,高质量的Android交流圈子,不一定能短期就让每个人的技术突飞猛进,但从长远来说,眼光,格局,长远发展的方向才是最重要的。

35岁中年危机大多是因为被短期的利益牵着走,过早压榨掉了价值,如果能一开始就树立一个正确的长远的职业规划。35岁后的你只会比周围的人更值钱。

,等一线互联网公司主流架构技术。如果你有需要,尽管拿走好了。

[外链图片转存中…(img-vyJyWDjO-1646229259524)]

如果你觉得自己学习效率低,缺乏正确的指导,可以点击加入资源丰富,学习氛围浓厚的技术圈一起学习交流吧!

群内有许多来自一线的技术大牛,也有在小厂或外包公司奋斗的码农,我们致力打造一个平等,高质量的Android交流圈子,不一定能短期就让每个人的技术突飞猛进,但从长远来说,眼光,格局,长远发展的方向才是最重要的。

35岁中年危机大多是因为被短期的利益牵着走,过早压榨掉了价值,如果能一开始就树立一个正确的长远的职业规划。35岁后的你只会比周围的人更值钱。



这篇关于APP路由框架与组件化简析,腾讯架构师深入讲解Android开发的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程