小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

Android進(jìn)階

 quasiceo 2018-09-06

前言

APT的學(xué)習(xí)要花點(diǎn)時(shí)間去掌握和實(shí)踐的,短時(shí)間內(nèi)只能掌握知識(shí)點(diǎn),更多的是在實(shí)戰(zhàn)中去實(shí)踐。其實(shí),APT就是一種工具而已,只要用多了,自然就會(huì)熟練了,不過要想實(shí)踐之前,還是必須把基礎(chǔ)知識(shí)學(xué)好才能實(shí)戰(zhàn)進(jìn)入開發(fā)。文章會(huì)從基礎(chǔ)用例講解知識(shí)點(diǎn),然后再通過實(shí)戰(zhàn)進(jìn)行實(shí)踐

APT簡(jiǎn)介

APT(Annotation Processing Tool)是一種處理注解的工具,它會(huì)對(duì)源代碼中的注解進(jìn)行額外的處理,比如在編譯時(shí)生成一些重復(fù)性操作的Java代碼,或者不需要程序員去關(guān)心的Java代碼等。在使用APT的過程中會(huì)涉及到下面兩個(gè)第三方庫的使用

  1. AutoService:這個(gè)庫的主要作用是注冊(cè)注解,并對(duì)其生成META-INF的配置信息
  2. JavaPoet:這個(gè)庫的主要作用是幫助我們通過類調(diào)用的形式來生成Java代碼

APT主要過程包括初始化過程和注解處理過程

  1. 初始化過程:獲取APT提供的工具類,為后面的注解處理提供幫助
  2. 注解處理過程:獲取注解的元素,對(duì)元素進(jìn)行額外處理,可用JavaPoet生成Java代碼

APT流程

1、定義注解

該注解是可以在我們的項(xiàng)目中使用到的,且規(guī)定為注解元素的類型為Type,和在編譯時(shí)生效

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ModuleWrapper {

}
  • 1
  • 2
  • 3
  • 4
  • 5

2、定義Processor

Processor會(huì)在編譯期對(duì)注解進(jìn)行解析,取出對(duì)應(yīng)的元素進(jìn)行處理。至于AutoService則是固定的寫法,加個(gè)注解即可

@AutoService(Processor.class)
public class ModuleProcessor extends AbstractProcessor {

    private Filer filerUtils; // 文件寫入
    private Elements elementUtils; // 操作Element工具類
    private Messager messagerUtils; // Log 日志
    private Map<String, String> options; // 額外配置參數(shù)

    @Override
    public synchronized void init(ProcessingEnvironment processingEnvironment) {
        super.init(processingEnvironment);

        filerUtils = processingEnvironment.getFiler();
        elementUtils = processingEnvironment.getElementUtils();
        messagerUtils = processingEnvironment.getMessager();
        options = processingEnvironment.getOptions();
    }

    @Override
    public Set<String> getSupportedAnnotationTypes() {
        Set<String> types = new LinkedHashSet<>();
        types.add(ModuleWrapper.class.getCanonicalName());
        return types;
    }

