android优雅私有方法注释,Android注解使用之通过annotationProcessor注解生成代码实现自己的findviewbyid框架...

freddon

发表于2018-03-07

阅读 2566 |

评论 0

早期android studio gradle版本需要使用android-apt,新版本直接使用annotationProcessor即可。

本项目没有使用AutoService注解,而是手动创建Processor文件,以了解autoservice到底做了什么事

## 前言

---

之前,开发android应用使用过butterknife、dagger、EventBus3等开源注解框架,当时只是简单的研究下原理,觉得轮子造好了使用就可以了,也没深究。直到需要自己开发sdk过程或者只需要少量类似butterknife的功能,才发现盲目引入一个三方库造成的影响实在是有点得不偿失。

于是看了几篇相关的博文,研究了下butterknife早期和最新的代码,了解到,实现注解方式的findviewbyid功能,大概分为两种:使用InvocationHandler、生成相应的java文件。前者使用大量次数的反射,后者运用缓存机制可以少次数的使用反射来调用生成的java文件来达成目的。本篇只记录使用后者的简单demo。

首先,灰常简要的介绍下apt、annotationProcessor

### apt

---

Annotation Processing Tool,是一种处理注释的工具,它对源代码文件进行检测找出其中的Annotation,可以使用它在代码编译期解析注解,并且生成新的 Java 文件,根据注解自动生成由开发者编写的代码。 Annotation处理器在处理Annotation时可以根据源文件中的Annotation生成额外的源文件和其它的文件,APT还会编译生成的源文件和原来的源文件,将它们一起生成class文件。

在Android Studio中使用apt,需要在根路径的build.gradle中引入依赖:

```

classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'

```

需要使用到的module中build.gradle还要加入:

```

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

```

### annotationProcessor

---

annotationProcessor是APT工具中的一种,他是google开发的内置框架,不需要引入。本篇使用annotationProcessor。

## 1 开始

---

### 1.1 项目结构预览

![项目结构预览](/i/o_1c80evuf5nv976913ga1oce1jub7.png "项目结构预览")

### 1.2 nsannotations模块

这是一个`java-library`

`build.gradle`

```gradle

apply plugin: 'java-library'

dependencies {

implementation fileTree(dir: 'libs', include: ['*.jar'])

sourceCompatibility = "1.8"

targetCompatibility = "1.8"

}

sourceCompatibility = "1.8"

targetCompatibility = "1.8"

```

这个模块源码只有一个注解文件,

`ViewById.java`

```java

package com.freddon.android.app.nsannotations;

import java.lang.annotation.ElementType;

import java.lang.annotation.Retention;

import java.lang.annotation.RetentionPolicy;

import java.lang.annotation.Target;

@Retention(RetentionPolicy.CLASS)

@Target(ElementType.FIELD)

public @interface ViewById {

int value();

}

```

### 1.3 nsannotations-compiler模块

同样,这是一个`java-library`

`build.gradle`

```

apply plugin: 'java-library'

dependencies {

implementation fileTree(include: ['*.jar'], dir: 'libs')

implementation project(':nsannotations')

//用来生成java文件的

implementation 'com.squareup:javapoet:1.9.0'

}

sourceCompatibility = "1.8"

targetCompatibility = "1.8"

```

创建一个AbstractProcessor的子类,用来处理注解。

并且在

`nsannotation-compiler/src/main/resources/META-INF/services/`下创建名为`javax.annotation.processing.Processor`的文件,内容为Processor子类的CanonicalName,如果是多个Processor,换行分隔,如:

```

com.freddon.android.app.nsannotation_compiler.NSViewProcessor

```

`NSViewProcessor.java`

