Android手撸一个简易路由Router
核心原理
目前市面上大部分的Router框架(ARouter,WMRouter)实现核心原理都差不多,在代码里加入的@Route注解,会在编译时期通过APT(Annotation Processing Tool)生成一些存储path和activityClass映射关系的类文件,然后app进程启动的时候会拿到这些类文件,把保存这些映射关系的数据读到内存里(保存在map里),然后在进行路由跳转的时候,通过build()方法传入要到达页面的路由地址。本文介绍如何实现一个简易路由Router来加深理解。
包结构
这里使用的是APT技术帮助我们在编译时自动生成我们需要用到相关的类,首先新建三个module ,arouter-api、lib-annotation和lib-annotation-compiler,需要注意的是lib-annotation和lib-annotation-compiler在New Module的时候要选的是Java or Kotlin Library 而不是其他。
arouter-api
arouter-api模块很简单,只有ARouter和IRouter接口。
ARouter是一个单例,内部维护着一个Map用于管理跳转需要的path路径和目标Activity,这里只做简单的处理。IRouter则是提供对外实现的接口,下面会用到。
ARouter.kt
class ARouter {
companion object {
private const val TAG = "ARouter"
private lateinit var activityMap: MutableMap<String, Class<out Activity?>>
@JvmStatic
val instance: ARouter by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) {
activityMap = mutableMapOf()
ARouter()
}
}
private lateinit var mContext: Context
fun init(context: Context) {
Log.d(TAG, "init: ")
mContext = context
val className: List<String> = getAllActivityUtils("com.he.arouter_api")
for (cls in className) {
Log.d(TAG, "init: className =$cls")
try {
val aClass = Class.forName(cls)
if (IRouter::class.java.isAssignableFrom(aClass)) {
val iRouter = aClass.newInstance() as IRouter
iRouter.putActivity()
}
} catch (e: Exception) {
Log.d(TAG, "init: Exception =" + e.message)
}
}
}
/**
* 将activity压入 RouteProcessor调用
*
* @param activityName
* @param cls
*/
fun putActivity(activityName: String, cls: Class<out Activity?>?) {
Log.d(TAG, "putActivity: $activityName")
if (cls != null && !TextUtils.isEmpty(activityName)) {
activityMap[activityName] = cls
}
}
/**
* 通过之前定义的path就行启动
*
* @param activityName
*/
fun jumpActivity(activityName: String?) {
jumpActivity(activityName, null)
}
fun jumpActivity(activityName: String?, bundle: Bundle?) {
val intent = Intent()
val aCls: Class<out Activity?>? = activityMap[activityName]
Log.d(TAG, "jumpActivity: $aCls")
if (aCls == null) {
return
}
if (bundle != null) {
intent.putExtras(bundle)
}
intent.setClass(mContext, aCls)
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
Log.d(TAG, "jumpActivity: startActivity")
mContext.startActivity(intent)
}
fun getAllActivityUtils(packageName: String?): List<String> {
val list: MutableList<String> = arrayListOf()
val path: String
try {
path = mContext.packageManager.getApplicationInfo(mContext.packageName, 0).sourceDir
val dexFile = DexFile(path)
val enumeration: Enumeration<*> = dexFile.entries()
while (enumeration.hasMoreElements()) {
val name = enumeration.nextElement() as String
if (name.contains(packageName!!)) {
list.add(name)
}
}
} catch (e: java.lang.Exception) {
e.printStackTrace()
}
return list
}
}
IRouter.kt
interface IRouter {
fun putActivity()
}
lib-annotation
annotation模块非常简单,这里只创建了一个Router注解,然后其他业务组件(如MainActivity、OneActivity、TwoActivity)依赖它.
/**
* Target 用于描述注解的使用范围
* Retention 用于描述注解的保留时间
* @AutoService(Processor.class) AutoService使用kotiln注解有一些问题 ,会出现无法生成代码的情况,//https://cloud.tencent.com/developer/article/1587253
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.CLASS)
public @interface Route {
String path() default "";
}
lib-annotation-compiler
这个模块是核心,主要实现就在这里
当前模块下的build.gradle 需要添加几个依赖
dependencies {
implementation project(path: ':apt:lib-annotation')
//https://github.com/google/auto
annotationProcessor 'com.google.auto.service:auto-service:1.0'
compileOnly 'com.google.auto.service:auto-service-annotations:1.0'
//https://github.com/square/javapoet
implementation 'com.squareup:javapoet:1.13.0'
}
RouteProcessor
利用APT技术生成一个工具类,生成的类实现上面的IRouter,并覆写putActivity方法
/**
* @AutoService(Processor.class) AutoService使用kotiln注解有一些问题 ,会出现无法生成代码的情况,//https://cloud.tencent.com/developer/article/1587253
*/
@AutoService(Processor.class)
public class RouteProcessor extends AbstractProcessor {
Filer filer;
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
Set<? extends Element> elementsAnnotatedWith = roundEnv.getElementsAnnotatedWith(Route.class);
Map<String, String> map = new HashMap<>();
for (Element element :
elementsAnnotatedWith) {
TypeElement typeElement = (TypeElement) element;
Route annotation = typeElement.getAnnotation(Route.class);
String path = annotation.path();
Name qualifiedName = typeElement.getQualifiedName();
map.put(path, qualifiedName + ".class");
}
if (map.size() > 0) {
Iterator<String> iterator = map.keySet().iterator();
while (iterator.hasNext()) {
String activityKey = iterator.next();
String cls = map.get(activityKey);
//使用Javapoet
MethodSpec methodSpec = MethodSpec.methodBuilder("putActivity")
.addModifiers(Modifier.PUBLIC)
.returns(void.class)
.addStatement("ARouter.getInstance().putActivity("+"\""+activityKey+"\","+cls+")")
.build();
final ClassName InterfaceName = ClassName.get("com.he.arouter_api","IRouter");
TypeSpec typeSpec = TypeSpec.classBuilder("ActivityUtil"+System.currentTimeMillis())
.addSuperinterface(InterfaceName)
.addModifiers(Modifier.PUBLIC)
.addMethod(methodSpec)
.build();
JavaFile javaFile = JavaFile.builder("com.he.arouter_api", typeSpec).build();
try {
javaFile.writeTo(filer);
} catch (IOException e) {
e.printStackTrace();
}
}
// //原生实现
// Writer writer = null;
// String className = "ActivityUtil" + System.currentTimeMillis();
// try {
// JavaFileObject classFile = filer.createSourceFile("com.he.arouter_api." + className);
// writer = classFile.openWriter();
// writer.write("package com.he.arouter_api;\n" +
// "\n" +
// "import com.he.arouter_api.ARouter;\n" +
// "import com.he.arouter_api.IRouter;\n" +
// "\n" +
// "public class " + className + " implements IRouter {\n" +
// " @Override\n" +
// " public void putActivity() {\n");
//
// Iterator<String> iterator = map.keySet().iterator();
// while (iterator.hasNext()) {
// String activityKey = iterator.next();
// String cls = map.get(activityKey);
// writer.write(" ARouter.getInstance().putActivity(");
// writer.write("\"" + activityKey + "\"," + cls + ");");
// }
// writer.write("\n}\n" +
// "}");
// } catch (Exception e) {
// e.printStackTrace();
// } finally {
// if (writer != null) {
// try {
// writer.close();
// } catch (IOException e) {
// e.printStackTrace();
// }
// }
// }
}
return false;
}
@Override
public Set<String> getSupportedAnnotationTypes() {
HashSet<String> types = new HashSet<>();
types.add(Route.class.getCanonicalName());
return types;
}
@Override
public SourceVersion getSupportedSourceVersion() {
return processingEnv.getSourceVersion();
}
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
filer = processingEnv.getFiler();
}
}
- getSupportedAnnotationTypes()方式表示我们要处理那个注解,这里是Route这个注解
- getSupportedSourceVersion() 声明支持java 版本
- init(ProcessingEnvironment processingEnv) 初始化,得到下面需要用到的Filer
- process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) 主要代码生成逻辑在这里进行
最后使用
在需要使用Router的模块下的build.gradle添加依赖
implementation project(path: ':lib-annotation')
kapt project(path: ':apt:lib-annotation-compiler') // 在kotiln项目中,为 Kotlin 提供 apt 服务,替换annotationProcessor
implementation project(path: ':arouter-api')
在Application下初始化
class App : Application() {
override fun onCreate() {
super.onCreate()
ARouter.instance.init(this)
}
}
在目标Activity上加上@Route(path)
@Route(path = "module_one/OneActivity")
class OneActivity : AppCompatActivity() {
companion object{
private const val TAG = "OneActivity"
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_one)
}
}
ARouter.instance.jumpActivity
@Route(path = "main/MainActivity")
class MainActivity : AppCompatActivity() {
companion object {
private const val TAG = "MainActivity"
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
this.findViewById<Button>(R.id.btn).setOnClickListener {
ARouter.instance.jumpActivity("module_one/OneActivity")
}
}
}
留下评论