美文网首页
打造一个简易的编译时注解框架(二)

打造一个简易的编译时注解框架(二)

作者: 梦止惰 | 来源:发表于2017-04-26 16:20 被阅读259次

上一节我们已经搭建好了环境,和一个简易的demo,我们在此基础上再写一个简单的注解demo
先看一看项目结构

Paste_Image.png

1,建一个注解类取名叫InjectView

@Retention(RetentionPolicy.CLASS)
public @interface InjectView {
    int value();
} 

2,在process Java moduel创建注解处理器ViewInjectorProcessor

在这里Google给我提供了一个依赖包,专门为了减少建立META-INF.services的步骤
在Java moduel的build中添加依赖:

compile 'com.google.auto.service:auto-service:1.0-rc2'

这个类就这样写:

@AutoService(Processor.class)
public class ViewInjectorProcessor extends AbstractProcessor {
  @Override
    public SourceVersion getSupportedSourceVersion() {
        return SourceVersion.latestSupported();
    }

    @Override
    public Set<String> getSupportedAnnotationTypes() {
        return (Collections.singleton(InjectView.class.getCanonicalName()));
    }
   @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
      return  false;
}
}

3,建立注解处理接口类AnnotationHandler


public interface AnnotationHandler {
    /**
     * @param processingEnv
     */
    void attachProcessingEnv(ProcessingEnvironment processingEnv);

    /**
     * @param roundEnv
     * @return
     */
     Map<String, List<VariableElement>> handleAnnotation(RoundEnvironment roundEnv);
   // Map<String, InjectorInfo> handleAnnotation(RoundEnvironment roundEnv);
}

ViewInjectHandler实现里面的方法

public class ViewInjectHandler implements com.example.annote.handler.AnnotationHandler {
    ProcessingEnvironment mProcessingEnv;
    private Map<String, InjectorInfo> mInfoMap = new HashMap<>();
    @Override
    public void attachProcessingEnv(ProcessingEnvironment processingEnv) {
        mProcessingEnv = processingEnv;
    }


    @Override
    public Map<String, List<VariableElement>> handleAnnotation(RoundEnvironment roundEnv) {
  //  public   Map<String, InjectorInfo>  handleAnnotation(RoundEnvironment roundEnv) {
        Map<String, List<VariableElement>> annotationMap = new HashMap<String, List<VariableElement>>();
        // 获取使用ViewInjector注解的所有元素
        Set<? extends Element> elementSet = roundEnv.getElementsAnnotatedWith(InjectView.class);
        for (Element element : elementSet) {
            // 注解的字段
            VariableElement varElement = (VariableElement) element;
            // 类型的完整路径名,比如某个Activity的完整路径
            String className = AnnotationUtil.getParentClassName(mProcessingEnv,varElement);
            // 获取这个类型的所有注解,例如某个Activity中的所有View的注解对象
            List<VariableElement> cacheElements = annotationMap.get(className);
            if (cacheElements == null) {
                cacheElements = new LinkedList<VariableElement>();
            }
            // 将元素添加到该类型对应的字段列表中
            cacheElements.add(varElement);
            // 以类的路径为key,字段列表为value,存入map.
            // 这里是将所在字段按所属的类型进行分类
            annotationMap.put(className, cacheElements);
        }

        return annotationMap;
    }
}

4,建立接口AdapterWriter

public interface AdapterWriter {
    void generate(Map<String, List<VariableElement>> typeMap);
  //  void generate(Map<String, InjectorInfo> mInfoMap);
}

AbsWriter实现AdapterWriter

public abstract class AbsWriter implements AdapterWriter {
    ProcessingEnvironment mProcessingEnv;
    Filer mFiler;


    public AbsWriter(ProcessingEnvironment processingEnv) {
        mProcessingEnv = processingEnv;
        mFiler = processingEnv.getFiler();
    }