```

package com.freddon.android.app.nsannotation_compiler;

import com.freddon.android.app.nsannotations.ViewById;

import java.io.IOException;

import java.util.HashSet;

import java.util.Set;

import java.util.TreeMap;

import javax.annotation.processing.AbstractProcessor;

import javax.annotation.processing.Messager;

import javax.annotation.processing.ProcessingEnvironment;

import javax.annotation.processing.RoundEnvironment;

import javax.lang.model.SourceVersion;

import javax.lang.model.element.Element;

import javax.lang.model.element.TypeElement;

import javax.tools.Diagnostic;

public class NSViewProcessor extends AbstractProcessor {

private TreeMap annotionClassesMap;

private Messager $messager;

@Override

public synchronized void init(ProcessingEnvironment processingEnv) {

super.init(processingEnv);

annotionClassesMap = new TreeMap<>();

$messager=processingEnv.getMessager();

}

@Override

public boolean process(Set extends TypeElement> annotations, RoundEnvironment roundEnv) {

annotionClassesMap.clear();

for (Element element : roundEnv.getElementsAnnotatedWith(ViewById.class)) {

TypeElement typeElement = (TypeElement) element.getEnclosingElement();

String className = typeElement.getQualifiedName().toString();

ViewAnnotionClass viewAnnotionClass = annotionClassesMap.get(className);

if (viewAnnotionClass==null){

viewAnnotionClass=new ViewAnnotionClass(typeElement, processingEnv.getElementUtils());

annotionClassesMap.put(className,viewAnnotionClass);

}

ViewField viewField=new ViewField(element);

//add field

viewAnnotionClass.addField(viewField);

}

for(ViewAnnotionClass viewAnnotionClass :annotionClassesMap.values()){

try {

viewAnnotionClass.generateFile().writeTo(processingEnv.getFiler());

} catch (IOException e) {

e.printStackTrace();

}

}

return true;

}

@Override

public Set getSupportedAnnotationTypes() {

Set supportedAnnotationTypes = new HashSet<>();

supportedAnnotationTypes.add(ViewById.class.getCanonicalName());

return supportedAnnotationTypes;

}

@Override

public SourceVersion getSupportedSourceVersion() {

return SourceVersion.latestSupported();

}

}

```

`ViewAnnotionClass.java`

```java

package com.freddon.android.app.nsannotation_compiler;

import com.squareup.javapoet.ClassName;

import com.squareup.javapoet.JavaFile;

import com.squareup.javapoet.MethodSpec;

import com.squareup.javapoet.ParameterizedTypeName;

import com.squareup.javapoet.TypeName;

import com.squareup.javapoet.TypeSpec;

import java.util.ArrayList;

import java.util.List;

import javax.lang.model.element.Modifier;

import javax.lang.model.element.Name;

import javax.lang.model.element.TypeElement;

import javax.lang.model.util.Elements;

/**

* Created by fred on 2018/2/27.

*/

public class ViewAnnotionClass {

static class TypeUtil {

public final static ClassName finderType = ClassName.get("com.freddon.android.app.nsannotation_api", "IFinder");

public final static ClassName binderType = ClassName.get("com.freddon.android.app.nsannotation_api", "IViewBinder");

}

private TypeElement typeElement;

private Elements elementUtils;

List fields;

public ViewAnnotionClass(TypeElement typeElement, Elements elementUtils) {

this.typeElement = typeElement;

this.elementUtils = elementUtils;

if (fields == null) {

fields = new ArrayList<>();

} else {

fields.clear();

}

}

public void addField(ViewField viewField) {

fields.add(viewField);

}

public JavaFile generateFile() {

//生成 IViewBinder api方法

// public interface IViewBinder {

//

// public void bindView(Object target, Object object, IFinder finder) ;

//

// void unbind(Object target);}

MethodSpec.Builder finderBuilder = MethodSpec.methodBuilder("bindView")

.returns(void.class)

.addModifiers(Modifier.PUBLIC)

.addAnnotation(Override.class)

.addParameter(TypeName.get(typeElement.asType()), "target")

.addParameter(TypeName.OBJECT, "object")

.addParameter(TypeUtil.finderType, "finder");

//方法体

for (ViewField field : fields){

finderBuilder.addStatement("target.$N = ($T)(finder.findView(object, $L))",

field.getFieldName(), ClassName.get(field.getFieldType()),

field.getResId());

}

//方法2 void unbind(Object target);

MethodSpec.Builder unbindbuilder = MethodSpec.methodBuilder("unbind")

.returns(void.class)

.addModifiers(Modifier.PUBLIC)

.addAnnotation(Override.class)

.addParameter(TypeName.get(typeElement.asType()), "target");

// .addParameter(TypeName.OBJECT, "target")

//方法体

for (ViewField field : fields) {

unbindbuilder.addStatement("target.$N = null",

field.getFieldName());

}

//生成类

TypeSpec clazz = TypeSpec.classBuilder(typeElement.getSimpleName() + "$$IViewBinder")

.addModifiers(Modifier.PUBLIC)

.addSuperinterface(ParameterizedTypeName.get(

TypeUtil.binderType,

TypeName.get(typeElement.asType())))

.addMethod(finderBuilder.build())

.addMethod(unbindbuilder.build())

.build();

String packageName = elementUtils.getPackageOf(typeElement).getQualifiedName().toString();

return JavaFile.builder(packageName, clazz).

build();

}

}

```

