Android中應(yīng)該了解的注解知識(shí)(Android進(jìn)階之光筆記)

注解

本文講解一些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也不需要做修改

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市遣耍,隨后出現(xiàn)的幾起案子闺阱,更是在濱河造成了極大的恐慌,老刑警劉巖舵变,帶你破解...
    沈念sama閱讀 207,248評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件酣溃,死亡現(xiàn)場(chǎng)離奇詭異瘦穆,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)赊豌,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,681評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門扛或,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人碘饼,你說(shuō)我怎么就攤上這事熙兔。” “怎么了艾恼?”我有些...
    開封第一講書人閱讀 153,443評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵住涉,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我钠绍,道長(zhǎng)舆声,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,475評(píng)論 1 279
  • 正文 為了忘掉前任五慈,我火速辦了婚禮纳寂,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘泻拦。我一直安慰自己毙芜,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,458評(píng)論 5 374
  • 文/花漫 我一把揭開白布争拐。 她就那樣靜靜地躺著腋粥,像睡著了一般。 火紅的嫁衣襯著肌膚如雪架曹。 梳的紋絲不亂的頭發(fā)上隘冲,一...
    開封第一講書人閱讀 49,185評(píng)論 1 284
  • 那天,我揣著相機(jī)與錄音绑雄,去河邊找鬼。 笑死万牺,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的覆旱。 我是一名探鬼主播扣唱,決...
    沈念sama閱讀 38,451評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼噪沙,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了霹购?” 一聲冷哼從身側(cè)響起朋腋,我...
    開封第一講書人閱讀 37,112評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤贞奋,失蹤者是張志新(化名)和其女友劉穎穷绵,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體勾缭,經(jīng)...
    沈念sama閱讀 43,609評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡俩由,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,083評(píng)論 2 325
  • 正文 我和宋清朗相戀三年幻梯,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了碘梢。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片伐蒂。...
    茶點(diǎn)故事閱讀 38,163評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡逸邦,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情烛卧,我是刑警寧澤,帶...
    沈念sama閱讀 33,803評(píng)論 4 323
  • 正文 年R本政府宣布呈宇,位于F島的核電站甥啄,受9級(jí)特大地震影響炬搭,放射性物質(zhì)發(fā)生泄漏宫盔。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,357評(píng)論 3 307
  • 文/蒙蒙 一有额、第九天 我趴在偏房一處隱蔽的房頂上張望巍佑。 院中可真熱鬧寄悯,春花似錦、人聲如沸腻菇。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,357評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)邦危。三九已至,卻和暖如春希坚,著一層夾襖步出監(jiān)牢的瞬間陵且,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,590評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工茬底, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留获洲,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,636評(píng)論 2 355
  • 正文 我出身青樓最爬,卻偏偏與公主長(zhǎng)得像烂叔,于是被迫代替她去往敵國(guó)和親固歪。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,925評(píng)論 2 344

推薦閱讀更多精彩內(nèi)容