アリルートフレーム--ARouterソース解析のCompler
77162 ワード
転載は出典を明記してください.http://blog.csdn.net/crazy1235/article/details/77126904 注釈プロセッサの処理フロー Route Processor init process パーロット categories route Verify InterceptorProcessor init process parse Interceptors verify AutowiredProcessor init process categories generate Helper 総括 は を参照してください.
この間、会社のプロジェクトは部品化の再構成をしていますが、途中でたくさんの痛みがあります.
モジュール化の一番重要なのはプロジェクトと業務によってモジュールを分けます.モジュールの粒度はみんなでコントロールします.
ここで言えば、モジュール間のデータ転送問題です.
モジュール化後、モジュール同士が相互に依存しない場合、どうやってデータを転送しますか?
答えは陰的なIntent方式でデータをジャンプして伝えることです.
以前はIntentジャンプを表示していましたが、クラスに直接依存する問題があり、結合性が非常に深刻になります.対照的に、暗黙的なIntentはクラス間の直接的な依存性を必要としないが、規則的な集中的管理ができ、拡張性が悪い.
そこでアリさんがアレウタールートの枠組みを作っているのを発見しました.
ARouterのいいところはここでは多く言いません.みんなは公式文書を見に行ったり、githubに行ってREADMEを見てもいいです.
【https://github.com/alibaba/ARouterを選択します
次はいくつかのブログに分けてARouterのソースコードを分析します.
ARouterのソースコードを見ると、二つのSDKを提供しています.一つはAPIで、一つはComplerです. Copiler SDKは、コンパイラが関連するクラスのファイルを生成するためのものである. API SDKは、実行中の経路移動などの役割を果たす.
ここでまずCompler層SDKについて話します. Route Processorルーティングパスプロセッサ InterceptorProcessorプロセッサ AutowireProcessor自動組立プロセッサ 注釈プロセッサの処理フロー
(画像はネットワークから変わります)
実際には、Compler SDKは、スキャンされた注釈に基づいて、対応するマッピング(java)ファイルを生成するだけである.
最後のステップは、パケット名を固定してマッピングファイルをロードすることによって、API SDKによって行われる.
公式デモを例にとって:
上の図に示すように、ARouterがコンパイル中に生成したクラスファイルです.赤い色が表示されているのは、Route Processorが生成したクラスファイル です.ブルーの表示は、InterceptorProcessorが生成したクラスファイル です.オレンジ色のスカラーは、AutowiredProcessorが生成したクラスファイル です.
arouter-complerのディレクトリ構造は以下の通りです.
processorパッケージの下には注釈プロセッサ があります. utilsパッケージの下には、関連するツールクラス があります.
以下はそれぞれこの3つの注釈プロセッサについて説明します.
コンパイルを使ったことがあるという友達は知っていますが、注釈プロセッサはAbstractProcessorを引き継ぐ必要があります.主に関連する関数はinit()とprocess()の二つがあります.
Route Processor
クラスの継承情報:
init()
process
一般的にプロcess()関数で行われる操作は以下の通りです.が注釈された要素を巡回する .は、要素が要求(フィルタ要素)に適合しているかどうかを検査する .出力クラスパラメータ を取得する.マッピングファイル(javaファイル) を生成する.エラー処理
この関数のコードはちょっと長いです.みんなでじっくり見てください.
これからどうやって経路を分けますか? Route注釈が設定されている場合、経路は空であることが許されず、かつ「/」で始まる必要がある . RouteProcessor注釈プロセッサによって生成されるファイルは、3つの種類があります. は同じ優先度のスクリーンセーバーを設定できません.そうでないと、異常 が投げられます. InterceptorProcessorが生成するクラスファイルフォーマットは、 です.
これで、ARouterのCompler SDKの3つの注釈プロセッサは分析済みです.
次の文章はAPI SDKのソースコードを分析し始めました.
楽しみにしていてください
参照
https://yq.aliyun.com/articles/71687
この間、会社のプロジェクトは部品化の再構成をしていますが、途中でたくさんの痛みがあります.
モジュール化の一番重要なのはプロジェクトと業務によってモジュールを分けます.モジュールの粒度はみんなでコントロールします.
ここで言えば、モジュール間のデータ転送問題です.
モジュール化後、モジュール同士が相互に依存しない場合、どうやってデータを転送しますか?
答えは陰的なIntent方式でデータをジャンプして伝えることです.
以前はIntentジャンプを表示していましたが、クラスに直接依存する問題があり、結合性が非常に深刻になります.対照的に、暗黙的なIntentはクラス間の直接的な依存性を必要としないが、規則的な集中的管理ができ、拡張性が悪い.
そこでアリさんがアレウタールートの枠組みを作っているのを発見しました.
ARouterのいいところはここでは多く言いません.みんなは公式文書を見に行ったり、githubに行ってREADMEを見てもいいです.
【https://github.com/alibaba/ARouterを選択します
次はいくつかのブログに分けてARouterのソースコードを分析します.
ARouterのソースコードを見ると、二つのSDKを提供しています.一つはAPIで、一つはComplerです.
ここでまずCompler層SDKについて話します.
(画像はネットワークから変わります)
実際には、Compler SDKは、スキャンされた注釈に基づいて、対応するマッピング(java)ファイルを生成するだけである.
最後のステップは、パケット名を固定してマッピングファイルをロードすることによって、API SDKによって行われる.
公式デモを例にとって:
上の図に示すように、ARouterがコンパイル中に生成したクラスファイルです.
arouter-complerのディレクトリ構造は以下の通りです.
以下はそれぞれこの3つの注釈プロセッサについて説明します.
コンパイルを使ったことがあるという友達は知っていますが、注釈プロセッサはAbstractProcessorを引き継ぐ必要があります.主に関連する関数はinit()とprocess()の二つがあります.
Route Processor
クラスの継承情報:
@AutoService(Processor.class)
@SupportedOptions(KEY_MODULE_NAME)
@SupportedSourceVersion(SourceVersion.RELEASE_7)
@SupportedAnnotationTypes({ANNOTATION_TYPE_ROUTE, ANNOTATION_TYPE_AUTOWIRED})
public class RouteProcessor extends AbstractProcessor {
initinit()
//
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
//
mFiler = processingEnv.getFiler(); // Generate class.
//
types = processingEnv.getTypeUtils(); // Get type utils.
//
elements = processingEnv.getElementUtils(); // Get class meta.
typeUtils = new TypeUtils(types, elements);
//
logger = new Logger(processingEnv.getMessager()); // Package the log utils.
// [moduleName]
Map options = processingEnv.getOptions();
if (MapUtils.isNotEmpty(options)) {
moduleName = options.get(KEY_MODULE_NAME);
}
if (StringUtils.isNotEmpty(moduleName)) {
//
moduleName = moduleName.replaceAll("[^0-9a-zA-Z_]+", "");
logger.info("The user has configuration the module name, it was [" + moduleName + "]");
} else {
// build.gradle moduleName, 。
logger.error("These no module name, at 'build.gradle', like :
" +
"apt {
" +
" arguments {
" +
" moduleName project.getName();
" +
" }
" +
"}
");
throw new RuntimeException("ARouter::Compiler >>> No module name, for more information, look at gradle log.");
}
//
iProvider = elements.getTypeElement(Consts.IPROVIDER).asType();
// RouterProcessor
logger.info(">>> RouteProcessor init. <<);
}
// Consts.java
public static final String KEY_MODULE_NAME = "moduleName";
ARouterの注釈を使う時、公式文書によって各moduleの中のbuild.gradleに下記の情報を配置する必要があります.javaCompileOptions {
annotationProcessorOptions {
arguments = [ moduleName : project.getName() ]
}
}
このプロパティを設定する目的は、コンパイル中に関連するmoduleの下のファイルとファイル名を生成するためです.process
一般的にプロcess()関数で行われる操作は以下の通りです.
@Override
public boolean process(Set extends TypeElement> annotations, RoundEnvironment roundEnv) {
if (CollectionUtils.isNotEmpty(annotations)) {
// Route
Set extends Element> routeElements = roundEnv.getElementsAnnotatedWith(Route.class);
try {
logger.info(">>> Found routes, start... <<);
// arseRoute()
this.parseRoutes(routeElements);
} catch (Exception e) {
logger.error(e);
}
// Route , true
return true;
}
// false
return false;
}
パースRoutes()この関数のコードはちょっと長いです.みんなでじっくり見てください.
// Consts.java
public static final String ACTIVITY = "android.app.Activity";
public static final String FRAGMENT = "android.app.Fragment";
public static final String FRAGMENT_V4 = "android.support.v4.app.Fragment";
public static final String SERVICE = "android.app.Service";
private static final String FACADE_PACKAGE = "com.alibaba.android.arouter.facade";
private static final String TEMPLATE_PACKAGE = ".template";
public static final String IROUTE_GROUP = FACADE_PACKAGE + TEMPLATE_PACKAGE + ".IRouteGroup";
public static final String IPROVIDER_GROUP = FACADE_PACKAGE + TEMPLATE_PACKAGE + ".IProviderGroup";
private void parseRoutes(Set extends Element> routeElements) throws IOException {
if (CollectionUtils.isNotEmpty(routeElements)) {
// ...
rootMap.clear();
// ACTIVITY, SERVICE, FRAGMENT, FRAGMENT_V4
TypeMirror type_Activity = elements.getTypeElement(ACTIVITY).asType();
TypeMirror type_Service = elements.getTypeElement(SERVICE).asType();
TypeMirror fragmentTm = elements.getTypeElement(FRAGMENT).asType();
TypeMirror fragmentTmV4 = elements.getTypeElement(Consts.FRAGMENT_V4).asType();
// ARouter
TypeElement type_IRouteGroup = elements.getTypeElement(IROUTE_GROUP);
TypeElement type_IProviderGroup = elements.getTypeElement(IPROVIDER_GROUP);
//
// , javapoet
ClassName routeMetaCn = ClassName.get(RouteMeta.class);
ClassName routeTypeCn = ClassName.get(RouteType.class);
/*
ParameterizedTypeName ,
```Map>```
*/
ParameterizedTypeName inputMapTypeOfRoot = ParameterizedTypeName.get(
ClassName.get(Map.class),
ClassName.get(String.class),
ParameterizedTypeName.get(
ClassName.get(Class.class),
WildcardTypeName.subtypeOf(ClassName.get(type_IRouteGroup))
)
);
/*
RouteMeta
```Map```
*/
ParameterizedTypeName inputMapTypeOfGroup = ParameterizedTypeName.get(
ClassName.get(Map.class),
ClassName.get(String.class),
ClassName.get(RouteMeta.class)
);
/*
*/
// 1。 :Map> routes
ParameterSpec rootParamSpec = ParameterSpec.builder(inputMapTypeOfRoot, "routes").build(); // ,
// 2。 Map atlas
ParameterSpec groupParamSpec = ParameterSpec.builder(inputMapTypeOfGroup, "atlas").build();
// 3。 Map providers
ParameterSpec providerParamSpec = ParameterSpec.builder(inputMapTypeOfGroup, "providers").build();
// MethodSpec
// public static final String METHOD_LOAD_INTO = "loadInto";
/*
Build method : 'loadInto'
*/
MethodSpec.Builder loadIntoMethodOfRootBuilder = MethodSpec.methodBuilder(METHOD_LOAD_INTO)
.addAnnotation(Override.class) // override
.addModifiers(PUBLIC) // public
.addParameter(rootParamSpec); //
//
/**
* @Override
* public void loadInto(Map> routes) { }
*/
//
// , , java
for (Element element : routeElements) { //
TypeMirror tm = element.asType();
Route route = element.getAnnotation(Route.class);
RouteMeta routeMete = null;
//
if (types.isSubtype(tm, type_Activity)) { // Activity
logger.info(">>> Found activity route: " + tm.toString() + " <<);
Map paramsType = new HashMap<>();
// @AutoWired
for (Element field : element.getEnclosedElements()) {
// 1. field
// 2. AutoWired
// 3. IProvider
if (field.getKind().isField() && field.getAnnotation(Autowired.class) != null && !types.isSubtype(field.asType(), iProvider)) {
// ,
Autowired paramConfig = field.getAnnotation(Autowired.class);
// ,Autowired , name , name , field 。
// TypeUtils , field , int 。
paramsType.put(StringUtils.isEmpty(paramConfig.name()) ? field.getSimpleName().toString() : paramConfig.name(), typeUtils.typeExchange(field));
}
// ,
routeMete = new RouteMeta(route, element, RouteType.ACTIVITY, paramsType);
}
// IProvider , PROVIDER
else if (types.isSubtype(tm, iProvider)) {
routeMete = new RouteMeta(route, element, RouteType.PROVIDER, null);
}
// Service , Service
else if (types.isSubtype(tm, type_Service)) { // Service
routeMete = new RouteMeta(route, element, RouteType.parse(Service), null);
}
// fragmentTmV4 , Fragment
else if (types.isSubtype(tm, fragmentTm) || types.isSubtype(tm, fragmentTmV4)) {
routeMete = new RouteMeta(route, element, RouteType.parse(FRAGMENT), null);
}
// ( , categories() "" )
categories(routeMete);
}
// , ROOT loadInto
// Provider loadInto
MethodSpec.Builder loadIntoMethodOfProviderBuilder = MethodSpec.methodBuilder(METHOD_LOAD_INTO)
.addAnnotation(Override.class)
.addModifiers(PUBLIC)
.addParameter(providerParamSpec);
//
/**
* @Override
* public void loadInto(Map providers) { }
*/
// , categories(routeMete);
for (Map.EntrySet > entry : groupMap.entrySet()) {
String groupName = entry.getKey();
// -- loadInto(Map atlas)
MethodSpec.Builder loadIntoMethodOfGroupBuilder = MethodSpec.methodBuilder(METHOD_LOAD_INTO)
.addAnnotation(Override.class)
.addModifiers(PUBLIC)
.addParameter(groupParamSpec);
// loadInto
Set groupData = entry.getValue();
// PROVIDERL
for (RouteMeta routeMeta : groupData) {
switch (routeMeta.getType()) {
case PROVIDER:
List extends TypeMirror> interfaces = ((TypeElement) routeMeta.getRawType()).getInterfaces();
//
for (TypeMirror tm : interfaces) {
// IProvider
if (types.isSameType(tm, iProvider)) {
// , loadInfo() :
// singleService IProvider
/**
* @Route(path = "/service/single")
* public class SingleService implements IProvider
*
* providers.put("com.alibaba.android.arouter.demo.testservice.SingleService", RouteMeta.build(RouteType.PROVIDER, SingleService.class, "/service/single", "service", null, -1, -2147483648));
*/
loadIntoMethodOfProviderBuilder.addStatement(
"providers.put($S, $T.build($T." + routeMeta.getType() + ", $T.class, $S, $S, null, " + routeMeta.getPriority() + ", " + routeMeta.getExtra() + "))",
(routeMeta.getRawType()).toString(),
routeMetaCn,
routeTypeCn,
ClassName.get((TypeElement) routeMeta.getRawType()),
routeMeta.getPath(),
routeMeta.getGroup());
} else if (types.isSubtype(tm, iProvider)) {
// IProvider
// , loadInfo() :
// singleService IProvider
/**
* @Route(path = "/service/hello")
* public class HelloServiceImpl implements HelloService
* public interface HelloService extends IProvider
* //
* providers.put("com.alibaba.android.arouter.demo.testservice.HelloService", RouteMeta.build(RouteType.PROVIDER, HelloServiceImpl.class, "/service/hello", "service", null, -1, -2147483648));
*/
loadIntoMethodOfProviderBuilder.addStatement(
"providers.put($S, $T.build($T." + routeMeta.getType() + ", $T.class, $S, $S, null, " + routeMeta.getPriority() + ", " + routeMeta.getExtra() + "))",
tm.toString(), // So stupid, will duplicate only save class name.
routeMetaCn,
routeTypeCn,
ClassName.get((TypeElement) routeMeta.getRawType()),
routeMeta.getPath(),
routeMeta.getGroup());
}
}
break;
default:
break;
}
//
StringBuilder mapBodyBuilder = new StringBuilder();
Map paramsType = routeMeta.getParamsType();
if (MapUtils.isNotEmpty(paramsType)) {
for (Map.Entry types : paramsType.entrySet()) {
mapBodyBuilder.append("put(\"").append(types.getKey()).append("\", ").append(types.getValue()).append("); ");
}
}
// // : put("pac", 9); put("obj", 10);
String mapBody = mapBodyBuilder.toString();
// loadInto
loadIntoMethodOfGroupBuilder.addStatement(
"atlas.put($S, $T.build($T." + routeMeta.getType() + ", $T.class, $S, $S, " + (StringUtils.isEmpty(mapBody) ? null : ("new java.util.HashMap(){{" + mapBodyBuilder.toString() + "}}")) + ", " + routeMeta.getPriority() + ", " + routeMeta.getExtra() + "))",
routeMeta.getPath(), //
routeMetaCn, // RouteMeta
routeTypeCn, // RouteType
ClassName.get((TypeElement) routeMeta.getRawType()), //
routeMeta.getPath().toLowerCase(), //
routeMeta.getGroup().toLowerCase()); //
}
// :
// atlas.put("/test/activity1", RouteMeta.build(RouteType.ACTIVITY, Test1Activity.class, "/test/activity1", "test", new java.util.HashMap(){{put("pac", 9); put("obj", 10); put("name", 8); put("boy", 0); put("age", 3); put("url", 8); }}, -1, -2147483648));
// java
// public static final String NAME_OF_GROUP = PROJECT + SEPARATOR + "Group" + SEPARATOR;
// public static final String SEPARATOR = "$$";
// public static final String PROJECT = "ARouter";
String groupFileName = NAME_OF_GROUP + groupName;
JavaFile.builder(PACKAGE_OF_GENERATE_FILE, // package --"com.alibaba.android.arouter.routes"
TypeSpec.classBuilder(groupFileName) //java
.addJavadoc(WARNING_TIPS) // doc
.addSuperinterface(ClassName.get(type_IRouteGroup)) //
.addModifiers(PUBLIC) // public
.addMethod(loadIntoMethodOfGroupBuilder.build()) // ( )
.build()
).build().writeTo(mFiler);
// map ,
rootMap.put(groupName, groupFileName);
}
// .................................................................... //
// for , ARouter$$Group$$service.java ARouter$$Group$$test.java , com.alibaba.android.arouter.routes。
if (MapUtils.isNotEmpty(rootMap)) {
// group, Root
for (Map.Entry entry : rootMap.entrySet()) {
loadIntoMethodOfRootBuilder.addStatement("routes.put($S, $T.class)", entry.getKey(), ClassName.get(PACKAGE_OF_GENERATE_FILE, entry.getValue()));
// statement : routes.put("test", ARouter$$Group$$test.class);
}
}
// provider
// provider :ARouter$$Providers$$xxx
String providerMapFileName = NAME_OF_PROVIDER + SEPARATOR + moduleName;
JavaFile.builder(PACKAGE_OF_GENERATE_FILE,
TypeSpec.classBuilder(providerMapFileName)
.addJavadoc(WARNING_TIPS)
.addSuperinterface(ClassName.get(type_IProviderGroup))
.addModifiers(PUBLIC)
.addMethod(loadIntoMethodOfProviderBuilder.build())
.build()
).build().writeTo(mFiler);
// root
// ARouter$$Root$$xxx
String rootFileName = NAME_OF_ROOT + SEPARATOR + moduleName;
JavaFile.builder(PACKAGE_OF_GENERATE_FILE,
TypeSpec.classBuilder(rootFileName)
.addJavadoc(WARNING_TIPS)
.addSuperinterface(ClassName.get(elements.getTypeElement(ITROUTE_ROOT)))
.addModifiers(PUBLIC)
.addMethod(loadIntoMethodOfRootBuilder.build())
.build()
).build().writeTo(mFiler);
}
}
categories()これからどうやって経路を分けますか?
private void categories(RouteMeta routeMete) {
//
if (routeVerify(routeMete)) {
// groupMap group
Set routeMetas = groupMap.get(routeMete.getGroup());
if (CollectionUtils.isEmpty(routeMetas)) { // map , map
Set routeMetaSet = new TreeSet<>(new Comparator() {
@Override
public int compare(RouteMeta r1, RouteMeta r2) {
try {
return r1.getPath().compareTo(r2.getPath());
} catch (NullPointerException npe) {
logger.error(npe.getMessage());
return 0;
}
}
});
// map
routeMetaSet.add(routeMete);
groupMap.put(routeMete.getGroup(), routeMetaSet);
} else { //
routeMetas.add(routeMete);
}
} else {
//
logger.warning(">>> Route meta verify error, group is " + routeMete.getGroup() + " <<);
}
}
route Verify()//
private boolean routeVerify(RouteMeta meta) {
String path = meta.getPath();
// “/”
if (StringUtils.isEmpty(path) || !path.startsWith("/")) { // The path must be start with '/' and not empty!
return false;
}
// ,group ""
if (StringUtils.isEmpty(meta.getGroup())) { // Use default group(the first word in path)
try {
// group
String defaultGroup = path.substring(1, path.indexOf("/", 1));
if (StringUtils.isEmpty(defaultGroup)) {
return false;
}
meta.setGroup(defaultGroup);
return true;
} catch (Exception e) {
logger.error("Failed to extract default group! " + e.getMessage());
return false;
}
}
return true;
}
上の分析によって以下の点が得られます.1. ARouter$$Group$$xxx ( )
2. ARouter$$Providers$$xxx ( )
3. ARouter$$Root$$xxx ( )
InterceptorProcessor@AutoService(Processor.class)
@SupportedOptions(KEY_MODULE_NAME)
@SupportedSourceVersion(SourceVersion.RELEASE_7)
@SupportedAnnotationTypes(ANNOTATION_TYPE_INTECEPTOR)
public class InterceptorProcessor extends AbstractProcessor
init() @Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
// ... RouteProcressor
iInterceptor = elementUtil.getTypeElement(Consts.IINTERCEPTOR).asType();
}
process @Override
public boolean process(Set extends TypeElement> annotations, RoundEnvironment roundEnv) {
if (CollectionUtils.isNotEmpty(annotations)) {
// Interceptor
Set extends Element> elements = roundEnv.getElementsAnnotatedWith(Interceptor.class);
try {
//
parseInterceptors(elements);
} catch (Exception e) {
logger.error(e);
}
return true;
}
return false;
}
parse Interceptors()private Map<Integer, Element> interceptors = new TreeMap<>();
private void parseInterceptors(Set extends Element> elements) throws IOException {
if (CollectionUtils.isNotEmpty(elements)) {
//
for (Element element : elements) {
if (verify(element)) { //
Interceptor interceptor = element.getAnnotation(Interceptor.class);
//
Element lastInterceptor = interceptors.get(interceptor.priority());
// ,
if (null != lastInterceptor) {
throw new IllegalArgumentException(
String.format(Locale.getDefault(), "More than one interceptors use same priority [%d], They are [%s] and [%s].",
interceptor.priority(),
lastInterceptor.getSimpleName(),
element.getSimpleName())
);
}
//
interceptors.put(interceptor.priority(), element);
} else {
logger.error("A interceptor verify failed, its " + element.asType());
}
}
// Interface of ARouter.
TypeElement type_ITollgate = elementUtil.getTypeElement(IINTERCEPTOR);
TypeElement type_ITollgateGroup = elementUtil.getTypeElement(IINTERCEPTOR_GROUP);
/**
*
*
* ```Map>```
*/
ParameterizedTypeName inputMapTypeOfTollgate = ParameterizedTypeName.get(
ClassName.get(Map.class),
ClassName.get(Integer.class),
ParameterizedTypeName.get(
ClassName.get(Class.class),
WildcardTypeName.subtypeOf(ClassName.get(type_ITollgate))
)
);
//
ParameterSpec tollgateParamSpec = ParameterSpec.builder(inputMapTypeOfTollgate, "interceptors").build();
// : 'loadInto'
MethodSpec.Builder loadIntoMethodOfTollgateBuilder = MethodSpec.methodBuilder(METHOD_LOAD_INTO)
.addAnnotation(Override.class)
.addModifiers(PUBLIC)
.addParameter(tollgateParamSpec);
// , loadInto
if (null != interceptors && interceptors.size() > 0) {
// Build method body
for (Map.Entry entry : interceptors.entrySet()) {
loadIntoMethodOfTollgateBuilder.addStatement("interceptors.put(" + entry.getKey() + ", $T.class)", ClassName.get((TypeElement) entry.getValue()));
//
// interceptors.put(1, Test1Interceptor.class);
}
}
//
JavaFile.builder(PACKAGE_OF_GENERATE_FILE,
TypeSpec.classBuilder(NAME_OF_INTERCEPTOR + SEPARATOR + moduleName)
.addModifiers(PUBLIC)
.addJavadoc(WARNING_TIPS)
.addMethod(loadIntoMethodOfTollgateBuilder.build())
.addSuperinterface(ClassName.get(type_ITollgateGroup))
.build()
).build().writeTo(mFiler);
logger.info(">>> Interceptor group write over. <<);
}
}
verify()//
private boolean verify(Element element) {
Interceptor interceptor = element.getAnnotation(Interceptor.class);
return null != interceptor && ((TypeElement)element).getInterfaces().contains(iInterceptor);
}
上の分析によって以下の点が得られます.ARouter$$Interceptors$$xxx
AutowiredProcessor@AutoService(Processor.class)
@SupportedOptions(KEY_MODULE_NAME)
@SupportedSourceVersion(SourceVersion.RELEASE_7)
@SupportedAnnotationTypes({ANNOTATION_TYPE_AUTOWIRED})
public class AutowiredProcessor extends AbstractProcessor
init() @Override
public synchronized void init(ProcessingEnvironment processingEnvironment) {
super.init(processingEnvironment);
mFiler = processingEnv.getFiler(); // Generate class.
types = processingEnv.getTypeUtils(); // Get type utils.
elements = processingEnv.getElementUtils(); // Get class meta.
typeUtils = new TypeUtils(types, elements);
logger = new Logger(processingEnv.getMessager()); // Package the log utils.
}
process // process categories() generateHelper()
@Override
public boolean process(Set extends TypeElement> set, RoundEnvironment roundEnvironment) {
if (CollectionUtils.isNotEmpty(set)) {
try {
logger.info(">>> Found autowired field, start... <<);
// 1.
categories(roundEnvironment.getElementsAnnotatedWith(Autowired.class));
// 2.
generateHelper();
} catch (Exception e) {
logger.error(e);
}
return true;
}
return false;
}
categoriesprivate Map<TypeElement, List<Element>> parentAndChild = new HashMap<>();
//
private void categories(Set extends Element> elements) throws IllegalAccessException {
if (CollectionUtils.isNotEmpty(elements)) {
for (Element element : elements) { //
//
TypeElement enclosingElement = (TypeElement) element.getEnclosingElement();
// private,
if (element.getModifiers().contains(Modifier.PRIVATE)) {
throw new IllegalAccessException("The autowired fields CAN NOT BE 'private'!!! please check field ["
+ element.getSimpleName() + "] in class [" + enclosingElement.getQualifiedName() + "]");
}
//
if (parentAndChild.containsKey(enclosingElement)) { // Has categries
parentAndChild.get(enclosingElement).add(element);
} else {
List childs = new ArrayList<>();
childs.add(element);
parentAndChild.put(enclosingElement, childs);
}
}
logger.info("categories finished.");
}
}
generate Helper //
private void generateHelper() throws IOException, IllegalAccessException {
// ISyringe
TypeElement type_ISyringe = elements.getTypeElement(ISYRINGE);
// SerializationService
TypeElement type_JsonService = elements.getTypeElement(JSON_SERVICE);
TypeMirror iProvider = elements.getTypeElement(Consts.IPROVIDER).asType();
TypeMirror activityTm = elements.getTypeElement(Consts.ACTIVITY).asType();
TypeMirror fragmentTm = elements.getTypeElement(Consts.FRAGMENT).asType();
TypeMirror fragmentTmV4 = elements.getTypeElement(Consts.FRAGMENT_V4).asType();
//
ParameterSpec objectParamSpec = ParameterSpec.builder(TypeName.OBJECT, "target").build();
//
if (MapUtils.isNotEmpty(parentAndChild)) {
for (Map.Entry<TypeElement, List<Element>> entry : parentAndChild.entrySet()) {
// : '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("."));
// :Test1Activity$$ARouter$$Autowired
String fileName = parent.getSimpleName() + NAME_OF_AUTOWIRED;
//
TypeSpec.Builder helper = TypeSpec.classBuilder(fileName)
.addJavadoc(WARNING_TIPS)
.addSuperinterface(ClassName.get(type_ISyringe))
.addModifiers(PUBLIC);
// SerializationService
FieldSpec jsonServiceField = FieldSpec.builder(TypeName.get(type_JsonService.asType()), "serializationService", Modifier.PRIVATE).build();
//
helper.addField(jsonServiceField);
// inject
// serializationService = ARouter.getInstance().navigation(SerializationService.class);
injectMethodBuilder.addStatement("serializationService = $T.getInstance().navigation($T.class);", ARouterClass, ClassName.get(type_JsonService));
//
// :Test1Activity substitute = (Test1Activity)target;
injectMethodBuilder.addStatement("$T substitute = ($T)target", ClassName.get(parent), ClassName.get(parent));
//
for (Element element : childs) {
Autowired fieldConfig = element.getAnnotation(Autowired.class);
String fieldName = element.getSimpleName().toString();
// IProvider
if (types.isSubtype(element.asType(), iProvider)) {
// name , Type
if ("".equals(fieldConfig.name())) {
injectMethodBuilder.addStatement(
"substitute." + fieldName + " = $T.getInstance().navigation($T.class)",
ARouterClass,
ClassName.get(element.asType())
);
} else { // name , name
injectMethodBuilder.addStatement(
"substitute." + fieldName + " = ($T)$T.getInstance().build($S).navigation();",
ClassName.get(element.asType()),
ARouterClass,
fieldConfig.name()
);
}
// , if
if (fieldConfig.required()) {
injectMethodBuilder.beginControlFlow("if (substitute." + fieldName + " == null)");
injectMethodBuilder.addStatement(
"throw new RuntimeException(\"The field '" + fieldName + "' is null, in class '\" + $T.class.getName() + \"!\")", ClassName.get(parent));
injectMethodBuilder.endControlFlow();
}
} else { // It's normal intent value
String statment = "substitute." + fieldName + " = substitute.";
boolean isActivity = false;
// Activity , getIntent()
if (types.isSubtype(parent.asType(), activityTm)) {
isActivity = true;
statment += "getIntent().";
}
// Fragment , getArguments()
else if (types.isSubtype(parent.asType(), fragmentTm) || types.isSubtype(parent.asType(), fragmentTmV4)) {
statment += "getArguments().";
}
// Activity Fragment,
else {
throw new IllegalAccessException("The field [" + fieldName + "] need autowired from intent, its parent must be activity or fragment!");
}
statment = buildStatement(statment, typeUtils.typeExchange(element), isActivity);
// SerializationService
if (statment.startsWith("serializationService.")) { // Not mortals
injectMethodBuilder.beginControlFlow("if (null != serializationService)");
injectMethodBuilder.addStatement(
"substitute." + fieldName + " = " + statment,
(StringUtils.isEmpty(fieldConfig.name()) ? fieldName : fieldConfig.name()),
ClassName.get(element.asType())
);
injectMethodBuilder.nextControlFlow("else");
injectMethodBuilder.addStatement(
"$T.e(\"" + Consts.TAG + "\", \"You want automatic inject the field '" + fieldName + "' in class '$T' , then you should implement 'SerializationService' to support object auto inject!\")", AndroidLog, ClassName.get(parent));
injectMethodBuilder.endControlFlow();
} else {
injectMethodBuilder.addStatement(statment, StringUtils.isEmpty(fieldConfig.name()) ? 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(\"" + Consts.TAG + "\", \"The field '" + fieldName + "' is null, in class '\" + $T.class.getName() + \"!\")", AndroidLog, ClassName.get(parent));
injectMethodBuilder.endControlFlow();
}
}
}
// inject()
helper.addMethod(injectMethodBuilder.build());
//
JavaFile.builder(packageName, helper.build()).build().writeTo(mFiler);
}
logger.info(">>> Autowired processor stop. <<);
}
}
AutowiredProcessorが生成したjavaファイルの例は以下の通りである.public class Test1Activity$$ARouter$$Autowired implements ISyringe {
private SerializationService serializationService;
@Override
public void inject(Object target) {
serializationService = ARouter.getInstance().navigation(SerializationService.class);;
Test1Activity substitute = (Test1Activity)target;
substitute.name = substitute.getIntent().getStringExtra("name");
substitute.age = substitute.getIntent().getIntExtra("age", 0);
substitute.girl = substitute.getIntent().getBooleanExtra("boy", false);
substitute.pac = substitute.getIntent().getParcelableExtra("pac");
if (null != serializationService) {
substitute.obj = serializationService.json2Object(substitute.getIntent().getStringExtra("obj"), TestObj.class);
} else {
Log.e("ARouter::", "You want automatic inject the field 'obj' in class 'Test1Activity' , then you should implement 'SerializationService' to support object auto inject!");
}
substitute.url = substitute.getIntent().getStringExtra("url");
substitute.helloService = ARouter.getInstance().navigation(HelloService.class);
}
}
締め括りをつけるこれで、ARouterのCompler SDKの3つの注釈プロセッサは分析済みです.
次の文章はAPI SDKのソースコードを分析し始めました.
楽しみにしていてください
参照
https://yq.aliyun.com/articles/71687