`ViewField.java`

```java

package com.freddon.android.app.nsannotation_compiler;

import com.freddon.android.app.nsannotations.ViewById;

import javax.lang.model.element.Element;

import javax.lang.model.element.ElementKind;

import javax.lang.model.element.Name;

import javax.lang.model.type.TypeMirror;

/**

* Created by fred on 2018/2/27.

*/

public class ViewField {

private TypeMirror fieldType;

private Name fieldName;

private int resId;

public ViewField(Element element) {

if (element.getKind() != ElementKind.FIELD) {

throw new IllegalArgumentException("Only fields can be annotated");

}

ViewById annotation= element.getAnnotation(ViewById.class);

resId=annotation.value();

fieldName=element.getSimpleName();

fieldType=element.asType();

}

public TypeMirror getFieldType() {

return fieldType;

}

public Name getFieldName() {

return fieldName;

}

public int getResId() {

return resId;

}

}

```

### 1.4 nsannotations-api模块

android-library模块,

`build.gradle`

```

apply plugin: 'com.android.library'

android {

compileSdkVersion 26

defaultConfig {

minSdkVersion 14

targetSdkVersion 26

versionCode 1

versionName "1.0"

testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"

}

buildTypes {

release {

minifyEnabled false

proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'

}

}

}

dependencies {

implementation fileTree(dir: 'libs', include: ['*.jar'])

implementation 'com.android.support:appcompat-v7:26.1.0'

}

```

`IFinder.java`

```

package com.freddon.android.app.nsannotation_api;

import android.view.View;

/**

* Created by fred on 2018/3/3.

*/

public interface IFinder {

View findView(Object object, int resId);

}

```

`Finder.java`

```

package com.freddon.android.app.nsannotation_api;

import android.app.Dialog;

import android.view.View;

import android.view.Window;

/**

* Created by fred on 2018/3/3.

*/

final class Finder implements IFinder {

@Override

public View findView(Object object, int resId) {

if (object instanceof View){

return ((View)object).findViewById(resId);

}

if (object instanceof Dialog){

return ((Dialog)object).findViewById(resId);

}

if (object instanceof Window){

return ((Window)object).findViewById(resId);

}

return null;

}

private View findView(Window object, int resId){

return object.findViewById(resId);

}

private View findView(Dialog object, int resId){

return object.findViewById(resId);

}

private View findView(View object, int resId){

return object.findViewById(resId);

}

}

```

`IViewBinder.java`

```

package com.freddon.android.app.nsannotation_api;

/**

* Created by fred on 2018/3/3.

*/

public interface IViewBinder {

void bindView(T target, Object object, IFinder finder) ;

void unbind(T target);

}

```

最终向外公开的api类

`FNViewBinder.java`

```

package com.freddon.android.app.nsannotation_api;

import java.util.Map;

import java.util.TreeMap;

/**

* Created by fred on 2018/3/3.

*/

public class FNViewBinder {

private static Finder Finder=new Finder();

static Map caches=new TreeMap<>();

public static void bind(Object target){

bind(target,target);

}

public static void bind(Object target,Object object){

bind(target,object,Finder);

}

private static void bind(Object target,Object object,Finder finder){

//获取注解所在的类

final String annotatedClassName = target.getClass().getName();

//获取生成的类

//先从缓存中获取

IViewBinder binder = caches.get(annotatedClassName);

if (binder==null){

try {

Class> clazz = Class.forName(annotatedClassName + "$$IViewBinder");

binder= (IViewBinder) clazz.newInstance();

caches.put(annotatedClassName,binder);

} catch (ClassNotFoundException e) {

e.printStackTrace();

} catch (IllegalAccessException e) {

e.printStackTrace();

} catch (InstantiationException e) {

e.printStackTrace();

}

}

if (binder!=null){

binder.bindView(target,object,finder);

}

}

public static void unBind(Object host) {

final String className = host.getClass().getName();

IViewBinder binder = caches.get(className);

if (binder != null) {

binder.unbind(host);

}

caches.remove(className);

}

}

```

