アリルートフレーム--ARouterソース解析のCompler


転載は出典を明記してください.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は、実行中の経路移動などの役割を果たす.
  • 阿里路由框架--ARouter 源码解析之Compiler_第1张图片
    ここでまずCompler層SDKについて話します.
  • Route Processorルーティングパスプロセッサ
  • InterceptorProcessorプロセッサ
  • AutowireProcessor自動組立プロセッサ
  • 注釈プロセッサの処理フロー
    (画像はネットワークから変わります)
    実際には、Compler SDKは、スキャンされた注釈に基づいて、対応するマッピング(java)ファイルを生成するだけである.
    最後のステップは、パケット名を固定してマッピングファイルをロードすることによって、API SDKによって行われる.
    公式デモを例にとって:
    阿里路由框架--ARouter 源码解析之Compiler_第2张图片
    上の図に示すように、ARouterがコンパイル中に生成したクラスファイルです.
  • 赤い色が表示されているのは、Route Processorが生成したクラスファイル
  • です.
  • ブルーの表示は、InterceptorProcessorが生成したクラスファイル
  • です.
  • オレンジ色のスカラーは、AutowiredProcessorが生成したクラスファイル
  • です.
    arouter-complerのディレクトリ構造は以下の通りです.
    阿里路由框架--ARouter 源码解析之Compiler_第3张图片
  • processorパッケージの下には注釈プロセッサ
  • があります.
  • utilsパッケージの下には、関連するツールクラス
  • があります.
    以下はそれぞれこの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 {
    init
    init()
        //       
        @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()関数で行われる操作は以下の通りです.
  • が注釈された要素を巡回する
  • .
  • は、要素が要求(フィルタ要素)に適合しているかどうかを検査する
  • .
  • 出力クラスパラメータ
  • を取得する.
  • マッピングファイル(javaファイル)
  • を生成する.
  • エラー処理
  •     @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.javacom.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;
        }
    上の分析によって以下の点が得られます.
  • Route注釈が設定されている場合、経路は空であることが許されず、かつ「/」で始まる必要がある
  • .
  • RouteProcessor注釈プロセッサによって生成されるファイルは、3つの種類があります.
  • 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);
        }
    上の分析によって以下の点が得られます.
  • は同じ優先度のスクリーンセーバーを設定できません.そうでないと、異常
  • が投げられます.
  • InterceptorProcessorが生成するクラスファイルフォーマットは、
  • です.
    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;
        }
    categories
    private 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