Android开发必备——注解_android 注解

}

// 子类继承了父类,但是没有任何实现
public class Child extends Parent {
}


然后我们通过子类获取注解信息,测试代码如下:



private void test() {
Annotation[] annotations = Child.class.getAnnotations();
System.out.println(“-----start-----”);
for (Annotation annotation : annotations) {
System.out.println(annotation.toString());
}
System.out.println(“------end------”);
}


运行以上代码,最终会打印



> 
> System.out: -----start----- System.out: @com.payne.annotation.demo.ExAnn() System.out: ------end------
> 
> 
> 


通过以上打印可以发现:父类里面的注解如果有被@Inherited注解,其子类才可以获取到父类的注解。


## 3. 自定义注解


### 3.1 运行时注解


以前在Android开发过程中,我们经常会使用到`findViewById`去获取`View`,大量的视图控件意味着我们会去重复多次使用`findViewById`,以至于后面出现了第三方框架`ButterKnife`,下面以`findViewById`做示例。


1. 首先创建一个注解类,创建的注解类和接口有点像,只不过在`interface`前面多了一个@符号,如下:



@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.METHOD})
public @interface BindView {
int value();
}


2. 其次在测试类中使用,代码如下:



public class MainActivity extends AppCompatActivity {

@BindView(R.id.tv_show)
TextView tvShow;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    processView(this);
    tvShow.setText("赋值成功");
}

/**
 * 反射处理findViewById
 */
public static void processView(Activity activity) {
    // 获取Activity中所有的字段
    Field[] fields = activity.getClass().getDeclaredFields();
    if (fields == null) {
        return;
    }
    // 遍历字段数组,找到带有BindView注解的字段
    for (Field field : fields) {
        BindView bindView = field.getAnnotation(BindView.class);
        if (bindView == null) {
            continue;
        }
        // 获取注解值——即ViewID
        int value = bindView.value();
        // 通过ViewID找到View
        View viewById = activity.findViewById(value);
        try {
            // 给字段View赋值
            field.set(activity, viewById);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }
}

}


下面是运行效果:


![运行结果](https://img-blog.csdnimg.cn/img_convert/5fe2228065b3bea00586854ab6b8932b.png)


可以看到并没有直接给`tvShow`设置`findViewById`,但是运行之后`TextView`依然被成功设置为"赋值成功"。这是因为运行时在processView方法中通过反射获取并设值。


### 3.2 编译时注解


反射使用多了比较影响性能,翻看`ButterKnife`、`Retrofit`等第三方框架源码我们也能发现其并不是通过运行时反射赋值,而是通过编译工具在编译期间就对注解进行了处理,而处理注解需要使用到注解处理器,那什么是注解处理器呢? 注解处理器:英文为Annotation Processor,顾名思义,是用来处理注解的工具,其基本原理是将自定义注解处理器注册到编译器,编译器在编译阶段会去执行注册了的自定义注解处理器,完成对应的代码注入。 实现自定义注解处理器主要分为三步:


1. 编写注解。
2. 编写继承自`javax.annotation.processing`包下的`AbstractProcessor`类的自定义注解处理器。
3. 将自定义注解处理器注册到编译器。


下面是自定义注解处理器的实现示例:


![示例](https://img-blog.csdnimg.cn/img_convert/84cb1bf889c6fc012edbd1fa01b27771.png)


如上图,示例主要包括两个Java module和一个Android module。


1. test-annotation:Java module,主要用来存放自定义注解。
2. test-compiler:Java module,主要存放继承自`AbstractProcessor`类的自定义注解处理器类。
3. test-butterknife:Android module,主要存放工具类,用来反射获取编译器根据注解处理器生成的类。


#### 3.2.1 定义注解


test-annotation模块下定义一个需要处理的注解类`BindView`



@Retention(RetentionPolicy.CLASS)
@Target({ElementType.FIELD})
public @interface BindView {
int value();
}


#### 3.2.2 实现注解处理器



public class AnnotationCompiler extends AbstractProcessor {

/**
 * 设置支持的源版本,默认为RELEASE_6
 * 两种方式设置版本:
 * 1. 此处返回指定版本
 * 2. 类上面设置SupportedSourceVersion注解,并传入版本号
 */
@Override
public SourceVersion getSupportedSourceVersion() {
    return SourceVersion.latestSupported();
}

/**
 * 设置支持的注解类型,默认为空集合(都不支持)
 * 两种方式设置注解集合:
 * 1. 此处返回支持的注解集合
 * 2. 类上面设置SupportedAnnotationTypes注解,并传入需要支持的注解
 */
@Override
public Set<String> getSupportedAnnotationTypes() {
    Set<String> types = new HashSet<>();
    types.add(BindView.class.getCanonicalName());
    return types;
}

/**
 * 初始化操作
 *
 * @param processingEnv 环境
 */
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
    super.init(processingEnv);
}

/**
 * 处理注解
 *
 * @param set              待处理的注解集合
 * @param roundEnvironment RoundEnvironment
 * @return 返回true表示后续处理器不再处理
 */
@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
    Set<? extends Element> elements = roundEnvironment.getElementsAnnotatedWith(BindView.class);
    //TypeElement->类    ExecutableElement->方法   VariableElement->属性
    Map<String, List<VariableElement>> map = new HashMap<>(16);
    for (Element element : elements) {
        //属性元素
        VariableElement variableElement = (VariableElement) element;
        //获取类名
        String activityName = variableElement.getEnclosingElement().getSimpleName().toString();
        //根据类名将属性元素保存在集合中
        List<VariableElement> variableElements = map.get(activityName);
        if (variableElements == null) {
            variableElements = new ArrayList<>();
            map.put(activityName, variableElements);
        }
        variableElements.add(variableElement);
    }

    if (map.size() > 0) {
        for (String activityName : map.keySet()) {
            //根据类名获取属性元素集合
            List<VariableElement> variableElements = map.get(activityName);
            //获取类元素
            TypeElement enclosingElement = (TypeElement) variableElements.get(0).getEnclosingElement();
            //获取类的包名
            String packageName = processingEnv.getElementUtils().getPackageOf(enclosingElement).toString();
            //生成对应的类
            generateClass(variableElements, packageName, activityName);
        }
    }
    return false;
}

/**
 * 根据注解信息生成对应的类,本方法中手动生成类文件内容
 * 我们还可以使用第三方工具JavaPoet优雅的生成,具体参考地址:https://github.com/square/javapoet
 *
 * @param variableElements 设置了对应注解的属性元素的集合
 * @param packageName      包名
 * @param activityName     类名
 */
private void generateClass(List<VariableElement> variableElements, String packageName, String activityName) {
    Writer writer = null;
    try {
        JavaFileObject sourceFile = processingEnv.getFiler().createSourceFile(packageName + "." + activityName + "_ViewBinding");
        writer = sourceFile.openWriter();
        //包名
        writer.write("package " + packageName + ";\n");
        //导入包
        writer.write("import com.payne.buf.IBinder;\n");
        //类名以及实现的接口名
        writer.write("public class " + activityName + "_ViewBinding implements IBinder<"
                + packageName + "." + activityName + "> {\n");
        //实现接口中的方法
        writer.write("  @Override\n");
        writer.write("  public void bind(" + packageName + "." + activityName + " target) {\n");
        //遍历属性元素集合,根据信息生成findViewById操作
        for (VariableElement variableElement : variableElements) {
            String variableName = variableElement.getSimpleName().toString();
            int id = variableElement.getAnnotation(BindView.class).value();
            TypeMirror typeMirror = variableElement.asType();
            writer.write("      target." + variableName + " = (" + typeMirror + ") target.findViewById(" + id + ");\n");
        }
        writer.write("  }\n");
        writer.write("}\n");
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        if (writer != null) {
            try {
                writer.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

}


#### 3.2.3 注册注解处理器


有两种方法将自定义注解处理器注册到编辑器。


1. 手动注册:在test-compiler模块下的`src/main/`目录下创建`META-INF/services/`子目录,并创建文件名为`javax.annotation.processing.Processor`的文件,文件名是Processor类的全路径名,如下图:


![](https://img-blog.csdnimg.cn/img_convert/25f8dbcc7f0dd8be3f7d4c664dc5c064.png) ![](https://img-blog.csdnimg.cn/img_convert/ae68994efe8efd56a0c6766d352a86dc.png)


文件中加入自定义注解处理器的全路径名`com.payne.annotation.test_compiler.AnnotationCompiler`,如下图:


![](https://img-blog.csdnimg.cn/img_convert/4d06200539a53862a62fb50fdc290b73.png)


2. 自动注册:依赖于Google的工具框架。


首先在test-compiler模块下的`build.gradle`中加入依赖框架,然后在自定义注解处理器类上面添加`@AutoService(Processor.class)`注解即可。



implementation ‘com.google.auto.service:auto-service:1.0’
annotationProcessor ‘com.google.auto.service:auto-service:1.0’


![](https://img-blog.csdnimg.cn/img_convert/4821809cb356c33593bc972541ed2658.png)


编译之后可以发现test-compiler模块下的`build`文件夹中自动生成了和我们手动注册一样的文件。


![](https://img-blog.csdnimg.cn/img_convert/1dd97eb047fe28fc2af06ffcf5010fbe.png)


最后在test-butterknife模块下提供`IBinder`接口以及`findViewById`的绑定类供App调用。



public interface IBinder {
void bind(T target);
}



public class PayneButterKnife {
@SuppressWarnings(“unchecked”)
public static void bind(Activity activity) {
String name = activity.getClass().getName() + “_ViewBinding”;
try {
Class<?> aClass = Class.forName(name);
IBinder iBinder = (IBinder) aClass.newInstance();
iBinder.bind(activity);
} catch (Exception e) {
e.printStackTrace();
}
}
}


## 4. 小结


1. 注释面向开发者,注解面向程序。
2. 可以通过`Target`元注解设置注解的作用范围,`Retention`元注解设置注解的作用时机。
3. 源码时注解主要作用源码阶段,编译则被舍弃,主要面向编辑器等开发工具,用来在开发阶段提示开发者或者限制开发者使用范围。
4. 编译时注解主要作用于编译阶段,编译之后则被舍弃,主要面向编译器,可以根据注解信息生成对应的类。
5. 运行时注解主要作用于运行阶段,注解信息运行时依然存在,可以通过反射获取使用。


### 文末


要想成为架构师,那就不要局限在编码,业务,要会选型、扩展,提升编程思维。此外,良好的职业规划也很重要,学习的习惯很重要,但是最重要的还是要能持之以恒,任何不能坚持落实的计划都是空谈。


如果你没有方向,这里给大家分享一套由阿里高级架构师编写的《Android八大模块进阶笔记》,帮大家将杂乱、零散、碎片化的知识进行体系化的整理,让大家系统而高效地掌握Android开发的各个知识点。  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/06e41b3932164f0db07014d54e6e5626.png)  
 相对于我们平时看的碎片化内容,这份笔记的知识点更系统化,更容易理解和记忆,是严格按照知识体系编排的。


## 总结

算法知识点繁多,企业考察的题目千变万化,面对越来越近的“金九银十”,我给大家准备好了一套比较完善的学习方法,希望能帮助大家在有限的时间里尽可能系统快速的恶补算法,通过高效的学习来提高大家面试中算法模块的通过率。

这一套学习资料既有文字档也有视频,里面不仅仅有关键知识点的整理,还有案例的算法相关部分的讲解,可以帮助大家更好更全面的进行学习,二者搭配起来学习效果会更好。


**部分资料展示:**

![](https://img-blog.csdnimg.cn/img_convert/f3c5259bf8801319e5f917710cf28e15.webp?x-oss-process=image/format,png)
![](https://img-blog.csdnimg.cn/img_convert/dbc53fe58acd1a6d279c9da75043eefc.webp?x-oss-process=image/format,png)
![](https://img-blog.csdnimg.cn/img_convert/9c25e7169f14f03bfb9663bc88d1d2f3.webp?x-oss-process=image/format,png)
![](https://img-blog.csdnimg.cn/img_convert/df0fd987800396841e9b17c72700a23b.webp?x-oss-process=image/format,png)

**有了这套学习资料,坚持刷题一周,你就会发现自己的算法知识体系有明显的完善,离大厂Offer的距离更加近。**




**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**

**[需要这份系统化学习资料的朋友,可以戳这里获取](https://bbs.csdn.net/topics/618156601)**

**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**

14414655522)]
[外链图片转存中...(img-6m80TMjy-1714414655523)]
[外链图片转存中...(img-lt8ZJMc5-1714414655523)]

**有了这套学习资料,坚持刷题一周,你就会发现自己的算法知识体系有明显的完善,离大厂Offer的距离更加近。**




**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**

**[需要这份系统化学习资料的朋友,可以戳这里获取](https://bbs.csdn.net/topics/618156601)**

**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值