Android 編譯時註解


demo地址

http://download.csdn.net/download/ty_phone8/9966875


1、簡介

註解?

說說我所知的註解吧:

  • 標識,說到這兒可能就懵逼了,啥?
    舉個例子吧,比如:@Override、SupportedOptions、SupportedSourceVersion、SuppressWarnings等等,當然需要具體瞭解可以查看更多

  • 運行時動態處理
    這個大家見得就多了去了,在運行時拿到類的Class對象,然後遍歷其方法、變量,判斷有無註解聲明,然後做一些操作。這個東西性能低大多時候不建議使用,常見的框架有:xutils , afinal 等。

  • 編譯時動態處理
    說道這個東西可能做Android都瞭解吧!butterknife 這個框架有木有沒有使用過的?大多數人都是用過吧,沒使用過也沒關係,建議大家去看看。這類註解會在編譯的時候,根據註解標識,動態生成一些類或者xml都可以,在運行時期,這類註解是沒有的,會依靠動態生成的類做一些操作,因爲沒有反射,效率和直接調用方法沒什麼區別~~~

2、註解

http://blog.csdn.net/ty_phone8/article/details/77718519

或者看看其他的對註解屬性介紹的博客,看過之後,才能更好的理解下面例子的註解

3、例子

那我們就說一下過程:

前期準備:我們先看一看項目結構吧

這裏寫圖片描述

可以看出有兩個 java的jar 和一個Android的jar


首先呢,我們需要在整個項目的build文件中添加一句話:

classpath group: 'com.neenbedankt.gradle.plugins', name: 'android-apt', version: '1.8'

接下來呢,我們也需要在app項目的build文件中添加一句話:

apply plugin: 'com.neenbedankt.android-apt'

接下來,我們就按照inject-annotation、inject-apt、inject-api的順序創建jar包,當然不必在意jar包的名字,咱們想怎麼命名就怎麼命名。


inject-annotation 主要用來自定義註解

我這裏呢準備了兩個註解:
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.CLASS)
public @interface BindView {
    int value() default 0;
}
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.CLASS)
public @interface OnClick {
    int[] value() default {};
}

inject-apt 主要在編譯時獲取您的註解並且進行一些操作

我這裏呢,定義的處理器如下:
@AutoService(Processor.class)
@SupportedSourceVersion(SourceVersion.RELEASE_7)
public class AgentProcessor extends AbstractProcessor {
    ...

    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        ...
        return false;
    }
}

這兒直接使用了

compile 'com.google.auto.service:auto-service:1.0-rc3'

這個開元jar包來去除,建立複雜的 META-INF/services/javax.annotation.processing.Processor文件

對註解字段或者方法的操作:
//Gets all the elements of the "BindView" annotation
Set<? extends Element> bindViewElement = roundEnv.getElementsAnnotatedWith(BindView.class);

for (Element element : bindViewElement) {
    //Because the "BindView" annotation is a field, the strong is converted to the "VariableElement" type
    VariableElement variableElement = (VariableElement) element;

    //Provides access to information about types and their members
    TypeElement typeElement = (TypeElement) variableElement.getEnclosingElement();

    String qualifiedName = typeElement.getQualifiedName().toString();
    ProxyEntity entity = proxyEetityMap.get(qualifiedName);

    if (null == entity) {
        entity = new ProxyEntity(elementUtils, typeElement);
        proxyEetityMap.put(qualifiedName, entity);
    }

    BindView bindView = element.getAnnotation(BindView.class);
    int value = bindView.value();

    entity.setVariableElementMapValue(value, variableElement);
}
//Gets all the elements of the "OnClick" annotation
Set<? extends Element> onClickElement = roundEnv.getElementsAnnotatedWith(OnClick.class);

for (Element element : onClickElement) {
    //Because the "OnClick" annotation is a method, the strong is converted to the "ExecutableElement" type
    ExecutableElement executableElement = (ExecutableElement) element;

    //Provides access to information about types and their members
    TypeElement typeElement = (TypeElement) executableElement.getEnclosingElement();

    String qualifiedName = typeElement.getQualifiedName().toString();
    ProxyEntity entity = proxyEetityMap.get(qualifiedName);

    if (null == entity) {
        entity = new ProxyEntity(elementUtils, typeElement);
        proxyEetityMap.put(qualifiedName, entity);
    }

    OnClick onClick = element.getAnnotation(OnClick.class);
    int[] values = onClick.value();

    for (int value : values) {
        entity.setExecutableElementMapValue(value, executableElement);
    }
}
這裏呢,我建立了一個實體類來實現我需要進行的操作:
/*
    在這裏我使用了 javapoet 來生成我需要的java文件
    有需要可以區簡單的瞭解一下 javapoet 的用法
*/
public class ProxyEntity {
    public static final String PROXY = "ViewBinding";
    private String qualifiedName;//Full name of the class
    private String packageName;//The package name
    private String className;//taxon
    private String proxyClassName;//The proxy class name

