Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> android 記一次annotation + AbstractProcessor 編譯自動生成sdcard管理類

android 記一次annotation + AbstractProcessor 編譯自動生成sdcard管理類

編輯:關於Android編程

https://github.com/shf981862482/SuperAnnotation
在app開發中,難免要做一些sdcard的操作
比如:判斷sdcard存在,生成相應目錄, 刪除文件等;
我們可以通過注解來自動生成如下的文件
外層build.gradle

    dependencies {
        classpath 'com.android.tools.build:gradle:2.1.2'
        classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }

app build.radle

apply plugin: 'com.android.application'
//注解處理器
apply plugin: 'com.neenbedankt.android-apt'

dependencies {
    compile fileTree(dir: 'libs', include: ['**.*'])
    testCompile 'junit:junit:4.12'
    compile 'com.android.support:appcompat-v7:23.4.0'
    //注解處理器
    compile 'com.google.auto.service:auto-service:1.0-rc2'
    compile 'com.squareup:javapoet:1.7.0'
    compile 'com.sun_multi:annotaion:0.0.3'
    apt'com.sun_multi:compiler:0.0.2'
}

SDCardUtil

public final class SuperSDCardUtil {
  public static final String mRootFileName = "Super";

  public static final String img = "img";

  public static final String data = "data";

  public static final String temp = "temp";

  public static final String msg = "msg";

  public static final String sound = "sound";

  public static final String soundLocal = "soundLocal";

  public static final String download = "download";

  static {
    createCacheFile();
  }

  public static String getSDCardImgPath() {
    String path = getSDCardPath() + File.separator + img;
    return path;
  }

  public static String getSDCardDataPath() {
    String path = getSDCardPath() + File.separator + data;
    return path;
  }

  public static String getSDCardTempPath() {
    String path = getSDCardPath() + File.separator + temp;
    return path;
  }

  public static String getSDCardMsgPath() {
    String path = getSDCardPath() + File.separator + msg;
    return path;
  }

  public static String getSDCardSoundPath() {
    String path = getSDCardPath() + File.separator + sound;
    return path;
  }

  public static String getSDCardSoundLocalPath() {
    String path = getSDCardPath() + File.separator + soundLocal;
    return path;
  }

  public static String getSDCardDownloadPath() {
    String path = getSDCardPath() + File.separator + download;
    return path;
  }

  public static void createCacheFile() {
    initFile(getSDCardImgPath());
    initFile(getSDCardDataPath());
    initFile(getSDCardTempPath());
    initFile(getSDCardMsgPath());
    initFile(getSDCardSoundPath());
    initFile(getSDCardSoundLocalPath());
    initFile(getSDCardDownloadPath());
  }

  public static boolean isSDCardExistReal() {
    boolean isExits = false;
    isExits = Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED);
    return isExits;
  }

  public static String getSDCardPath() {
    String path = null;
    if(isSDCardExistReal()) {
      path = Environment.getExternalStorageDirectory().toString() + File.separator +mRootFileName;
    }
    else {
      path = Environment.getDataDirectory().toString() + File.separator +mRootFileName;
    }
    return path;
  }

  public static void initFile(String path) {
    File file = new File(path);
    if(file != null && !file.exists()) {
      file.mkdirs();;
    }
  }

  public static void delete(File file) {
    if(file.isFile()) {
      file.delete();;
      return;
    }
    if(file.isDirectory()) {
      File[] childFiles = file.listFiles();
      if (childFiles == null || childFiles.length == 0) {
        file.delete();
        return;
      }
      for (int i = 0; i < childFiles.length; i++) {
        delete(childFiles[i]);
      }
      file.delete();
    }
  }

  public static void clearFile() {
    String path=getSDCardPath();
    delete(new File(path));
    createCacheFile();
  }
}

那麼可見以上的方法足夠用了、但是如果我們切換到了別的項目,如果產品說圖片文件目錄需要是imgsbchanpin,難道要復制一份java文件,改一下常量所對應的目錄名稱嗎?
no,no,no 作為懶人我們要想辦法搞一套自動化的東西