    @Override
    public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
        initModuleMap(roundEnvironment);
        return false;
    }

    private void initModuleMap(RoundEnvironment roundEnv) {
        //獲取對(duì)應(yīng)的注解元素
        Set<? extends Element> set = roundEnv.getElementsAnnotatedWith(ModuleWrapper.class);
        for (Element element : set) {
            //如果是個(gè)類
            if (element.getKind() == ElementKind.CLASS) {
                //獲取類名
                String clzName = element.getSimpleName().toString();
                //對(duì)元素進(jìn)行處理,可用javapoet生成Java代碼
                ......
            } else {
                messagerUtils.printMessage(Diagnostic.Kind.NOTE, "only support class");
            }
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47

3、AbstractProcessor實(shí)現(xiàn)方法介紹

  1. init():初始化過程,可通過初始化方法獲取各種工具類
  2. process():注解處理過程,可通過獲取注解元素后,對(duì)注解元素進(jìn)行額外處理
  3. getSupportedAnnotationTypes():獲取需要解析的注解類型

APT知識(shí)點(diǎn)

1、初始化介紹

APT初始化階段為init方法的調(diào)用,我們可以使用ProcessingEnvironment獲取一些實(shí)用類以及獲取選項(xiàng)參數(shù)等

方法 說明
getElementUtils() 返回實(shí)現(xiàn)Elements接口的對(duì)象,用于操作元素的工具類
getFiler() 返回實(shí)現(xiàn)Filer接口的對(duì)象,用于創(chuàng)建文件、類和輔助文件
getMessager() 返回實(shí)現(xiàn)Messager接口的對(duì)象,用于報(bào)告錯(cuò)誤信息、警告提醒
getOptions() 返回指定的參數(shù)選項(xiàng)
getTypeUtils() 返回實(shí)現(xiàn)Types接口的對(duì)象,用于操作類型的工具類

2、Element介紹

Element是操作元素最主要的類,可通過getElementsAnnotatedWith獲取Element的元素,經(jīng)過getKind()判斷元素的類型后,可強(qiáng)制轉(zhuǎn)換成對(duì)應(yīng)的類型,在對(duì)應(yīng)的類型中有著不同的方法可以調(diào)用

類型 說明
ExecutableElement 表示類、接口的方法元素。包括構(gòu)造方法、注解類型
PackageElement 表示包元素。提供對(duì)有關(guān)包及其成員的信息的訪問
TypeElement 表示類、接口元素。提供對(duì)有關(guān)類型及其成員的信息的訪問
TypeParameterElement 表示類、接口、方法、構(gòu)造方法的參數(shù)元素
VariableElement 表示字段、enum、方法、構(gòu)造方法參數(shù)、局部變量、異常參數(shù)

ElementKind為元素的類型,元素的類型判斷不需要用instanceof去判斷,而應(yīng)該通過getKind()去判斷對(duì)應(yīng)的類型

類型 說明
PACKAGE
ENUM 枚舉
CLASS
ANNOTATION_TYPE 注解
INTERFACE 接口
ENUM_CONSTANT 枚舉常量
FIELD 字段
PARAMETER 方法參數(shù)
LOCAL_VARIABLE 局部變量
METHOD 方法
CONSTRUCTOR 構(gòu)造方法
TYPE_PARAMETER 類型參數(shù)

3、TypeMirror介紹

TypeMirror是一個(gè)接口,表示Java編程語言中的類型。這些類型包括基本類型、引用類型、數(shù)組類型、類型變量和null類型等等

類型 說明
ArrayType 表示數(shù)組類型
DeclaredType 表示聲明類型(類或接口類型)
ErrorType 表示異常類或接口類型
ExecutableType 表示executable類型(方法、構(gòu)造方法、初始化)
NoType 表示在實(shí)際類型不適合的地方使用的偽類型
NullType 表示null類型
PrimitiveType 表示基本數(shù)據(jù)類型
ReferenceType 表示引用類型
TypeVariable 表示類型變量
WildcardType 表示通配符類型參數(shù)

TypeKind為類型的屬性,類型的屬性判斷不需要用instanceof去判斷,而應(yīng)該通過getKind()去判斷對(duì)應(yīng)的屬性

類型 說明
BOOLEAN 基本類型boolean
INT 基本類型int
LONG 基本類型long
FLOAT 基本類型float
DOUBLE 基本類型double
VOID 對(duì)應(yīng)于關(guān)鍵字void的偽類型
NULL null類型
ARRAY 數(shù)組類型
PACKAGE 對(duì)應(yīng)于包元素的偽類型
EXECUTABLE 方法、構(gòu)造方法、初始化

這里需要注意的是,如果我們通過注解去獲取Class類型的值,如果獲取的Class未被編譯,則會(huì)拋出MirroredTypeException異常,此時(shí)我們需要通過try-catch語句在catch里去獲取我們所需要的類元素

try {  
    annotation.value();//如果value為Class類型則會(huì)報(bào)異常
} catch (MirroredTypeException mte) {
    DeclaredType classTypeMirror = (DeclaredType) mte.getTypeMirror();
    TypeElement classTypeElement = (TypeElement) classTypeMirror.asElement();//通過異常去獲取類元素
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

4、Filer介紹

Filer接口支持通過注解處理器創(chuàng)建新文件??梢詣?chuàng)建三種文件類型:源文件、類文件和輔助資源文件

方法 說明
createSourceFile 創(chuàng)建源文件
createClassFile 創(chuàng)建類文件
createResource 創(chuàng)建輔助資源文件

5、Messager介紹

Messager接口提供注解處理器用來報(bào)告錯(cuò)誤消息、警告和其他通知的方式

方法 說明
printMessage 打印錯(cuò)誤消息

6、Options介紹

通過getOptions()方法獲取選項(xiàng)參數(shù),在gradle文件中配置選項(xiàng)參數(shù)值

android {
    defaultConfig {
        javaCompileOptions {
            annotationProcessorOptions {
                arguments = [ version : '1.0.0' ]
            }
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

通過ProcessingEnvironment去獲取對(duì)應(yīng)的參數(shù)

processingEnvironment.getOptions().get("version");
  • 1

7、獲取注解元素

通過RoundEnvironment接口去獲取注解元素,通過JavaPoet生成Java代碼

方法 說明
getElementsAnnotatedWith 返回注解元素的集合

APT實(shí)戰(zhàn)

下面通過APT的實(shí)戰(zhàn),進(jìn)行對(duì)項(xiàng)目的模塊化劃分

1、項(xiàng)目結(jié)構(gòu)

  1. 創(chuàng)建Module,名為annotation,放置我們的注解類
  2. 創(chuàng)建Module,名為compiler,放置我們的注解處理類
  3. 主工程則直接依賴annotation和compiler

注意事項(xiàng):創(chuàng)建Module的時(shí)候,需要選擇java Lib,而不是Android Lib

2、Gradle配置

annotation的Module必須聲明Java編譯版本

apply plugin: 'java-library'

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

sourceCompatibility = "1.7"
targetCompatibility = "1.7"
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

compiler的Module必須聲明Java編譯版本,且依賴于annotation和導(dǎo)入我們所需的庫

apply plugin: 'java-library'

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

    implementation project(':annotation')
    implementation 'com.google.auto.service:auto-service:1.0-rc2'
    implementation 'com.squareup:javapoet:1.7.0'
}

sourceCompatibility = "1.7"
targetCompatibility = "1.7"
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

主工程必須通過依賴annotaion和compiler,由于我們只是在編譯期生效,可用annotationProcessor

implementation project(':annotation')
annotationProcessor project(':compiler')
  • 1
  • 2

注意事項(xiàng):定義編譯的jdk版本為1.7

3、定義注解

ModuleWrapper注解,表示需要加載的模塊

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ModuleWrapper {

}
  • 1
  • 2
  • 3
  • 4
  • 5

IModule接口,表示當(dāng)前類是一個(gè)模塊類

public interface IModule {
    String getModuleName();
}
  • 1
  • 2
  • 3

4、定義Processor

@AutoService(Processor.class)
public class ModuleProcessor extends AbstractProcessor {

    private Map<String, ModuleInfo> moduleMaps = new HashMap<>();

    private Filer filerUtils; // 文件寫入
    private Elements elementUtils; // 操作Element 的工具類
    private Messager messagerUtils; // Log 日志
    private Map<String, String> options;

    @Override
    public synchronized void init(ProcessingEnvironment processingEnvironment) {
        super.init(processingEnvironment);

        filerUtils = processingEnvironment.getFiler();
        elementUtils = processingEnvironment.getElementUtils();
        messagerUtils = processingEnvironment.getMessager();
        options = processingEnvironment.getOptions();
    }

    @Override
    public Set<String> getSupportedAnnotationTypes() {
        Set<String> types = new LinkedHashSet<>();
        types.add(ModuleWrapper.class.getCanonicalName());
        return types;
    }

    @Override
    public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
        try {
            initModuleMap(roundEnvironment);
            createModuleMap();
            createModuleConstant();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return false;
    }

    /**
     * 通過注解元素獲取組件實(shí)體
     *
     * @param roundEnv
     */
    private void initModuleMap(RoundEnvironment roundEnv) {
        Set<? extends Element> set = roundEnv.getElementsAnnotatedWith(ModuleWrapper.class);
        for (Element element : set) {
            if (element.getKind() == ElementKind.CLASS) {
                String clzName = element.getSimpleName().toString();
                if (moduleMaps.get(clzName) == null) {
                    ModuleInfo info = new ModuleInfo(elementUtils, (TypeElement) element);
                    moduleMaps.put(clzName, info);
                }
            } else {
                messagerUtils.printMessage(Diagnostic.Kind.NOTE, "only support class");
            }
        }
    }

    /**
     * 創(chuàng)建組件管理者
     *
     * @throws IOException
     */
    private void createModuleMap() throws IOException {

        FieldSpec fieldSpec = FieldSpec
                .builder(ParameterizedTypeName.get(HashMap.class, String.class, IModule.class)
                        , "moduleMap", Modifier.PRIVATE)
                .initializer("new HashMap<>()")
                .build();

        CodeBlock.Builder codeBlock = CodeBlock.builder();
        for (String key : moduleMaps.keySet()) {
            ModuleInfo info = moduleMaps.get(key);
            codeBlock.addStatement("moduleMap.put($S ,new $T())", info.getFullClassName(),
                    ClassName.get(info.packageName, info.className));
        }

        MethodSpec initMethod = MethodSpec.methodBuilder("init")
                .addModifiers(Modifier.PUBLIC)
                .addCode(codeBlock.build())
                .returns(TypeName.VOID)
                .build();

        MethodSpec getMethod = MethodSpec.methodBuilder("get")
                .addModifiers(Modifier.PUBLIC)
                .addParameter(String.class, "cls")
                .addStatement("return moduleMap.get(cls)")
                .returns(IModule.class)
                .build();

        ArrayList<MethodSpec> methods = new ArrayList<>();
        methods.add(initMethod);
        methods.add(getMethod);

        TypeSpec moduleFactory = TypeSpec.classBuilder("ModuleFactory")
                .addModifiers(Modifier.PUBLIC)
                .addMethods(methods)
                .addField(fieldSpec)
                .build();

        JavaFile javaFile = JavaFile.builder("com.hensen.compiler.processor", moduleFactory)
                .build();
        javaFile.writeTo(filerUtils);
    }

    /**
     * 創(chuàng)建組件常量
     *
     * @throws IOException
     */
    private void createModuleConstant() throws IOException {

        TypeSpec.Builder moduleConstant = TypeSpec.classBuilder("ModuleConstant")
                .addModifiers(Modifier.PUBLIC);

        for (String key : moduleMaps.keySet()) {
            ModuleInfo info = moduleMaps.get(key);
            FieldSpec fieldSpec = FieldSpec.builder(String.class, info.className)
                    .addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL)
                    .initializer("$S", info.getFullClassName())
                    .build();
            moduleConstant.addField(fieldSpec);
        }

        JavaFile javaFile = JavaFile.builder("com.hensen.compiler.processor", moduleConstant.build())
                .build();
        javaFile.writeTo(filerUtils);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131

5、組件實(shí)體

組件實(shí)體保存著組件的信息

public class ModuleInfo {

    public String packageName;
    public String className;

    public ModuleInfo(Elements elementUtils, TypeElement typeElement) {
        packageName = getPackageName(elementUtils, typeElement);
        className = getClassName(typeElement, packageName);
    }

    public String getClassName(TypeElement type, String packageName) {
        int packageLen = packageName.length() + 1;
        return type.getQualifiedName().toString().substring(packageLen)
                .replace('.', '$');
    }

    public String getPackageName(Elements elementUtils, TypeElement classElement) {
        PackageElement packageElement = elementUtils.getPackageOf(classElement);
        return packageElement.getQualifiedName().toString();
    }

    public String getFullClassName() {
        return packageName + "." + className;
    }

    public String getClassName(){
        return className;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

6、注解使用

分別創(chuàng)建出禮物模塊和聊天模塊,聊天模塊增加發(fā)消息的方法

@ModuleWrapper
public class ChatModule implements IModule{

    @Override
    public String getModuleName() {
        return "ChatModule";
    }

    public void sendMessage() {
        Log.i("TAG", "Hi");
    }
}

@ModuleWrapper
public class GiftModule implements IModule{

    @Override
    public String getModuleName() {
        return "GiftModule";
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

7、生成代碼

在生成代碼之前需要gradle build,查看生成代碼。當(dāng)然對(duì)于模塊來說,不僅有g(shù)et()、還有add()、remove()等其他擴(kuò)展功能,具體的就留給大家去操作

public class ModuleFactory {
  private HashMap<String, IModule> moduleMap = new HashMap<>();

  public void init() {
    moduleMap.put("com.hensen.geneapt.GiftModule" ,new GiftModule());
    moduleMap.put("com.hensen.geneapt.ChatModule" ,new ChatModule());
  }

  public IModule get(String cls) {
    return moduleMap.get(cls);
  }
}

public class ModuleConstant {
  public static final String GiftModule = "com.hensen.geneapt.GiftModule";

  public static final String ChatModule = "com.hensen.geneapt.ChatModule";
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

8、組件使用

初始化組件的加載,通過工廠獲取對(duì)應(yīng)的模塊進(jìn)行操作

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        ModuleFactory moduleFactory = new ModuleFactory();
        moduleFactory.init();

        ChatModule chatModule = (ChatModule) moduleFactory.get(ModuleConstant.ChatModule);
        chatModule.sendMessage();

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多