android自动路由,Android组件化之路由

项目搭建

1.需求分析

2.功能模块划分

3.基础工具类、框架、接口提取

4.创建路由接口

5.配置gradle完成模块单独测试

if (appb_lib) {

apply plugin: 'com.android.library'

}else {

apply plugin: 'com.android.application'

}

安装 Octotree浏览器插件查看项目结构

dd1c1f52fbf5

image

baseMoudle路由工具类 RouterTools

//添加router与activity对应关系

public void addRouter(String router, Class extends Activity> clazz) {

mRouterMap.put(router, clazz);

}

//根据router跳转界面

public void navigate(Context context, String router) {

System.out.println(mRouterMap.get(router));

context.startActivity(new Intent(context, mRouterMap.get(router)));

}

//初始化router与activity对应关系数据

public void init(Context context) {

//用于asm增强

loadRouterMap();

if (isUseAms) {

return;

}

//如果没有使用asm 则采用遍历apk中的所有class,找到实现IRouter的类反射调用

String packageResourcePath = context.getApplicationContext().getPackageResourcePath();

try {

DexFile dexFile = new DexFile(packageResourcePath);

Enumeration entries = dexFile.entries();

while (entries.hasMoreElements()) {

String element = entries.nextElement();

Log.e("RouterTools", "init: " + element);

if (element.contains("com.example.router")) {

Log.e("RouterTools", "init: " + element);

Class> aClass = Class.forName(element);

int modifiers = aClass.getModifiers();

if (IRouter.class.isAssignableFrom(aClass) && !Modifier.isInterface(modifiers)) {

((IRouter) aClass.newInstance()).addRouter(mRouterMap);

}

}

}

} catch (Exception e) {

e.printStackTrace();

Log.e("RouterTools", "init: ", e);

}

for (Map.Entry> stringClassEntry : mRouterMap.entrySet()) {

Log.e("RouterTools", "stringClassEntry: "

+ stringClassEntry.getKey() + "--" + stringClassEntry.getValue());

}

}

//用于asm增强函数

private static void loadRouterMap() {

isUseAms = false;

//在此处完成字节码插桩

//register("com.example.router.appc.MyRouter$$APPC")

//register("com.example.router.appb.MyRouter$$APPB")

}

//用于asm调用,如果使用了ams就不需要遍历apk

private static void register(String className) {

isUseAms = true;

Log.e("RouterTools", "register: " + className);

try {

Class> aClass = Class.forName(className);

int modifiers = aClass.getModifiers();

if (IRouter.class.isAssignableFrom(aClass) && !Modifier.isInterface(modifiers)) {

((IRouter) aClass.newInstance()).addRouter(mRouterMap);

}

} catch (Exception e) {

e.printStackTrace();

}

}

}

APT自动生成个个模块实现IRouter的类

@AutoService(Processor.class)

@SupportedAnnotationTypes({"com.example.annotation.MyRouter"})

@SupportedSourceVersion(SourceVersion.RELEASE_8)

@SupportedOptions({"MODULE_NAME"})