自動成代碼

我們會想起一些android框架、比如dagger2,databinding等
dagger2的原理就是是在編譯時根據annotation來生成class文件

配置好環境

在根目錄下的build.gradle添加apt

buildscript {
    dependencies {
        classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
    }
}

app下的build.gradle添加注解處理器插件 javapoet等

//注解處理器
apply plugin: 'com.neenbedankt.android-apt'
dependencies {

    //注解處理器
    compile 'com.google.auto.service:auto-service:1.0-rc2'
    compile 'com.squareup:javapoet:1.7.0'

}

自定義注解

我新建了一個java的module
這裡寫圖片描述
因為annotation和javapoet暫時支持jdk1.7,如果你用的1.8需要在生成的java module中的
build.gradle添加如下代碼

apply plugin: 'java'
sourceCompatibility = JavaVersion.VERSION_1_7
targetCompatibility = JavaVersion.VERSION_1_7
dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
}

然後創建自己annotation

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Documented
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.CLASS)
public @interface SDCardRootFile {
    //filenames
    String[] fileNames() default {};
}

fileNames是你要生成的目錄名字  img sound 等

深入理解自定義annotation

@Target說明了Annotation所修飾的對象范圍:Annotation可被用於 packages、types(類、接口、枚舉、Annotation類型)、類型成員(方法、構造方法、成員變量、枚舉值)、方法參數和本地變量(如循環變量、catch參數)。在Annotation類型的聲明中使用了target可更加明晰其修飾的目標。

@Retention定義了該Annotation被保留的時間長短:某些Annotation僅出現在源代碼中,而被編譯器丟棄;而另一些卻被編譯在class文件中;編譯在class文件中的Annotation可能會被虛擬機忽略,而另一些在class被裝載時將被讀取(請注意並不影響class的執行,因為Annotation與class在使用上是被分離的)。使用這個meta-Annotation可以對 Annotation的“生命周期”限制。

AbstractProcessor

AbstractProcessor來通過自定義的annotation來生成代碼

public class SDcardProcessor extends AbstractProcessor {  

    @Override  
    public synchronized void init(ProcessingEnvironment env){ }  

    @Override  
    public boolean process(Set annoations, RoundEnvironment env) { }  

    @Override  
    public Set getSupportedAnnotationTypes() { }  

    @Override  
    public SourceVersion getSupportedSourceVersion() { }  

}  

init(ProcessingEnvironment env):
每一個注解處理器類都必須有一個空的構造函數。然而,這裡有一個特殊的init()方法,它會被注解處理工具調用,並輸入ProcessingEnviroment參數。ProcessingEnviroment提供很多有用的工具類Elements,Types和Filer。

public boolean process(Setannoations,
RoundEnvironment env)

這相當於每個處理器的主函數main()。
在這裡寫掃描、評估和處理注解的代碼,以及生成Java文件。輸入參數RoundEnviroment,可以讓查詢出包含特定注解的被注解元素。

getSupportedAnnotationTypes():
這裡必須指定,這個注解處理器是注冊給哪個注解的。注意,它的返回值是一個字符串的集合,包含本處理器想要處理的注解類型的合法全稱。換句話說,在這裡定義你的注解處理器注冊到哪些注解上。
getSupportedSourceVersion():
用來指定你使用的Java版本。通常這裡返回SourceVersion.latestSupported()。然而,如果有足夠的理由只支持Java
6的話,也可以返回SourceVersion.RELEASE_6。推薦使用前者。

再new一個java的module,並將自定義annotation的module引入
build.gradle如下

apply plugin: 'java'
sourceCompatibility = JavaVersion.VERSION_1_7
targetCompatibility = JavaVersion.VERSION_1_7
dependencies {
//    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile 'com.google.auto.service:auto-service:1.0-rc2'
    compile 'com.squareup:javapoet:1.7.0'
    compile project(':annotation')
}

