注解
本文講解一些Android中用到的基本注解只是及ButterKnife和Dagger2原理
注解分類
注解分為標(biāo)準(zhǔn)注解和元注解
標(biāo)準(zhǔn)注解
- @Override:對(duì)覆蓋超類中的方法進(jìn)行標(biāo)記,如果被標(biāo)記的方法并沒(méi)有實(shí)際覆蓋超類中的方法,則編譯器會(huì)發(fā)出警告.
- @Deprecated:對(duì)不鼓勵(lì)使用或者已經(jīng)過(guò)時(shí)的方法進(jìn)行注解,當(dāng)編程人員使用這些方法時(shí),將會(huì)在編譯時(shí)顯示提示信息
- @SupressWarnings:選擇性的取消特定代碼中的警告
- @SafeVarargs:JDK7新增,用來(lái)聲明使用了可變長(zhǎng)度參數(shù)的方法,其在與泛型類一起使用時(shí)不會(huì)出現(xiàn)類型安全問(wèn)題
元注解
元注解是用來(lái)注解其他的注解,從而創(chuàng)建新的注解
- @Target:注解所修飾的對(duì)象范圍
- @Inherited:表示注解可以被繼承
- @Documented:表示這個(gè)注解應(yīng)該被JavaDoc工具記錄
- @Retention:用來(lái)聲明注解的保留策略
- @Repeatable:JDK8新增,允許同一注解在同一聲明類型(類,屬性或方法)上多次使用
@Target注解取值是一個(gè)ElementType類型的數(shù)組,有以下幾種取值
- ElementType.TYPE:能修飾類,接口和枚舉類型
- ElementType.FIELD:能修飾成員變量
- ElementType.METHOD:能修飾方法
- ElementType.PARAMETER:能修飾參數(shù)
- ElementType.CONSTRUCTOR:能夠修飾構(gòu)造方法
- ElementType.LOCAL_VARIABLE:能修飾局部變量
- ElementType.ANNOTATION_TYPE:能修飾注解
- ElementType.PACKAGE:能修飾包
- ElementType.TYPE_PARAMETER:類型參數(shù)聲明
- ElementType.TYPE_USE:使用類型
@Retention注解有三種類型,表示不同級(jí)別的保留策略
- RetentionPolicy.SOURCE:源碼級(jí)注解,注解信息只會(huì)保留在.java源碼中,源碼在編譯后,注解信息被丟棄,不會(huì)保留在.class中
- RetentionPolicy.CLASS:編譯時(shí)注解,注解信息之后保留在.java源碼以及.class中,當(dāng)運(yùn)行Java程序時(shí),JVM會(huì)丟棄注解信息,不回保留在JVM中
- RetentionPolicy.RUNTIME:運(yùn)行時(shí)注解,當(dāng)運(yùn)行Java程序時(shí),JVM也會(huì)保留該注解信息,可以通過(guò)反射獲取該注解信息
定義注解
基本定義
定義新的注解類型使用@interface關(guān)鍵字,和定義接口很像
public @interface Swordsman{
}
使用注解
@Swordsman
public class AnnotationTest{
}
定義成員變量
注解只有成員變量,沒(méi)有方法,注解的成員變量在注解定義中以“無(wú)形參的方法”形式來(lái)聲明,其方法名定義了該成員變量的名字,其返回值定義了該成員變量的類型
public @interface Swordsman {
String name();
int age();
}
使用該注解時(shí)就應(yīng)該為該注解的成員變量指定值
@Swordsman(name = "張三",age = 23)
public class AnnotationTest {
}
定義成員變量時(shí),使用default關(guān)鍵字為其指定默認(rèn)值(使用默認(rèn)值時(shí)就不需要傳入?yún)?shù)了)
public @interface Swordsman {
String name() default "張三豐";
int age() default 99;
}
定義運(yùn)行時(shí)注解
用@Retention來(lái)設(shè)定注解的保留策略,三種策略的生命周期長(zhǎng)度為SOURCE《CLASS《RUNTIME,生命周期短的能起作用的地方,生命周期長(zhǎng)的也一定能起作用.
- 如果要在運(yùn)行時(shí)去動(dòng)態(tài)獲取注解信息,只能用RetentionPolicy.RUNTIME;
- 如果要在編譯時(shí)進(jìn)行一些預(yù)處理,比如生成一些輔助代碼,就使用RetentionPolicy.CLASS
- 如果只要做一些檢查性的操作,如@Override和@SuppressWarnings,則可選用RetentionPolicy.SOURCE
@Retention(RetentionPolicy.RUNTIME)
public @interface Swordsman {
String name() default "張三豐";
int age() default 99;
}
注解器處理
如果沒(méi)有處理注解的工具,注解也不會(huì)有太大的作用,對(duì)于不同的注解有不同的注解處理器,注解器的處理標(biāo)準(zhǔn)
- 針對(duì)運(yùn)行時(shí)注解采用反射機(jī)制處理
- 針對(duì)編譯時(shí)注解采用AbstractProcessor處理
編寫運(yùn)行時(shí)注解處理器
運(yùn)行時(shí)注解需要用到反射機(jī)制
@Documented
@Target(ElementType.METHOD)//定義方法
@Retention(RetentionPolicy.RUNTIME)
public @interface GET{
String value() default "";
}
上面代碼是Retrofit中定義的@GET注解
@GET(value = "http://baidu.com")
public String getIpMsg() {
return "";
}
寫一個(gè)簡(jiǎn)單的注解處理器
public static void main(String [] args){
Method[] methods = MainActivity.AnnotationTest.class.getDeclaredMethods();
for (Method method : methods) {
MainActivity.AnnotationTest.GET get = method.getAnnotation(MainActivity.AnnotationTest.GET.class);
System.out.println(get.value());
}
}
getDeclaredMethods和getAnnotation倆個(gè)反射方法都屬于AnnotatedElement接口,Class,Method和Filed等類都實(shí)現(xiàn)了該接口,調(diào)用getAnnotation方法返回指定類型的注解對(duì)象,也就是GET,調(diào)用GET的value方法返回從GET對(duì)象中提取的元素的值
編譯時(shí)注解處理器
定義注解
創(chuàng)建Java Library來(lái)專門存放注解,Library名為annotations
這個(gè)注解類似于ButterKnife的@BindView
@Retention(RetentionPolicy.CLASS)
@Target(ElementType.FIELD)
public @interface BindView {
int value() default -1;
}
創(chuàng)建Java Library存放注解處理器,Library命名為processor,配置processor的build.gradle依賴annotations
apply plugin: 'java-library'
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
compile project(':annotations')
}
sourceCompatibility = "1.7"
targetCompatibility = "1.7"
編寫注解處理器ClassProcessor
//Java7以后猿挚,使用下面?zhèn)z個(gè)注解代替對(duì)應(yīng)的方法骤铃,但考慮兼容問(wèn)題,一般還是實(shí)現(xiàn)方法
//@SupportedSourceVersion(SourceVersion.RELEASE_8)
//@SupportedAnnotationTypes("com.yangdxg.annotation.cls.BindView")
public class ClassProcessor extends AbstractProcessor {
/**
* 被注解處理工具調(diào)用浦夷,輸入processingEnvironment參數(shù)
* processingEnvironment提供很多有用的工具類,如Elements辜王,Types,F(xiàn)iler和Messager等
* @param processingEnvironment
*/
@Override
public synchronized void init(ProcessingEnvironment processingEnvironment) {
super.init(processingEnvironment);
}
/**
* 相當(dāng)于每個(gè)處理器的祝函數(shù)main(),這里寫掃描誓禁,評(píng)估和處理注解的代碼以及生成java文件肾档,
* 出入?yún)?shù)roundEnviroment摹恰,可以查詢出包含特定注解的被注解元素
* @param set
* @param roundEnvironment
* @return
*/
@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
return false;
}
/**
* 必須指定的方法,指定這個(gè)注解處理器是注冊(cè)給那個(gè)注解的怒见,返回一個(gè)字符串的集合俗慈,包含本處理器想要處理的注解類型的合法全稱
* @return
*/
@Override
public Set<String> getSupportedAnnotationTypes() {
LinkedHashSet<String> annotations = new LinkedHashSet<>();
annotations.add(BindView.class.getCanonicalName());
return annotations;
}
/**
* 指定使用的Java版本
* 一般返回 SourceVersion.latestSupported()
*
* @return
*/
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latestSupported();
}
}
實(shí)現(xiàn)process方法
@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
Messager messager = processingEnv.getMessager();
for (Element element : roundEnvironment.getElementsAnnotatedWith(BindView.class)) {
if (element.getKind() == ElementKind.FIELD) {
//使用messager的printMessage方法來(lái)打印出注解修飾的成員變量的名稱
messager.printMessage(Diagnostic.Kind.NOTE, "printMessage:" + element.toString());
}
}
return true;
}
注冊(cè)注解處理器
為了使用注解處理器,需要用一個(gè)服務(wù)文件來(lái)注冊(cè),創(chuàng)建這個(gè)服務(wù)文件
- 在processor庫(kù)的main目錄下新建resources資源文件夾
- 在resources中建立META-INF/services目錄文件夾
- 在META-INF/services中創(chuàng)建javax.annotation.processing.Processor文件,這個(gè)文件的內(nèi)容是注解處理器的名稱,這里文件內(nèi)容是com.yangdxg.processor.ClassProcessor
可以使用AutoService幫助完成上面步驟
- 添加依賴auto-sercvice
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
compile project(':annotations')
compile 'com.google.auto.service.auto-service:1.0-rc2'
}
- 在注解處理器中添加@AutoService(Processor.class)
@AutoService(Processor.class)
public class ClassProcessor extends AbstractProcessor {
- 在app中添加對(duì)注解器的依賴
compile project(':annotations')
compile project(':processor')
- 在Activity中使用注解
@BindView(value = R.id.tv_text)
TextView mTextView;
- 先對(duì)工程clean再M(fèi)ake Project,在Gradle Console中就打印出了注解方法
注: printMessage:mTextView
使用android-apt插件
應(yīng)用了processor庫(kù),但注解處理器只在編譯處理期間需要用到,編譯處理完后就沒(méi)有實(shí)際作用了,而主工程添加了這個(gè)庫(kù)會(huì)引入很多不必要的文件,為了解決這個(gè)問(wèn)題引入插件android-apt,它的作用是
- 僅僅在編譯時(shí)期去依賴注解處理器所在的函數(shù)庫(kù)并進(jìn)行工作,但不會(huì)打包到APK中
- 為注解處理器生成的代碼設(shè)置好路徑,以便Android Studio能夠找到它
- 在app的build.gradle中以apt的方式引入注解處理器processor
dependencies {
annotationProcessor ':processor'
依賴注入的原理
控制反轉(zhuǎn)
為了解決對(duì)象之間耦合度過(guò)高的問(wèn)題,提出了IoC理論,用來(lái)實(shí)現(xiàn)對(duì)象之間的解耦,即控制反轉(zhuǎn),借助第三方實(shí)現(xiàn)具有依賴關(guān)系的對(duì)象之間的解耦
依賴注入
控制反轉(zhuǎn)是獲得依賴對(duì)象的過(guò)程被反轉(zhuǎn)了,控制反轉(zhuǎn)之后,獲得依賴對(duì)象的過(guò)程由自身管理變?yōu)橛蒊oC容器主動(dòng)注入
依賴注入的實(shí)現(xiàn)方式
構(gòu)造方法注入
public class Car{
private Engine mEngine;
public Car(Engine engine){
this.mEngine=engine;
}
}
Setter方法注入
public class Car{
private Engine mEngine;
public void set(Engine engine){
this.mEngine=engine;
}
}
接口注入
public interface ICar{
public void setEngine(Engine engine);
}
Car類實(shí)現(xiàn)ICar
public class Car implement ICar{
private Engine mEngine;
public void setEngine(Engine engine){
this.mEngine=engine;
}
}
通過(guò)以上三種方式,Car和Engine解耦合了,Car不關(guān)心Engine的實(shí)現(xiàn),即使Engine的類型變換了,Car也不需要做修改