    @Override
      public void generate(Map<String, List<VariableElement>> typeMap) {
 //   public void generate(Map<String, InjectorInfo> infoMap) {
        Iterator<Map.Entry<String, List<VariableElement>>> iterator = typeMap.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry<String, List<VariableElement>> entry = iterator.next();
            List<VariableElement> cacheElements = entry.getValue();
            if (cacheElements == null || cacheElements.size() == 0) {
                continue;
            }

            // 取第一个元素来构造注入信息
            InjectorInfo info = createInjectorInfo(cacheElements.get(0));
                Writer writer = null;
                JavaFileObject javaFileObject;
                try {
                    mProcessingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, info.getClassFullPath());
                    javaFileObject = this.mFiler.createSourceFile(info.getClassFullPath());

                    writer = javaFileObject.openWriter();
                    // 写入package, import, class以及findViews函数等代码段
                    generateImport(writer, info);
                    // 写入该类中的所有字段到findViews方法中
                    for (VariableElement variableElement : entry.getValue()) {
                        writeField(writer, variableElement, info);
                    }
                    // 写入findViews函数的大括号以及类的大括号
                    writeEnd(writer);
                } catch (IOException e) {
                    e.printStackTrace();
                } finally {
                    try {
                        if (writer != null) {
                            writer.flush();
                        }
                        IOUtil.closeQuitly(writer);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }

                }
            }
    //    writeInfo(infoMap);
    }

    /**
     * @param element
     * @return
     */
    protected InjectorInfo createInjectorInfo(VariableElement element) {
        TypeElement typeElement = (TypeElement) element.getEnclosingElement();
        String packageName = AnnotationUtil.getPackageName(mProcessingEnv, typeElement);
        String className = typeElement.getSimpleName().toString();
        return new InjectorInfo(packageName, className);
    }

    protected abstract void generateImport(Writer writer, InjectorInfo info)
            throws IOException;

    protected abstract void writeField(Writer writer, VariableElement element, InjectorInfo info)
            throws IOException;

    protected abstract void writeEnd(Writer writer) throws IOException;
}

DefaultJavaFileWriter继承AbsWriter

public class DefaultJavaFileWriter extends AbsWriter {

    public DefaultJavaFileWriter(ProcessingEnvironment processingEnv) {
        super(processingEnv);
    }

    @Override
    protected void generateImport(Writer writer, InjectorInfo info) throws IOException {
        writer.write("package " + info.packageName + " ;");
        writer.write("\n\n");
        writer.write("import com.example.butterknife.InjectAdapter ;");
        writer.write("\n");
        writer.write("import com.example.butterknife.ViewFinder;");

        writer.write("\n\n\n");
        writer.write("/* This class is generated by Simple ViewInjector, please don't modify! */ ");
        writer.write("\n");
        writer.write("public class " + info.newClassName
                + " implements InjectAdapter<" + info.classlName + "> { ");
        writer.write("\n");
        writer.write("\n");
        // 查找方法
        writer.write("  public void injects(" + info.classlName
                + " target)  { ");
        writer.write("\n");


    }

      @Override
      protected void writeField(Writer writer, VariableElement element, InjectorInfo info) throws IOException {
          InjectView injector = element.getAnnotation(InjectView.class);
        //  writer.write(" ViewFinder.setContentView(target,"  + injector.value() + "  ) ; \n");
          String fieldName = element.getSimpleName().toString();
          writer.write("      target." + fieldName + " =  ViewFinder.findViewById(target, "
                  + injector.value() + "  ) ; ");
          writer.write("\n");
      }

    @Override
    protected void writeEnd(Writer writer) throws IOException {
        writer.write("  }");
        writer.write("\n\n");
        writer.write(" } ");
    }
    @Override
    public  String generateJavaCode(InjectorInfo info) {
        StringBuilder builder = new StringBuilder();
        builder.append("// Generated code from HyViewInjector. Do not modify!\n");
        builder.append("package ").append(info.packageName).append(";\n\n");

        builder.append("import android.view.View;\n");
        builder.append("import com.example.butterknife.ViewFinder;\n");
        builder.append("import com.example.butterknife.InjectAdapter ;\n");
        builder.append('\n');

        builder.append("public class ").append(info.newClassName);
        builder.append(" implements InjectAdapter<T>");
        builder.append(" {\n");
        generateInjectMethod(builder,info);
        builder.append("\n");
        builder.append("}\n");
        return builder.toString();
    }
}