    public ProxyEntity(Elements elementUtils, TypeElement typeElement) {
        qualifiedName = typeElement.getQualifiedName().toString();
        PackageElement packageElement = elementUtils.getPackageOf(typeElement);
        packageName = packageElement.getQualifiedName().toString();
        className = qualifiedName.substring(packageName.length() + 1).replace(".", "_");
        proxyClassName = className + "_" + PROXY;
    }

    public JavaFile GenerateProxyClasses() {
        ...
        return javaFile;
    }

    private Map<Integer, VariableElement> variableElementMap;
    private Map<Integer, ExecutableElement> executableElementMap;

    public void setVariableElementMapValue(Integer key, VariableElement value) {
        if (null == variableElementMap) {
            variableElementMap = new LinkedHashMap<>();
        }
        variableElementMap.put(key, value);
    }

    public void setExecutableElementMapValue(Integer key, ExecutableElement value) {
        if (null == executableElementMap) {
            executableElementMap = new LinkedHashMap<>();
        }
        executableElementMap.put(key, value);
    }
}

inject-api 在運行時的時候調用

這裏建立了一個接口,和一個獲取代理類的類

代理類需要實現的接口:

public interface ViewBinding<T> {
    void bind(T t, Object source);

    void unbind();
}

獲取代理類的類

public class AgentInject {
    private static final String SUFFIX = "_ViewBinding";

    public static void injectView(Activity activity) {
        ViewBinding proxyActivity = findProxyActivity(activity);
        View view = activity.getWindow().getDecorView();
        proxyActivity.bind(activity, view);
    }

    private static ViewBinding findProxyActivity(Object activity) {
        try {
            Class clazz = activity.getClass();
            Class injectorClazz = Class.forName(clazz.getName() + SUFFIX);
            return (ViewBinding) injectorClazz.newInstance();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        throw new RuntimeException(String.format("can not find %s , something when compiler.", activity.getClass().getSimpleName() + SUFFIX));
    }
}

在app中調用

public class MainActivity extends AppCompatActivity {

    @BindView(R.id.text1)
    TextView text1;
    @BindView(R.id.text2)
    Button text2;
    @BindView(R.id.text3)
    TextView text3;
    @BindView(R.id.text4)
    Button text4;
    @BindView(R.id.text5)
    TextView text5;
    @BindView(R.id.text6)
    Button text6;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        AgentInject.injectView(this);
        text1.setText("This is my first TextView.");
        text2.setText("This is my first Button.");
        text3.setText("This is my second TextView.");
        text4.setText("This is my second Button.");
        text5.setText("This is my third TextView.");
        text6.setText("This is my third Button.");
    }


    private static final String TAG = "MainActivity";

    @OnClick({R.id.text1, R.id.text3, R.id.text5})
    public void TestOnClickTxt(View v) {
        switch (v.getId()) {
            case R.id.text1:
                Log.d(TAG, "TestOnClickTxt:  1 - " + v);
                break;
            case R.id.text3:
                Log.d(TAG, "TestOnClickTxt:  2 - " + v);
                break;
            case R.id.text5:
                Log.d(TAG, "TestOnClickTxt:  3 - " + v);
                break;
        }
    }

    @OnClick({R.id.text2, R.id.text4, R.id.text6})
    public void TestOnClickBtn(View v) {
        switch (v.getId()) {
            case R.id.text2:
                Log.d(TAG, "TestOnClickTxt:  1 - " + v);
                break;
            case R.id.text4:
                Log.d(TAG, "TestOnClickTxt:  2 - " + v);
                break;
            case R.id.text6:
                Log.d(TAG, "TestOnClickTxt:  3 - " + v);
                break;
        }
    }
}

demo地址

http://download.csdn.net/download/ty_phone8/9966875


知識貴在分享!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章