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)
先登录,才能发评论哦~