路由的意義:
- 模塊間解耦,不能在代碼中寫死Activity類名。
- 動態(tài)配置業(yè)務需求,現(xiàn)在都是業(yè)務模塊化開發(fā)了。
1. 注解
我們這次編寫的MRoute主要使用了編譯時注解技術(shù),注解在我們?nèi)粘J褂玫目蚣苤卸加畜w現(xiàn)。
運行時注解,主要集合反射來完成功能。
編譯時注解,則主要是在編譯階段生成類,來輔助我們后面實現(xiàn)功能。
關(guān)于注解,不做詳細描述。
2. Activity跳轉(zhuǎn)
正常情況下,我們進行Activity跳轉(zhuǎn)是這樣的:
Intent intent=new Intent(this,AnotherActivity.class);
startActivity(intent);
使用路由跳轉(zhuǎn)是這樣的:
MRouter.startActivity(context,url,bundle);
MRouter會根據(jù)傳入的參數(shù)url來查找對應的類,然后完成Activity的跳轉(zhuǎn)。
3. 具體實現(xiàn)
3.1 項目結(jié)構(gòu)
MRoute
|--RouteAnnotation
|
|--RouteProcessor
|
|--RouteApi
3.2 RouteAnnotation
注意該Moudule為Java Library.
RouteAnnotation中定義了MRouter使用的注解Route,具體實現(xiàn)如下:
/**
* Android路由注解。
*
* @author zhangshuai.
*/
@Documented
@Inherited
@Retention(RetentionPolicy.CLASS)
@Target(ElementType.TYPE)
public @interface Route {
String url() default "";
}
要點解釋:
- 因為使用編譯時注解,因此Route的只存在于class中,不是Runtime。
- 注解的只能使用在類上。
3.3 RouteCompiler
該Module同樣為Java Library,用于處理注解并生成輔助類。
該項目中定義了注解處理器RouteProcessor,使用了auto-service、javapoet來輔助完成。
下面來看核心類RouteProcessor實現(xiàn):
import core.zs.routeannotation.Route;
@AutoService(Processor.class)
public class RouteProcessor extends AbstractProcessor {
private Filer mFiler;
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
mFiler = processingEnv.getFiler();
}
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(Route.class);
TypeSpec spec = processElements(elements);
try {
if (spec != null) {
// 生成Java文件
// RouteMap.java
JavaFile.builder("core.zs.route", spec).build().writeTo(mFiler);
}
} catch (IOException e) {
e.printStackTrace();
}
return true;
}
private TypeSpec processElements(Set<? extends Element> elements) {
if (elements == null || elements.size() == 0) {
return null;
}
// 1. 構(gòu)造參數(shù),參數(shù)為activityMap
// 參數(shù)類型為:HashMap<String,Class<?>>
ParameterizedTypeName mapTypeName =
ParameterizedTypeName.get(ClassName.get(HashMap.class), ClassName.get(String
.class), ClassName.get(Class.class));
ParameterSpec mapSpec = ParameterSpec.builder(mapTypeName, "activityMap").build();
// 2. 構(gòu)造方法,方法名為initActivityMap
// 參數(shù)為activityMap
MethodSpec.Builder initMethodBuilder =
MethodSpec.methodBuilder("initActivityMap").addModifiers(Modifier.PUBLIC,
Modifier.STATIC).addParameter(mapSpec);
// 3. 處理使用Route注解的類信息
for (Element element : elements) {
Route route = element.getAnnotation(Route.class);
String url = route.url();
if (null != url && !"".equals(url)) {
// 在initActivityMap中添加語句
// activityMap.put(url,xxx.class);
initMethodBuilder.addStatement("activityMap.put($S,$T.class)", url, ClassName.get
((TypeElement) element));
}
}
// 返回我們構(gòu)造的類元素
return TypeSpec.classBuilder("RouteMap").addMethod(initMethodBuilder.build())
.addModifiers(Modifier.PUBLIC).build();
}
@Override
public Set<String> getSupportedAnnotationTypes() {
Set<String> set = new LinkedHashSet<>();
set.add(Route.class.getCanonicalName());
return set;
}
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latestSupported();
}
}
使用處理器后,會在project的app/build/generated/source/apt/debug/項目名 下生產(chǎn)一個RouteMap.java文件。
其內(nèi)容如下:
package core.zs.router;
import java.lang.Class;
import java.lang.String;
import java.util.HashMap;
public class RouteMap {
public static void initActivityMap(HashMap<String, Class> activityMap) {
activityMap.put("/core/zs/TestActivity",TestActivity.class);
}
}
3.4 RouteApi
注意該Module為Android Library,該Module主要是封裝一些常用的api,供其他項使用。
只有一個類MRouter。
package core.zs.routeapi;
/**
* Created by ZhangShuai on 2018/6/26.
*/
public class MRouter {
public static HashMap<String, Class<?>> activityMap = new HashMap<>();
public static void init(Context context) {
try {
// 通過反射來調(diào)用RouteMap的initActivityMap方法,將數(shù)據(jù)保存到activityMap中
Class clz = Class.forName("core.zs.route.RouteMap");
if (clz != null) {
Method initMapMethod = clz.getMethod("initActivityMap", HashMap.class);
initMapMethod.invoke(null, activityMap);
}
} catch (Exception e) {
e.printStackTrace();
}
}
// 替換常規(guī)的startActivity方法
public static void startActivity(Context context, String url, Bundle bundle) {
if (context == null || url == null) {
return;
}
Class atClazz = activityMap.get(url);
if (atClazz != null) {
System.out.println(atClazz.getCanonicalName());
Intent intent = new Intent(context, atClazz);
if (bundle != null) {
intent.putExtras(bundle);
}
context.startActivity(intent);
}else{
System.out.println("=====Activity is null.");
}
}
}
3.5 如何使用
- 在Application中初始化
public class MyApp extends Application {
@Override
public void onCreate() {
super.onCreate();
MRouter.init(this);
}
}
- 在Activity上使用Route注解。
@Route(url = "/core/zs/route/RouteActivity")
public class RouteActivity extends AppCompatActivity{
//....
}
- 進行跳轉(zhuǎn)
MRouter.startActivity(MainActivity.this, "/core/zs/route/RouteActivity", null);
4. 總結(jié)
隨著項目的不斷變化,模塊化開發(fā)、組件化開發(fā)逐步被引入,使用MRouter能很好的幫你實現(xiàn)業(yè)務之間的解耦,實現(xiàn)業(yè)務之間的簡單配置、順利跳轉(zhuǎn)。
奉上源碼地址MRouter。