Android应用开发之分享一个简单易懂的Android编译时注解实践项目Demo
凌雪 2018-10-24 来源 :网络 阅读 1798 评论 0

摘要:本文将带你了解Android应用开发之分享一个简单易懂的Android编译时注解实践项目Demo,希望本文对大家学Android有所帮助。

本文将带你了解Android应用开发之分享一个简单易懂的Android编译时注解实践项目Demo,希望本文对大家学Android有所帮助。

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()的功能:
?1TextView 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 annotations,   RoundEnvironment roundEnv)   {        Set elementSet =   roundEnv.getElementsAnnotatedWith(RioBind.class);//          收集信息,分类        Map> cacheMap = new   HashMap<>();        for   (Element element : elementSet) {            VariableElement   variableElement = (VariableElement)   element;            String   className = getClassName(variableElement);//            List  filedList =   cacheMap.get(className);            if   (null == filedList) {                filedList   = new   ArrayList<>();                cacheMap.put(className,   filedList);            }            filedList.add(variableElement);        }//          产生java文件        Iterator  iterator =   cacheMap.keySet().iterator();        while   (iterator.hasNext())   {//            准备好生成java文件产生的信息//              获取class的名字            String   className =   iterator.next();//              获取class中的所有成员属性            List  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,   ListcacheElements   ){        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,   ListcacheElements)   {        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   SetgetSupportedAnnotationTypes()   {        Set  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  {    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{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);} }
  

本文由职坐标整理并发布,希望对同学们有所帮助。了解更多详情请关注职坐标移动开发之Android频道!

本文由 @凌雪 发布于职坐标。未经许可,禁止转载。
喜欢 | 0 不喜欢 | 0
看完这篇文章有何感觉?已经有0人表态,0%的人喜欢 快给朋友分享吧~
评论(0)
后参与评论

您输入的评论内容中包含违禁敏感词

我知道了

助您圆梦职场 匹配合适岗位
验证码手机号,获得海同独家IT培训资料
选择就业方向:
人工智能物联网
大数据开发/分析
人工智能Python
Java全栈开发
WEB前端+H5

请输入正确的手机号码

请输入正确的验证码

获取验证码

您今天的短信下发次数太多了,明天再试试吧!

提交

我们会在第一时间安排职业规划师联系您!

您也可以联系我们的职业规划师咨询:

小职老师的微信号:z_zhizuobiao
小职老师的微信号:z_zhizuobiao

版权所有 职坐标-一站式AI+学习就业服务平台 沪ICP备13042190号-4
上海海同信息科技有限公司 Copyright ©2015 www.zhizuobiao.com,All Rights Reserved.
 沪公网安备 31011502005948号    

©2015 www.zhizuobiao.com All Rights Reserved