Android 编译时注解实践项目Demo-简单易懂

Android编译时注解需要掌握的知识点:

1、编译时 与运行时的区别

2、注解

3、反射

。。。。。

一、注解

说的简单点就是Android代码中常见到的:@Override 这一类是不是很简单哈哈,深入了解可以查看其它资料

二、开干

(一)搞一个类似butterknife的简单的项目,帮助理解编译时注解,整个工程目录差不多是这样:

bind-annotation: 注解相关java库

bind-compiler:用来解析bind-annotation中自定义的注解,java库

bind-api:主要是app项目中调用接口 ,android库

app : 简单测试

对了,就是这么简单

(1)bind-annotation: 注解相关java库

只有一个注解文件,灰常简单,直接上代码,关于

package com.apacherio.jondy.rio_annotation;
 
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
 
/**
 * Created by Administrator on 2018/2/11 0011.
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.CLASS)
public @interface RioBind {
    int value();
}

(2)在app中试一哈上面写的注解

代码中的@RioBind 就是上面自定义的注解

package com.apacherio.jondy.annotationdemo;
 
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.widget.Button;
import android.widget.TextView;
 
import com.apacherio.jondy.bind_api.RioBinder;
import com.apacherio.jondy.rio_annotation.RioBind;
 
public class MainActivity extends AppCompatActivity {
 
@RioBind(R.id.tv) TextView tv;
@RioBind(R.id.btn) Button click;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        RioBinder.bind(this);
        tv.setText("静态编译模拟Butterknife绑定成功!");
    }
}

(3)bind-compiler:用来解析bind-annotation中自定义的注解

老规矩,直接上代码:基本都有注释。

重点关注:AbstractProcessor 、@AutoService(Processor.class)

这个类的主要作用是啥?

1、编译时,提取出包含有@RioBind的类,比如MainAcivity,不止一个类

2、在类中提取出使用@RioBind的field,比如 TextView Button,当然一个类中有不少于一个。这些field按类分在一起。
3、构建一个类,什么类呢?看下图 以$ViewBinder结尾的类,这个后缀可以自由发挥。

这个类的作用就是帮助我们实现findViewById()的功能:

TextView textView = (TextView)this. findViewById(R.id.tv);

这个需要field的类别 TextView ,Id ,Activity(Fragment)对象,这些都会在下面的代码中解析得到

@AutoService(Processor.class)
public class RioBindtProcessor extends AbstractProcessor {
    private Filer mFiler;
    private Messager mMessager;
    private Elements mElementsUtil;
 
    private static final String SUFFIX ="ViewBinder";
 
 
    @Override
    public synchronized void init(ProcessingEnvironment processingEnv) {
        super.init(processingEnv);
        mFiler = processingEnv.getFiler();
        mElementsUtil = processingEnv.getElementUtils();
        mMessager = processingEnv.getMessager();
 
    }
 
    /*
    * Element 的子类有以下4种
    * VariableElement //一般代表成员变量
    * ExecutableElement //一般代表类中的方法
    * TypeElement //一般代表代表类
    * PackageElement //一般代表Package
    * */
 
    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        Set<? extends Element> elementSet = roundEnv.getElementsAnnotatedWith(RioBind.class);
//        收集信息,分类
        Map<String, List<VariableElement>> cacheMap = new HashMap<>();
        for (Element element : elementSet) {
            VariableElement variableElement = (VariableElement) element;
            String className = getClassName(variableElement);
//
            List<VariableElement> filedList = cacheMap.get(className);
            if (null == filedList) {
                filedList = new ArrayList<>();
                cacheMap.put(className, filedList);
            }
            filedList.add(variableElement);
        }