新建幾個類

SDcardProcessor 處理器
SDcardAnnotatedClass 用來存放每個注解的實體類
ProcessorUtil 處理器工具類,獲取包名等
StringUtils 實現駝峰明面
代碼分別如下

package com.just.sdcardProcessor;

import com.google.auto.service.AutoService;
import com.just.annotations.SDCardRootFile;
import com.just.utils.ProcessorUtil;
import com.just.utils.StringUtils;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.CodeBlock;
import com.squareup.javapoet.FieldSpec;
import com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.TypeSpec;

import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;

import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Messager;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.Processor;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.tools.Diagnostic;

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

    private static final String CLASS_NAME = "SDCardUtil";

    private static final String ANNOTATION = "@" + SDCardRootFile.class.getSimpleName();

    //******Field**************************************************************
    private static final String SDCARD_ROOT_FILE_NAME = "mRootFileName";

    //******Method**************************************************************
    private static final String METHOD_INIT_FILE = "initFile";
    private static final String METHOD_GET_SDCARD = "getSDCard%sPath";
    private static final String METHOD_GET_SDCARD_PATH = "getSDCardPath";
    private static final String METHOD_IS_SDCARD_EXIST_REAL = "isSDCardExistReal";
    private static final String METHOD_CREATE_CACHE_FILE = "createCacheFile";
    private static final String METHOD_DELETE = "delete";
    private static final String METHOD_CLEARFILE = "clearFile";

    //******Class**************************************************************
    ClassName mEnvironmentClassName = ClassName.get("android.os", "Environment");
    ClassName mFileClassName = ClassName.get("java.io", "File");

    private Messager messager;

    @Override
    public Set getSupportedAnnotationTypes() {
        Set annotataions = new LinkedHashSet();
        annotataions.add(SDCardRootFile.class.getCanonicalName());
        return annotataions;
    }

    @Override
    public SourceVersion getSupportedSourceVersion() {
//        super.getSupportedSourceVersion();
        return SourceVersion.latestSupported();
    }

    @Override
    public synchronized void init(ProcessingEnvironment processingEnv) {
        super.init(processingEnv);
        messager = processingEnv.getMessager();
    }

    /**
     *
     * @param annotations
     * @param roundEnv  注釋處理工具框架將提供一個注解處理器實現了這個接口的對象,以便處理器可以查詢信息的注釋處理
     * @return
     */
    @Override
    public boolean process(Set annotations, RoundEnvironment roundEnv) {
        ArrayList annotatedClasses = new ArrayList<>();
        //SDCardRootFile
//        roundEnv.getElementsAnnotatedWith(SDCardRootFile.class)  // 獲得被該注解聲明的元素
        for (Element element : roundEnv.getElementsAnnotatedWith(SDCardRootFile.class)) {
            //if public Field
            if (!ProcessorUtil.isFinalValidField(element, messager, ANNOTATION)) {
                return true;
            }
            VariableElement variableElement = (VariableElement) element;
            try {
                //paser
                annotatedClasses.add(buildAnnotVariabldSDcardClass(variableElement));
            } catch (IOException e) {
                String message = String.format("Couldn't processvariablass %s: .%s", variableElement,
                        e.getMessage());
                messager.printMessage(Diagnostic.Kind.ERROR, message, element);
            }
        }
        try {
            generate(annotatedClasses);
        } catch (IOException e) {
            messager.printMessage(Diagnostic.Kind.ERROR, "Couldn't generate class");
        }
        return true;
    }

    /**
     * package com.example;    // PackageElement
     *

* public class Foo { // TypeElement *

* private int a; // VariableElement * private Foo other; // VariableElement *

* public Foo () {} // ExecuteableElement *

* public void setA ( // ExecuteableElement * int newA // TypeElement * ) {} * } * * @param annotatedClass * @return * @throws IOException */ private SDcardAnnotatedClass buildAnnotVariabldSDcardClass(VariableElement annotatedClass) throws IOException { return new SDcardAnnotatedClass(annotatedClass); } private void generate(List classList) throws IOException { if (null == classList || classList.size() == 0) { return; } for (int i = 0; i < classList.size(); i++) { String packageName = ProcessorUtil.getPackageName(processingEnv.getElementUtils(), classList.get(i).getQualifiedSuperClassName()); TypeSpec generateClass = generateClass(classList.get(i)); JavaFile javaFile = JavaFile.builder(packageName, generateClass). build(); javaFile.writeTo(processingEnv.getFiler()); } } public TypeSpec generateClass(SDcardAnnotatedClass classes) { TypeSpec.Builder builder = TypeSpec.classBuilder(classes.getAppRootPathName() + CLASS_NAME) .addModifiers(Modifier.PUBLIC, Modifier.FINAL); builder.addField(makeCreateField(SDCARD_ROOT_FILE_NAME, classes.getAppRootPathName())); List initfileMethodNames = new ArrayList<>(); for (String fileName : classes.getFileNames()) { messager.printMessage(Diagnostic.Kind.NOTE, "fileName=" + fileName); builder.addField(makeCreateField(fileName, fileName)); MethodSpec methodSpec = makeFilePathMethod(fileName); builder.addMethod(methodSpec); initfileMethodNames.add(methodSpec.name); } builder.addStaticBlock(makeStaticb()); builder.addMethod(makeCreateCacheFile(initfileMethodNames)); builder.addMethod(makeIsDdcardExistRealMethod()); builder.addMethod(makeGetSDCardPthMethod()); builder.addMethod(maekInitFileMethod()); builder.addMethod(makeDeleFileMethod()); builder.addMethod(makeClearFileMethod()); return builder.build(); } /** * .initializer("$S + $L", "Lollipop v.", 5.0d) * * @param fieldName * @param value * @return */ private FieldSpec makeCreateField(String fieldName, String value) { return FieldSpec.builder(String.class, fieldName) .addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL) .initializer("$S", value) .build(); } private CodeBlock makeStaticb() { return CodeBlock.builder() .addStatement(METHOD_CREATE_CACHE_FILE + "()") .build(); } private MethodSpec makeClearFileMethod() { return MethodSpec.methodBuilder(METHOD_CLEARFILE) .addModifiers(Modifier.PUBLIC, Modifier.STATIC) .addStatement("String path=" + METHOD_GET_SDCARD_PATH + "()") .addStatement(METHOD_DELETE + "(new $T(path))", mFileClassName) .addStatement(METHOD_CREATE_CACHE_FILE + "()") .build(); } private MethodSpec makeDeleFileMethod() { return MethodSpec.methodBuilder(METHOD_DELETE) .addModifiers(Modifier.PUBLIC, Modifier.STATIC) .addParameter(mFileClassName, "file") .beginControlFlow("if(file.isFile())") .addStatement("file.delete();") .addStatement("return") .endControlFlow() .beginControlFlow("if(file.isDirectory())") .addStatement("File[] childFiles = file.listFiles()") .beginControlFlow("if (childFiles == null || childFiles.length == 0)") .addStatement("file.delete()") .addStatement("return") .endControlFlow() .beginControlFlow("for (int i = 0; i < childFiles.length; i++)") .addStatement(METHOD_DELETE + "(childFiles[i])") .endControlFlow() .addStatement("file.delete()") .endControlFlow() .build(); } private MethodSpec makeCreateCacheFile(List methodList) { MethodSpec.Builder builder = MethodSpec.methodBuilder(METHOD_CREATE_CACHE_FILE) .addModifiers(Modifier.PUBLIC, Modifier.STATIC); for (String methodName : methodList) { builder.addStatement(METHOD_INIT_FILE + "(" + methodName + "())"); } return builder.build(); } private MethodSpec makeFilePathMethod(String fileName) { return MethodSpec.methodBuilder(String.format(METHOD_GET_SDCARD, StringUtils.capitalize(fileName))) .returns(String.class) .addModifiers(Modifier.PUBLIC, Modifier.STATIC) .addStatement("String path = getSDCardPath() + File.separator + " + fileName) .addStatement("return path") .build(); } private MethodSpec makeIsDdcardExistRealMethod() { return MethodSpec.methodBuilder(METHOD_IS_SDCARD_EXIST_REAL) .returns(boolean.class) .addModifiers(Modifier.PUBLIC, Modifier.STATIC) .addStatement("boolean isExits = false") .addStatement("isExits = $T.getExternalStorageState().equals($T.MEDIA_MOUNTED)", mEnvironmentClassName, mEnvironmentClassName) .addStatement("return isExits") .build(); } private MethodSpec makeGetSDCardPthMethod() { return MethodSpec.methodBuilder(METHOD_GET_SDCARD_PATH) .returns(String.class) .addModifiers(Modifier.PUBLIC, Modifier.STATIC) .addStatement("String path = null") .beginControlFlow("if(isSDCardExistReal())") .addStatement("path = $T.getExternalStorageDirectory().toString() + $T.separator +" + SDCARD_ROOT_FILE_NAME, mEnvironmentClassName, mFileClassName) .endControlFlow() .beginControlFlow("else") .addStatement("path = $T.getDataDirectory().toString() + $T.separator +" + SDCARD_ROOT_FILE_NAME, mEnvironmentClassName, mFileClassName) .endControlFlow() .addStatement("return path") .build(); } private MethodSpec maekInitFileMethod() { return MethodSpec.methodBuilder(METHOD_INIT_FILE) .addModifiers(Modifier.PUBLIC, Modifier.STATIC) .addParameter(String.class, "path") .addStatement("$T file = new $T(path)", mFileClassName, mFileClassName) .beginControlFlow("if(file != null && !file.exists())") .addStatement("file.mkdirs();") .endControlFlow() .build(); } }