public class MyRouterProcessor extends AbstractProcessor {

private Filer filer;

private Elements elementUtils;

private Messager printMessage;

private ArrayList classNames = new ArrayList<>();

@Override

public synchronized void init(ProcessingEnvironment processingEnvironment) {

super.init(processingEnvironment);

filer = processingEnv.getFiler();

elementUtils = processingEnv.getElementUtils();

printMessage = processingEnv.getMessager();

println("init-----------------------------------------------");

}

@Override

public boolean process(Set extends TypeElement> set, RoundEnvironment roundEnvironment) {

classNames.clear();

println("process-----------------------------------------------");

/** 获取对应模块的 MODULE_NAME

javaCompileOptions {

annotationProcessorOptions {

arguments = [MODULE_NAME: project.getName()]

}

}

**/

String module_name = processingEnv.getOptions().get("MODULE_NAME");

Set extends Element> elements = roundEnvironment.getElementsAnnotatedWith(MyRouter.class);

//遍历找到带MyRouter注解的Activity

for (Element element : elements) {

if (element instanceof TypeElement) {//类注解

TypeElement typeElement = (TypeElement) element;

PackageElement packageOf = elementUtils.getPackageOf(typeElement);

Name qualifiedName = typeElement.getQualifiedName();

println(packageOf.getQualifiedName().toString() + "--" + qualifiedName);

MyRouter annotation = typeElement.getAnnotation(MyRouter.class);

classNames.add(new MyRouterBean(qualifiedName.toString(), annotation.value()));

}

}

println("process---------------------测试--------------------------");

for (TypeElement typeElement : set) {

PackageElement packageOf = elementUtils.getPackageOf(typeElement);

Name qualifiedName = typeElement.getQualifiedName();

println(packageOf.getQualifiedName().toString() + "--" + qualifiedName);

}

try {

writeToFile(module_name);

} catch (IOException e) {

e.printStackTrace();

}

return true;

}

//生成Java代码

private void writeToFile(String module_name) throws IOException {

if (classNames.isEmpty()) {

return;

}

//ClassName会自动倒入包

ClassName string = ClassName.get("java.lang",

"String");

ClassName hashMap = ClassName.get("java.util",

"HashMap");

ClassName activity = ClassName.get("",

"? extends android.app.Activity");

ClassName clazz = ClassName.get("java.lang",

"Class");

//泛形参数

ParameterizedTypeName typeName = ParameterizedTypeName.get(clazz, activity);

ParameterizedTypeName type = ParameterizedTypeName.get(hashMap, string, typeName);

MethodSpec.Builder addRouter = MethodSpec.methodBuilder("addRouter")

.addModifiers(Modifier.PUBLIC)

.addAnnotation(Override.class)

.addJavadoc("addRouter\r\n@routerMap 路由map")

.returns(TypeName.VOID)

.addParameter(

type

,"routerMap");

ClassName routerTools = ClassName.get("com.example.basemoudle",

"RouterTools");

for (MyRouterBean className : classNames) {

// addRouter.addStatement("$T.getInstance().addRouter(\"$L\",$L.class)",routerTools, className.getRouter(), className.getClassName());

addRouter.addStatement("routerMap.put(\"$L\",$L.class)", className.getRouter(), className.getClassName());

}

TypeSpec typeSpec = TypeSpec.classBuilder("MyRouter$$" + module_name.toUpperCase())

.addSuperinterface( ClassName.get("com.example.basemoudle","IRouter"))

.addModifiers(Modifier.PUBLIC)

.addMethod(addRouter.build())

// .addAnnotation(AnnotationSpec

// .builder(MyRouter.class)

// .addMember("value","value=$L","2")

// .build())

.build();

JavaFile javaFile = JavaFile.builder("com.example.router." + module_name.toLowerCase(), typeSpec)

.build();

javaFile.writeTo(filer);

}

private void println(String msg) {

printMessage.printMessage(Diagnostic.Kind.NOTE,

"MyProcessor:" + msg + "\r\n");

}

}

生成的代码如下:

//注解的类

@MyRouter("MainActivity")

class MainActivity : AppCompatActivity() {}

//app模块自动生成的代码

package com.example.router.app;

···

···

public class MyRouter$$APP implements IRouter {

/**

* addRouter

* @routerMap 路由map

*/

@Override

public void addRouter(HashMap> routerMap) {

routerMap.put("MainActivity",com.zmp.javaproject.MainActivity.class);

}

}

app 初始化即可

//初始化

RouterTools.getInstance().init(this)

//使用路由

RouterTools.getInstance().navigate(this,"appb/BMainActivity")

plugin优化

此时的路由框架已经可以使用,但是apk运行期间初始化需要遍历dex,比较耗时

自定义插件

package com.example.plugin;

import com.android.build.gradle.AppExtension;

import com.android.build.gradle.AppPlugin;

import com.example.plugin.tools.Logger;

import com.example.plugin.transform.MyRouterTransform;

import org.gradle.api.Plugin;

import org.gradle.api.Project;

import org.jetbrains.annotations.NotNull;

/**

* Created at 9:25 2020/9/23

*

* @author zmp

*

* des:

*/

public class MyPlugin implements Plugin {

@Override

public void apply(@NotNull Project project) {

Logger.make(project);

Logger.w("apply:init-----------------");

boolean hasAppPlugin = project.getPlugins().hasPlugin(AppPlugin.class);

Logger.w("apply:init-----------------" + hasAppPlugin);

if (hasAppPlugin) {

Logger.w("apply:init-----------------" + hasAppPlugin);

AppExtension android = project.getExtensions().getByType(AppExtension.class);

//register this plugin 注册MyRouterTransform

android.registerTransform(new MyRouterTransform(project));

}

}

}

自定义MyRouterTransform 完成插桩

/**

* Created at 9:41 2020/9/23

*

* @author zmp

*

* des:

*/