//        产生java文件
        Iterator<String> iterator = cacheMap.keySet().iterator();
        while (iterator.hasNext()) {
//            准备好生成java文件产生的信息
//            获取class的名字
            String className = iterator.next();
//            获取class中的所有成员属性
            List<VariableElement> cacheElements = cacheMap.get(className);
//            获取包名
            String packageName = getPackageName(cacheElements.get(0));
//            获取最后生成文件的文件名:className+"$"+SUFFIX
            String bindViewClass = className+"$"+SUFFIX;
//            生成额外的文件,x写文件流
            Writer writer =null;
            try {
                JavaFileObject javaFileObject = mFiler.createSourceFile(bindViewClass);
//                拼接字符串
                writer = javaFileObject.openWriter();
//                获取简短新代理类名称
                String sampleClass =cacheElements.get(0).getEnclosingElement().getSimpleName().toString();
                String sampleBindViewClass =sampleClass+"$"+SUFFIX;
                writer.write(generateJavaCode(packageName,sampleBindViewClass,className, cacheElements));
                writer.close();
      
            } catch (IOException e) {
                e.printStackTrace();
            }
 
        }
 
 
        return false;
    }
    public String generateJavaCode(String mPackageName,String mProxyClassName,String className, List<VariableElement> cacheElements ){
        StringBuilder builder = new StringBuilder();
        builder.append("package " + mPackageName).append(";\n\n");
        builder.append("import com.apacherio.jondy.bind_api.*;\n");
//        builder.append("import android.view.View;\n");
        builder.append("public class ").append(mProxyClassName).append(" implements " + SUFFIX + "<" + className + ">");
        builder.append("\n{\n");
        generateMethod(builder,className,cacheElements);
        builder.append("\n}\n");
        return builder.toString();
    }
 
    private void generateMethod(StringBuilder builder,String className, List<VariableElement> cacheElements) {
        builder.append("public void bind("+className+" target  ){");
        builder.append("\n");
//        处理所有的成员属性
        for(VariableElement element :cacheElements){
            RioBind bindView = element.getAnnotation(RioBind.class);
            String filedName = element.getSimpleName().toString();
            TypeMirror typeMirror = element.asType();
            int id = bindView.value();
            builder.append("target.");
            builder.append(filedName );
            builder.append("= (");
            builder.append(typeMirror.toString());
            builder.append(")");
            builder.append("target.findViewById(");
            builder.append(""+id);
            builder.append(");");
            builder.append("\n");
 
        }
        builder.append("}\n");
 
    }
 
    private String getPackageName(VariableElement variableElement) {
        TypeElement typeElement = (TypeElement) variableElement.getEnclosingElement();
//        String packeName = mElementsUtil.getPackageOf(typeElement).getQualifiedName().toString();
        String packeName = processingEnv.getElementUtils().getPackageOf(typeElement).getQualifiedName().toString();
        return packeName;
    }
 
    private String getClassName(VariableElement element) {
        String packageName =getPackageName(element);
        TypeElement typeElement = (TypeElement) element.getEnclosingElement();
        String className =typeElement.getSimpleName().toString();
        return packageName+"."+className;
    }
 
    @Override
    public Set<String> getSupportedAnnotationTypes() {
        Set<String> annotations = new LinkedHashSet<>();
        annotations.add(RioBind.class.getCanonicalName());
        return annotations;
//        return super.getSupportedAnnotationTypes();
    }
 
    @Override
    public SourceVersion getSupportedSourceVersion() {
        return SourceVersion.latestSupported();
//        return super.getSupportedSourceVersion();
    }

(4) api 怎么搞?

直接上代码了!

RioBinder.java —— app调用接口 RioBinder.bind(this);

public class RioBinder {
    private  static final  String SUFFIX ="$ViewBinder";
    public static void bind(Activity target){
        Class<?>  clazz = target.getClass();
        String className =clazz.getName()+SUFFIX;
        try {
            Class<?>  binderClass = Class.forName(className);
            ViewBinder rioBind = (ViewBinder) binderClass.newInstance();
            rioBind.bind(target);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        }
    }
}

ViewBinder接口——用于(3)中构建以$ViewBinder结尾的类的继承 ,方便在RioBinder中直接调用该接口中的bind()方法

public interface ViewBinder<T> {
    public  void bind(T t);
}

(二)依赖关系及其他

app:

   implementation project(':bind-api')
    implementation project(':bind-annotation')
    annotationProcessor project(':bind-compiler')

bind-api: 

  implementation project(':bind-annotation')

bind-compiler:

apply plugin: 'java-library'
 
dependencies {
    implementation fileTree(include: ['*.jar'], dir: 'libs')
    implementation project(':bind-annotation')
    implementation 'com.google.auto.service:auto-service:1.0-rc2'
}
tasks.withType(JavaCompile) {
    options.encoding = "UTF-8"
}
 
sourceCompatibility = "1.7"
targetCompatibility = "1.7"

bind-annotation:

apply plugin: 'java-library'
 
dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
}
sourceCompatibility = "1.7"
targetCompatibility = "1.7"

(最后)点击As的make后,生成的类如下:

有点瑕疵,稍微更改下代码就可以了,省略....另外 ,类的生成这里可以用java类的生成方式来做,不用拼接字符串。

public class MainActivity$ViewBinder implements ViewBinder<com.apacherio.jondy.annotationdemo.MainActivity>
{
public void bind(com.apacherio.jondy.annotationdemo.MainActivity target  ){
target.tv= (android.widget.TextView)target.findViewById(2131165308);
target.click= (android.widget.Button)target.findViewById(2131165219);
}
 
}

效果:

项目demo:

https://download.csdn.net/download/xiaozhude/11059584

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值