package com.just.sdcardProcessor;

import com.just.annotations.SDCardRootFile;

import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.MirroredTypeException;

class SDcardAnnotatedClass {
    public final VariableElement typeElement;

    private String qualifiedSuperClassName;//規范命名
    private String annotatedClassName; //簡單命名
    private String simpleTypeName; //簡單命名

    private String[] fileNames;//屬性
    private String appRootPathName; //路徑命名

    /**
     *
     * @param typeElement  代表一個字段,枚舉常數,方法或者構造函數參數,局部變量,資源變量,或異常參數。
     */
    public SDcardAnnotatedClass(VariableElement typeElement) {
        //返回這個變量的值,如果這是最後一個字段初始化為一個編譯時常量。
        this.appRootPathName = (String) typeElement.getConstantValue();
        //返回這個變量元素的簡單的名稱。
        this.annotatedClassName = typeElement.getSimpleName().toString();
        this.typeElement = typeElement;
        //返回這個構造指定類型的注釋如果存在這樣一個注釋,其他null。
        SDCardRootFile annotation = typeElement.getAnnotation(SDCardRootFile.class);
        fileNames = annotation.fileNames();

        // Get the full QualifiedTypeName
        try {
            Class clazz = annotation.annotationType();
            qualifiedSuperClassName = clazz.getCanonicalName();
            simpleTypeName = clazz.getSimpleName();
        } catch (MirroredTypeException mte) {
            DeclaredType classTypeMirror = (DeclaredType) mte.getTypeMirror();
            TypeElement classTypeElement = (TypeElement) classTypeMirror.asElement();
            qualifiedSuperClassName = classTypeElement.getQualifiedName().toString();
            simpleTypeName = classTypeElement.getSimpleName().toString();
        }
    }