### 1.5 app主项目

`build.gradle`

```

apply plugin: 'com.android.application'

apply plugin: 'kotlin-android'

apply plugin: 'kotlin-android-extensions'

apply plugin: 'kotlin-kapt'

android {

compileSdkVersion 26

defaultConfig {

applicationId "com.freddon.android.app.fnannotation"

minSdkVersion 14

targetSdkVersion 26

versionCode 1

versionName "1.0"

testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"

}

buildTypes {

release {

minifyEnabled false

proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'

}

}

}

dependencies {

implementation project(":nsannotation-api")

provided project(':nsannotations')

kapt project(':nsannotation-compiler')

implementation fileTree(include: ['*.jar'], dir: 'libs')

implementation "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"

implementation 'com.android.support:appcompat-v7:26.1.0'

implementation 'com.android.support.constraint:constraint-layout:1.0.2'

implementation 'com.android.support:design:26.1.0'

testImplementation 'junit:junit:4.12'

androidTestImplementation 'com.android.support.test:runner:1.0.1'

androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'

}

```

由于我引入了kotlin框架,所以依赖的地方`annotationProcessor`应改写为`kapt`

`MainActivity.java`

```

package com.freddon.android.app.fnannotation;

import android.support.v7.app.AppCompatActivity;

import android.os.Bundle;

import android.widget.TextView;

import com.freddon.android.app.nsannotation_api.FNViewBinder;

import com.freddon.android.app.nsannotations.ViewById;

public class MainActivity extends AppCompatActivity {

@ViewById(R.id.textView)

TextView textView;

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

FNViewBinder.bind(this);

textView.setText("Hello Annotation");

}

}

```

clean build项目后,将会在`app/build/generated/source/kapt/debug/com/freddon/android/app/fnannotation`下生成一个`MainActivity$$IViewBinder.java`文件。

同理,Listeners等注解也可以按照如此方式处理,减少每次手写代码。

完~