public class MyRouterTransform extends MyBaseTransform {

public MyRouterTransform(Project project) {

super(project);

}

protected void transformJar(File input, File dest) {

if (ScanUtil.shouldProcessPreDexJar(input.getAbsolutePath())) {

ScanUtil.scanJar(input, dest);

} else {

super.transformJar(input, dest);

}

}

protected void transformSingleFile(String parentPath, File input, File dest) {

if (!parentPath.endsWith(File.separator)) {

parentPath += File.separator;

}

String replace = input.getAbsolutePath().replace(parentPath, "");

if (!File.separator.equals("/")) {

replace = replace.replaceAll("\\\\", "/");

}

try {

if (ScanUtil.shouldProcessClass(replace)) {

ScanUtil.scanClass(input, dest);

}

FileUtils.copyFile(input, dest);

} catch (Exception e) {

e.printStackTrace();

}

}

protected void insertInitCodeTo() {

File containsInitClass = ScanUtil.fileContainsInitClass;

Logger.e("containsInitClass:" + containsInitClass);

Logger.e("containsInitClass:" + Arrays.toString(ScanUtil.CLASS_NAMES.toArray()));

if (containsInitClass != null) {

insertInitCodeIntoJarFile(containsInitClass);

}

}

/**

* generate code into jar file

*

* @param jarFile the jar file which contains LogisticsCenter.class

* @return

*/

private void insertInitCodeIntoJarFile(File jarFile) {

try {

File optJar = new File(jarFile.getParent(), jarFile.getName() + ".opt");

if (optJar.exists()) {

optJar.delete();

}

JarFile file = new JarFile(jarFile);

Enumeration enumeration = file.entries();

JarOutputStream jarOutputStream = new JarOutputStream(new FileOutputStream(optJar));

while (enumeration.hasMoreElements()) {

JarEntry jarEntry = (JarEntry) enumeration.nextElement();

String entryName = jarEntry.getName();

ZipEntry zipEntry = new ZipEntry(entryName);

InputStream inputStream = file.getInputStream(jarEntry);

jarOutputStream.putNextEntry(zipEntry);

if (ScanUtil.ASM_CLASS_NAME.equals(entryName)) {

byte[] bytes = referHackWhenInit(inputStream);

jarOutputStream.write(bytes);

} else {

jarOutputStream.write(IOUtils.toByteArray(inputStream));

}

inputStream.close();

jarOutputStream.closeEntry();

}

jarOutputStream.close();

file.close();

if (jarFile.exists()) {

jarFile.delete();

}

optJar.renameTo(jarFile);

} catch (Exception e) {

e.printStackTrace();

}

}

//refer hack class when object init

private byte[] referHackWhenInit(InputStream inputStream) {

Logger.e("referHackWhenInit");

byte[] bytes = null;

try {

ClassReader cr = new ClassReader(inputStream);

ClassWriter cw = new ClassWriter(cr, 0);

ClassVisitor cv = new MyClassVisitor(Opcodes.ASM5, cw);

cr.accept(cv, ClassReader.EXPAND_FRAMES);

bytes = cw.toByteArray();

} catch (IOException e) {

e.printStackTrace();

}

return bytes;

}

class MyClassVisitor extends ClassVisitor {

MyClassVisitor(int api, ClassVisitor cv) {

super(api, cv);

}

@Override

public void visit(int version, int access, String name, String signature,

String superName, String[] interfaces) {

super.visit(version, access, name, signature, superName, interfaces);

}

@Override

public MethodVisitor visitMethod(int access, String name, String desc,

String signature, String[] exceptions) {

MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions);

//generate code into this method

Logger.e("visitMethod:" + name);

if (name.equals(ScanUtil.GENERATE_TO_METHOD_NAME)) {

mv = new RouteMethodVisitor(api, mv, access, name, desc);

}

return mv;

}

}

static class RouteMethodVisitor extends AdviceAdapter {

protected RouteMethodVisitor(int api, MethodVisitor methodVisitor, int access, String name, String descriptor) {

super(api, methodVisitor, access, name, descriptor);

}

@Override

protected void onMethodEnter() {

super.onMethodEnter();

}

@Override

protected void onMethodExit(int opcode) {

super.onMethodExit(opcode);

//插入字节码

for (String className : ScanUtil.CLASS_NAMES) {

Logger.w("className:" + className);

push(className.replaceAll("/", "."));

invokeStatic(Type.getType(ScanUtil.ASM_CLASS_NAME_TYPE), new Method("register", "(Ljava/lang/String;)V"));

}

}

@Override

public void visitMaxs(int maxStack, int maxLocals) {

super.visitMaxs(maxStack + Integer.MAX_VALUE, maxLocals);

}

}

}

发布插件到 mavenLocal

apply plugin: 'java-library'

apply plugin: 'kotlin'

apply plugin: 'maven'

dependencies {

implementation fileTree(dir: 'libs', include: ['*.jar'])

implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"

implementation gradleApi()

implementation 'com.android.tools.build:gradle:4.0.1'

}

apply plugin: 'maven-publish'

publishing {

publications {

mavenJava(MavenPublication) {

groupId 'com.example.android'

artifactId "zhanpple"

version 1.0

from components.java

// more goes in here

}

}

repositories {

mavenLocal()

}

}

sourceCompatibility = "1.7"

targetCompatibility = "1.7"

dd1c1f52fbf5

image

使用插件 项目build.gradle中

repositories {

google()

jcenter()

mavenLocal()

}

dependencies {

...

...

classpath "com.example.android:zhanpple:1.0"

}

使用插件 app中

apply plugin: 'my.router.plugin'

apply plugin: 'my.router.plugin' 要与插件的gradle-plugins对应

dd1c1f52fbf5

image

编译app 对比字节码

dd1c1f52fbf5

image

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值