5,ViewInjectorProcessor完整的代码如下:

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

    /**
     * 所有注解处理器的列表
     */
    List<AnnotationHandler> mHandlers = new LinkedList<AnnotationHandler>();
    /**
     * 类型与字段的关联表,用于在写入Java文件时按类型来写不同的文件和字段
     */
      final Map<String, List<VariableElement>> map = new HashMap<String, List<VariableElement>>();
   // final Map<String, InjectorInfo> mInfoMap = new HashMap<>();
    /**
     *
     */
    AdapterWriter mWriter;

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

    @Override
    public Set<String> getSupportedAnnotationTypes() {
        return (Collections.singleton(InjectView.class.getCanonicalName()));
    }

    @Override
    public synchronized void init(ProcessingEnvironment processingEnv) {
        super.init(processingEnv);
        registerHandlers();
        this.mWriter = new DefaultJavaFileWriter(processingEnv);
    }

    private void registerHandlers() {
        this.mHandlers.add(new ViewInjectHandler());
    }

    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        Iterator var4 = this.mHandlers.iterator();
        while (var4.hasNext()) {
            AnnotationHandler handler = (AnnotationHandler) var4.next();
            // 关联ProcessingEnvironment
            handler.attachProcessingEnv(this.processingEnv);
            // 解析注解相关的信息
            map.putAll(handler.handleAnnotation(roundEnv));
        }
        // 将解析到的数据写入到具体的类型中
        this.mWriter.generate(map);
        return true;
    }
}

6,建立Android moduel 名字butterknife,如图

Paste_Image.png
创建注册activity或者fragment的视图接口注册器

public interface InjectAdapter<T> {
void injects(T target);
}

创建转化控件的静态方法类
public class ViewFinder {


    public static <T extends View> T findViewById(Activity act, int id) {
        return (T) act.findViewById(id);
    }

   /* public static void setContentView(Activity act, int layoutId) {
        act.setContentView(layoutId);
    }
*/
    public static <T extends View> T findViewById(View rootView, int id) {
        return (T) rootView.findViewById(id);
    }

    public static <T extends View> T findViewById(Fragment fragment, int id) {
        return findViewById(fragment.getView(), id);
    }

}
绑定视图的实现类

public final class ButterView {
    public static final String SUFFIX = "$ViewAdapter";
    static Map<Class<?>, InjectAdapter<?>> sInjectCache = new HashMap();

    public ButterView() {
    }

    public static void inject(Activity activity) {
        InjectAdapter adapter = getViewAdapter(activity.getClass());
        adapter.injects(activity);
    }

    public static void inject(Fragment fragment, View rootView) {
        InjectAdapter adapter = getViewAdapter(fragment.getClass());
        if(fuckTheFragment(fragment, rootView)) {
            adapter.injects(fragment);
        }
    }

    private static boolean fuckTheFragment(Fragment fragment, View rootView) {
        try {
            Class e;
            for(e = fragment.getClass(); e != Object.class && !e.equals(Fragment.class); e = e.getSuperclass()) {
                ;
            }

            Field rootViewField = e.getDeclaredField("mView");
            rootViewField.setAccessible(true);
            rootViewField.set(fragment, rootView);
            Log.e("", "### getView " + fragment.getView());
            return true;
        } catch (SecurityException var4) {
            var4.printStackTrace();
        } catch (NoSuchFieldException var5) {
            var5.printStackTrace();
        } catch (IllegalArgumentException var6) {
            var6.printStackTrace();
        } catch (IllegalAccessException var7) {
            var7.printStackTrace();
        }

        return false;
    }

    public static void inject(View view) {
    }