demo地址:[https://github.com/FRED5DON/AndroidTrials/tree/master/FnAnnotation](https://github.com/FRED5DON/AndroidTrials/tree/master/FnAnnotation)

分类 :日常记录

## 前言

---

之前,开发android应用使用过butterknife、dagger、EventBus3等开源注解框架,当时只是简单的研究下原理,觉得轮子造好了使用就可以了,也没深究。直到需要自己开发sdk过程或者只需要少量类似butterknife的功能,才发现盲目引入一个三方库造成的影响实在是有点得不偿失。

于是看了几篇相关的博文,研究了下butterknife早期和最新的代码,了解到,实现注解方式的findviewbyid功能,大概分为两种:使用InvocationHandler、生成相应的java文件。前者使用大量次数的反射,后者运用缓存机制可以少次数的使用反射来调用生成的java文件来达成目的。本篇只记录使用后者的简单demo。

首先,灰常简要的介绍下apt、annotationProcessor

### apt

---

Annotation Processing Tool,是一种处理注释的工具,它对源代码文件进行检测找出其中的Annotation,可以使用它在代码编译期解析注解,并且生成新的 Java 文件,根据注解自动生成由开发者编写的代码。 Annotation处理器在处理Annotation时可以根据源文件中的Annotation生成额外的源文件和其它的文件,APT还会编译生成的源文件和原来的源文件,将它们一起生成class文件。

在Android Studio中使用apt,需要在根路径的build.gradle中引入依赖:

```

classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'

```

需要使用到的module中build.gradle还要加入:

```

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

```

### annotationProcessor

---

annotationProcessor是APT工具中的一种,他是google开发的内置框架,不需要引入。本篇使用annotationProcessor。

## 1 开始

---

### 1.1 项目结构预览

![项目结构预览](/i/o_1c80evuf5nv976913ga1oce1jub7.png "项目结构预览")

### 1.2 nsannotations模块

这是一个`java-library`

`build.gradle`

```gradle

apply plugin: 'java-library'

dependencies {

implementation fileTree(dir: 'libs', include: ['*.jar'])

sourceCompatibility = "1.8"

targetCompatibility = "1.8"

}

sourceCompatibility = "1.8"

targetCompatibility = "1.8"

```

这个模块源码只有一个注解文件,

`ViewById.java`

```java

package com.freddon.android.app.nsannotations;

import java.lang.annotation.ElementType;

import java.lang.annotation.Retention;

import java.lang.annotation.RetentionPolicy;

import java.lang.annotation.Target;

@Retention(RetentionPolicy.CLASS)

@Target(ElementType.FIELD)

public @interface ViewById {

int value();

}

```

### 1.3 nsannotations-compiler模块

同样,这是一个`java-library`

`build.gradle`

```

apply plugin: 'java-library'

dependencies {

implementation fileTree(include: ['*.jar'], dir: 'libs')

implementation project(':nsannotations')

//用来生成java文件的

implementation 'com.squareup:javapoet:1.9.0'

}

sourceCompatibility = "1.8"

targetCompatibility = "1.8"

```

创建一个AbstractProcessor的子类,用来处理注解。

并且在

`nsannotation-compiler/src/main/resources/META-INF/services/`下创建名为`javax.annotation.processing.Processor`的文件,内容为Processor子类的CanonicalName,如果是多个Processor,换行分隔,如:

```

com.freddon.android.app.nsannotation_compiler.NSViewProcessor

```

`NSViewProcessor.java`

```

package com.freddon.android.app.nsannotation_compiler;

import com.freddon.android.app.nsannotations.ViewById;

import java.io.IOException;

import java.util.HashSet;

import java.util.Set;

import java.util.TreeMap;

import javax.annotation.processing.AbstractProcessor;

import javax.annotation.processing.Messager;

import javax.annotation.processing.ProcessingEnvironment;

import javax.annotation.processing.RoundEnvironment;

import javax.lang.model.SourceVersion;

import javax.lang.model.element.Element;

import javax.lang.model.element.TypeElement;

import javax.tools.Diagnostic;

public class NSViewProcessor extends AbstractProcessor {

private TreeMap annotionClassesMap;

private Messager $messager;

@Override

public synchronized void init(ProcessingEnvironment processingEnv) {

super.init(processingEnv);

annotionClassesMap = new TreeMap<>();

$messager=processingEnv.getMessager();

}

@Override

public boolean process(Set extends TypeElement> annotations, RoundEnvironment roundEnv) {

annotionClassesMap.clear();

for (Element element : roundEnv.getElementsAnnotatedWith(ViewById.class)) {

TypeElement typeElement = (TypeElement) element.getEnclosingElement();

String className = typeElement.getQualifiedName().toString();

ViewAnnotionClass viewAnnotionClass = annotionClassesMap.get(className);

if (viewAnnotionClass==null){

viewAnnotionClass=new ViewAnnotionClass(typeElement, processingEnv.getElementUtils());

annotionClassesMap.put(className,viewAnnotionClass);

}

ViewField viewField=new ViewField(element);

//add field

viewAnnotionClass.addField(viewField);

}

for(ViewAnnotionClass viewAnnotionClass :annotionClassesMap.values()){

try {

viewAnnotionClass.generateFile().writeTo(processingEnv.getFiler());

} catch (IOException e) {

e.printStackTrace();

}

}

return true;

}

@Override

public Set getSupportedAnnotationTypes() {

Set supportedAnnotationTypes = new HashSet<>();

supportedAnnotationTypes.add(ViewById.class.getCanonicalName());

return supportedAnnotationTypes;

}

@Override

public SourceVersion getSupportedSourceVersion() {

return SourceVersion.latestSupported();

}

}

```

`ViewAnnotionClass.java`

```java

package com.freddon.android.app.nsannotation_compiler;

import com.squareup.javapoet.ClassName;

import com.squareup.javapoet.JavaFile;

import com.squareup.javapoet.MethodSpec;

import com.squareup.javapoet.ParameterizedTypeName;

import com.squareup.javapoet.TypeName;

import com.squareup.javapoet.TypeSpec;

import java.util.ArrayList;

import java.util.List;

import javax.lang.model.element.Modifier;

import javax.lang.model.element.Name;

import javax.lang.model.element.TypeElement;

import javax.lang.model.util.Elements;

/**

* Created by fred on 2018/2/27.

*/

public class ViewAnnotionClass {

static class TypeUtil {

public final static ClassName finderType = ClassName.get("com.freddon.android.app.nsannotation_api", "IFinder");

public final static ClassName binderType = ClassName.get("com.freddon.android.app.nsannotation_api", "IViewBinder");

}

private TypeElement typeElement;

private Elements elementUtils;

List fields;

public ViewAnnotionClass(TypeElement typeElement, Elements elementUtils) {

this.typeElement = typeElement;

this.elementUtils = elementUtils;

if (fields == null) {

fields = new ArrayList<>();

} else {

fields.clear();

}

}

public void addField(ViewField viewField) {

fields.add(viewField);

}

public JavaFile generateFile() {

//生成 IViewBinder api方法

// public interface IViewBinder {

//

// public void bindView(Object target, Object object, IFinder finder) ;

//

// void unbind(Object target);}

MethodSpec.Builder finderBuilder = MethodSpec.methodBuilder("bindView")

.returns(void.class)

.addModifiers(Modifier.PUBLIC)

.addAnnotation(Override.class)

.addParameter(TypeName.get(typeElement.asType()), "target")

.addParameter(TypeName.OBJECT, "object")

.addParameter(TypeUtil.finderType, "finder");

//方法体

for (ViewField field : fields){

finderBuilder.addStatement("target.$N = ($T)(finder.findView(object, $L))",

field.getFieldName(), ClassName.get(field.getFieldType()),

field.getResId());

}

//方法2 void unbind(Object target);

MethodSpec.Builder unbindbuilder = MethodSpec.methodBuilder("unbind")

.returns(void.class)

.addModifiers(Modifier.PUBLIC)

.addAnnotation(Override.class)

.addParameter(TypeName.get(typeElement.asType()), "target");

// .addParameter(TypeName.OBJECT, "target")

//方法体

for (ViewField field : fields) {

unbindbuilder.addStatement("target.$N = null",

field.getFieldName());

}

//生成类

TypeSpec clazz = TypeSpec.classBuilder(typeElement.getSimpleName() + "$$IViewBinder")

.addModifiers(Modifier.PUBLIC)

.addSuperinterface(ParameterizedTypeName.get(

TypeUtil.binderType,

TypeName.get(typeElement.asType())))

.addMethod(finderBuilder.build())

.addMethod(unbindbuilder.build())

.build();

String packageName = elementUtils.getPackageOf(typeElement).getQualifiedName().toString();

return JavaFile.builder(packageName, clazz).

build();

}

}

```

`ViewField.java`

```java

package com.freddon.android.app.nsannotation_compiler;

import com.freddon.android.app.nsannotations.ViewById;

import javax.lang.model.element.Element;

import javax.lang.model.element.ElementKind;

import javax.lang.model.element.Name;

import javax.lang.model.type.TypeMirror;

/**

* Created by fred on 2018/2/27.

*/

public class ViewField {

private TypeMirror fieldType;

private Name fieldName;

private int resId;

public ViewField(Element element) {

if (element.getKind() != ElementKind.FIELD) {

throw new IllegalArgumentException("Only fields can be annotated");

}

ViewById annotation= element.getAnnotation(ViewById.class);

resId=annotation.value();

fieldName=element.getSimpleName();

fieldType=element.asType();

}

public TypeMirror getFieldType() {

return fieldType;

}

public Name getFieldName() {

return fieldName;

}

public int getResId() {

return resId;

}

}

```

### 1.4 nsannotations-api模块

android-library模块,

`build.gradle`

```

apply plugin: 'com.android.library'

android {

compileSdkVersion 26

defaultConfig {

minSdkVersion 14

targetSdkVersion 26

versionCode 1

versionName "1.0"

testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"

}

buildTypes {

release {

minifyEnabled false

proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'

}

}

}

dependencies {

implementation fileTree(dir: 'libs', include: ['*.jar'])

implementation 'com.android.support:appcompat-v7:26.1.0'

}

```

`IFinder.java`

```

package com.freddon.android.app.nsannotation_api;

import android.view.View;

/**

* Created by fred on 2018/3/3.

*/

public interface IFinder {

View findView(Object object, int resId);

}

```

`Finder.java`

```

package com.freddon.android.app.nsannotation_api;

import android.app.Dialog;

import android.view.View;

import android.view.Window;

/**

* Created by fred on 2018/3/3.

*/

final class Finder implements IFinder {

@Override

public View findView(Object object, int resId) {

if (object instanceof View){

return ((View)object).findViewById(resId);

}

if (object instanceof Dialog){

return ((Dialog)object).findViewById(resId);

}

if (object instanceof Window){

return ((Window)object).findViewById(resId);

}

return null;

}

private View findView(Window object, int resId){

return object.findViewById(resId);

}

private View findView(Dialog object, int resId){

return object.findViewById(resId);

}

private View findView(View object, int resId){

return object.findViewById(resId);

}

}

```

`IViewBinder.java`

```

package com.freddon.android.app.nsannotation_api;

/**

* Created by fred on 2018/3/3.

*/

public interface IViewBinder {

void bindView(T target, Object object, IFinder finder) ;

void unbind(T target);

}

```

最终向外公开的api类

`FNViewBinder.java`

```

package com.freddon.android.app.nsannotation_api;

import java.util.Map;

import java.util.TreeMap;

/**

* Created by fred on 2018/3/3.

*/

public class FNViewBinder {

private static Finder Finder=new Finder();

static Map caches=new TreeMap<>();

public static void bind(Object target){

bind(target,target);

}

public static void bind(Object target,Object object){

bind(target,object,Finder);

}

private static void bind(Object target,Object object,Finder finder){

//获取注解所在的类

final String annotatedClassName = target.getClass().getName();

//获取生成的类

//先从缓存中获取

IViewBinder binder = caches.get(annotatedClassName);

if (binder==null){

try {

Class> clazz = Class.forName(annotatedClassName + "$$IViewBinder");

binder= (IViewBinder) clazz.newInstance();

caches.put(annotatedClassName,binder);

} catch (ClassNotFoundException e) {

e.printStackTrace();

} catch (IllegalAccessException e) {

e.printStackTrace();

} catch (InstantiationException e) {

e.printStackTrace();

}

}

if (binder!=null){

binder.bindView(target,object,finder);

}

}

public static void unBind(Object host) {

final String className = host.getClass().getName();

IViewBinder binder = caches.get(className);

if (binder != null) {

binder.unbind(host);

}

caches.remove(className);

}

}

```

### 1.5 app主项目

`build.gradle`

```

apply plugin: 'com.android.application'

apply plugin: 'kotlin-android'

apply plugin: 'kotlin-android-extensions'

apply plugin: 'kotlin-kapt'

android {

compileSdkVersion 26

defaultConfig {

applicationId "com.freddon.android.app.fnannotation"

minSdkVersion 14

targetSdkVersion 26

versionCode 1

versionName "1.0"

testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"

}

buildTypes {

release {

minifyEnabled false

proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'

}

}

}

dependencies {

implementation project(":nsannotation-api")

provided project(':nsannotations')

kapt project(':nsannotation-compiler')

implementation fileTree(include: ['*.jar'], dir: 'libs')

implementation "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"

implementation 'com.android.support:appcompat-v7:26.1.0'

implementation 'com.android.support.constraint:constraint-layout:1.0.2'

implementation 'com.android.support:design:26.1.0'

testImplementation 'junit:junit:4.12'

androidTestImplementation 'com.android.support.test:runner:1.0.1'

androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'

}

```

由于我引入了kotlin框架,所以依赖的地方`annotationProcessor`应改写为`kapt`

`MainActivity.java`

```

package com.freddon.android.app.fnannotation;

import android.support.v7.app.AppCompatActivity;

import android.os.Bundle;

import android.widget.TextView;

import com.freddon.android.app.nsannotation_api.FNViewBinder;

import com.freddon.android.app.nsannotations.ViewById;

public class MainActivity extends AppCompatActivity {

@ViewById(R.id.textView)

TextView textView;

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

FNViewBinder.bind(this);

textView.setText("Hello Annotation");

}

}

```

clean build项目后,将会在`app/build/generated/source/kapt/debug/com/freddon/android/app/fnannotation`下生成一个`MainActivity$$IViewBinder.java`文件。

同理,Listeners等注解也可以按照如此方式处理,减少每次手写代码。

完~

demo地址:[https://github.com/FRED5DON/AndroidTrials/tree/master/FnAnnotation](https://github.com/FRED5DON/AndroidTrials/tree/master/FnAnnotation)

评论(0)

先登录,才能发评论哦~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值