    public String getQualifiedSuperClassName() {
        return qualifiedSuperClassName;
    }

    public String getSimpleTypeName() {
        return simpleTypeName;
    }

    public VariableElement getTypeElement() {
        return typeElement;
    }

    public String getAnnotatedClassName() {
        return annotatedClassName;
    }

    public String[] getFileNames() {
        return fileNames;
    }

    public String getAppRootPathName() {
        return appRootPathName;
    }
}
package com.just.utils;
import javax.annotation.processing.Messager;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.PackageElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.util.Elements;
import javax.tools.Diagnostic;

public class ProcessorUtil {

    public static String getPackageName(Elements elementUtils, String qualifiedSuperClassName ) {
        TypeElement superClassname = elementUtils.getTypeElement(qualifiedSuperClassName);
        PackageElement pkg = elementUtils.getPackageOf(superClassname);
        if (pkg.isUnnamed()) {
            return null;
        }
        return pkg.getQualifiedName().toString();
    }

    public static boolean isValidClass(TypeElement element, Messager messager, String annotation) {
        if (!isPublic(element)) {
            String message = String.format("Classes annotated with %s must be public.",
                    annotation);
            messager.printMessage(Diagnostic.Kind.ERROR, message, element);
            return false;
        }

        if (isAbstract(element)) {
            String message = String.format("Classes annotated with %s must not be abstract.",
                    annotation);
            messager.printMessage(Diagnostic.Kind.ERROR, message, element);
            return false;
        }
        return true;
    }