    private static <T> InjectAdapter<T> getViewAdapter(Class<?> clazz) {
        InjectAdapter adapter = (InjectAdapter)sInjectCache.get(clazz);
        if(adapter == null) {
            String adapterClassName = clazz.getName() + "$ViewAdapter";

            try {
                Class e = Class.forName(adapterClassName);
                adapter = (InjectAdapter)e.newInstance();
                sInjectCache.put(e, adapter);
            } catch (ClassNotFoundException var4) {
                var4.printStackTrace();
            } catch (InstantiationException var5) {
                var5.printStackTrace();
            } catch (IllegalAccessException var6) {
                var6.printStackTrace();
            }
        }

        Log.e("", "### find adapter : " + adapter);
        return (InjectAdapter)adapter;
    }

}

同样的需要clean Project ,rebuild,生成Java文件
在MainActivity中验证:

public class MainActivity extends AppCompatActivity {
    @InjectView(R.id.textView)
    TextView mTextView;
    @InjectView(R.id.textView1)
    TextView mTextView1;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
          setContentView(R.layout.activity_main);
        //    mTextView = (TextView) findViewById(R.id.textView);
        ButterView.inject(this);
        mTextView.setText("我是编译时的注解");
        mTextView1.setText("我是第二个注解");
   //  showMessage();

    }

贴出utils和模型类里面的代码

public final class AnnotationUtil {
    /**
     * 获取包名
     * @param processingEnv
     * @param element
     * @return
     */
    public static String getPackageName(ProcessingEnvironment processingEnv, Element element) {
        return processingEnv.getElementUtils().getPackageOf(element).getQualifiedName().toString();
    }
    /**
     * 获取某个字段的完整的路径
     *
     * @param varElement
     * @return
     */
    public static String getParentClassName(ProcessingEnvironment processingEnv,VariableElement varElement) {
        // 获取该元素所在的类型,例如某个View是某个Activity的字段,这里就是获取这个Activity的类型
        TypeElement typeElement = (TypeElement) varElement.getEnclosingElement();
        // 获取typeElement的包名
        String packageName = AnnotationUtil.getPackageName(processingEnv, typeElement);
        // 类型的完整路径名,比如某个Activity的完整路径
        return packageName + "." + typeElement.getSimpleName().toString();
    }
}

public class InjectorInfo {
    public static final String SUFFIX = "$ViewAdapter";
    /**
     * 被注解的类的包名
     */
    public String packageName;
    /**
     * 被注解的类的类名
     */
    public String classlName;
    /**
     * 要创建的InjectAdapter类的完整路径,新类的名字为被注解的类名 + "$ViewAdapter", 与被注解的类在同一个包下
     */
    public String newClassName;


    private TypeElement typeElement;
    private int layoutId;

    private Map<Integer, ViewInfo> idViewMap = new HashMap<>();

    public InjectorInfo(String packageName, String classlName) {
        this.packageName = packageName;
        newClassName=classlName+SUFFIX;
        this.classlName = classlName;
    }

    public TypeElement getTypeElement() {
        return typeElement;
    }

    public void setTypeElement(TypeElement typeElement) {
        this.typeElement = typeElement;
    }

    public int getLayoutId() {
        return layoutId;
    }

    public void setLayoutId(int layoutId) {
        this.layoutId = layoutId;
    }


    public void putViewInfo(int id, ViewInfo viewInfo) {
        idViewMap.put(id, viewInfo);
    }

    public Map<Integer, ViewInfo> getIdViewMap() {
        return idViewMap;
    }

    public String getClassFullPath(){

        return packageName+ "."+newClassName;
    }
}

public class ViewInfo {
    private int id;
    private String type;
    private String name;

    public ViewInfo(int id, String type, String name) {
        this.id = id;
        this.type = type;
        this.name = name;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

运行后如图:

Paste_Image.png

这样一个简单的编译时注解框架就打造好了。

相关文章

网友评论

      本文标题:打造一个简易的编译时注解框架(二)

      本文链接:https://www.haomeiwen.com/subject/tnwizttx.html