    public static boolean isFinalValidField(Element element, Messager messager, String annotation) {
        if (!isPublic(element)) {
            String message = String.format("Classes annotated with %s must be public.",
                    annotation);
            messager.printMessage(Diagnostic.Kind.ERROR, message, element);
            return false;
        }
        if (!isField(element)) {
            String message = String.format("must be file.",
                    annotation);
            messager.printMessage(Diagnostic.Kind.ERROR, message, element);
            return false;
        }
        if (!isFinal(element)) {
            String message = String.format("must be final.",
                    annotation);
            messager.printMessage(Diagnostic.Kind.ERROR, message, element);
            return false;
        }
        return true;
    }

    public static boolean isField(Element annotatedClass) {
        return annotatedClass.getKind() == ElementKind.FIELD;
    }

    public static boolean isFinal(Element annotatedClass) {
        return annotatedClass.getModifiers().contains(Modifier.FINAL);
    }

    public static boolean isPublic(Element annotatedClass) {
        return annotatedClass.getModifiers().contains(Modifier.PUBLIC);
    }

    public static boolean isAbstract(Element annotatedClass) {
        return annotatedClass.getModifiers().contains(Modifier.ABSTRACT);
    }
}
package com.just.utils;

/**
 * Created by zhai on 16/6/22.
 */
public class StringUtils {
    public StringUtils() {
    }

    public static String decapitalize(String name) {
        if(name != null && name.length() != 0) {
            if(name.length() > 1 && Character.isUpperCase(name.charAt(1)) && Character.isUpperCase(name.charAt(0))) {
                return name;
            } else {
                char[] chars = name.toCharArray();
                chars[0] = Character.toLowerCase(chars[0]);
                return new String(chars);
            }
        } else {
            return name;
        }
    }

    public static String capitalize(String name) {
        if(name != null && name.length() != 0) {
            char[] chars = name.toCharArray();
            chars[0] = Character.toUpperCase(chars[0]);
            return new String(chars);
        } else {
            return name;
        }
    }
}

app下的build.gradle以來這兩個module

dependencies {
    apt project(':processor')
    compile project(':annotation')
}

如果有jdk版本問題需要加上如下代碼

compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_7
        targetCompatibility JavaVersion.VERSION_1_7
}

新建一個class類,用來注解

/**
 * Created by walkingMen on 2016/8/1.
 */
public class Constant implements Serializable {
    //=================SD文件路徑===========================================
    public static final String DISK_IMAGE_PHOTO_PATH = "img";

    public static final String DISK_DATA = "data";

    public static final String DISK_TAKE_PHOTO_PATH = "temp";

    public static final String DISK_MSG_CACHE_PATH = "msg";

    public static final String DISK_SOUND_PATH = "sound";

    public static final String DISK_MSG_RECORD_VOICE_PATH = "soundLocal";

    public static final String DISK_DOWNLOAD_PATH = "download";


    //緩存路徑
    @SDCardRootFile(fileNames = {DISK_IMAGE_PHOTO_PATH, DISK_DATA, DISK_TAKE_PHOTO_PATH,
            DISK_MSG_CACHE_PATH, DISK_SOUND_PATH, DISK_MSG_RECORD_VOICE_PATH, DISK_DOWNLOAD_PATH})
    public static final String CACHE_ROOT_DIR_NAME = "Super";
}

好了,大功告成,clean project rebuild project
這裡寫圖片描述

略微等待一下,會在app/build/generated/source/apt下生成相應的***SDCardUtil
這裡寫圖片描述

jcenter gradle地址

ok、開發完成,我已經將annotation和processor提交到了jcenter,地址如下

compile 'com.sun_multi:annotaion:0.0.3'
apt'com.sun_multi:compiler:0.0.2'

demo github地址

https://github.com/shf981862482/SuperAnnotation
歡迎star和fork

插曲

開發的時候,突然出現編譯沒自動生成Sdcardutils的情況,經過兩天的死磕終於解決了

下面是錯誤日志

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':app:compileTrunkDebugJavaWithJavac'.
> Compilation failed; see the compiler error output for details.

* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output.

後來在我新建的項目中專門測試這個bug 報了如下錯誤

錯誤: 服務配置文件不正確, 或構造處理程序對象

javax.annotation.processing.Processor: Provider com.mm.processor.sdcardProcessor.SDcardProcessor could not be instantiated: java.lang.VerifyError: Expecting a stackmap frame at branch target 24
  Exception Details:
    Location:
      com/squareup/javapoet/TypeName.(Ljava/util/List;)V @12: ifeq
    Reason:
      Expected stackmap frame at this location.
    Bytecode:
      0x0000000: 2a01 2bb7 0003 b201 bbb6 01be 9900 0cb2
      0x0000010: 01c3 1301 c5b6 01ca b201 bbb6 01be 9900
      0x0000020: 0cb2 01c3 1301 c5b6 01ca b1            
  時拋出異常錯誤
Error:Execution failed for task ':app:compileDebugJavaWithJavac'.
> Compilation failed; see the compiler error output for details.
gradlew compileDebug --stacktrace

我推測錯誤是因為jdk版本導致的
通過命令

gradlew compileDebug --stacktrace

獲取編譯的詳情日志如下

* What went wrong:
Unable to start the daemon process.
This problem might be caused by incorrect configuration of the daemon.
For example, an unrecognized jvm option is used.
Please refer to the user guide chapter on the daemon at https://docs.gradle.org/2.10/userguide/gradle_daemon.html
Please read the following process output to find out more:
-----------------------
FATAL ERROR in native method: JDWP No transports initialized, jvmtiError=AGENT_ERROR_TRANSPORT_INIT(197)
ERROR: transport error 202: bind failed: Address already in use
ERROR: JDWP Transport dt_socket failed to initialize, TRANSPORT_INIT(510)
JDWP exit error AGENT_ERROR_TRANSPORT_INIT(197): No transports initialized [debugInit.c:750]

* Exception is:
org.gradle.api.GradleException: Unable to start the daemon process.
This problem might be caused by incorrect configuration of the daemon.
For example, an unrecognized jvm option is used.
Please refer to the user guide chapter on the daemon at https://docs.gradle.org/2.10/userguide/gradle_daemon.html
Please read the following process output to find out more:
-----------------------

然後我去stackoverflow搜Unable to start the daemon process.
http://stackoverflow.com/questions/20471311/android-studio-unable-to-start-the-daemon-process

通過刪除了user下的.gradle 重新編譯,終於成功了
猜測可能是gradle的緩存在作怪

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved