上一節我們已經搭建好了環境,和一個簡易的demo,我們在此基礎上再寫一個簡單的注解demo
先看一看項目結構
Paste_Image.png
1,建一個注解類取名叫InjectView
@Retention(RetentionPolicy.CLASS)
public @interface InjectView {
int value();
}
2,在process Java moduel創建注解處理器ViewInjectorProcessor
在這里Google給我提供了一個依賴包,專門為了減少建立META-INF.services的步驟
在Java moduel的build中添加依賴:
compile 'com.google.auto.service:auto-service:1.0-rc2'
這個類就這樣寫:
@AutoService(Processor.class)
public class ViewInjectorProcessor extends AbstractProcessor {
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latestSupported();
}
@Override
public Set<String> getSupportedAnnotationTypes() {
return (Collections.singleton(InjectView.class.getCanonicalName()));
}
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
return false;
}
}
3,建立注解處理接口類AnnotationHandler
public interface AnnotationHandler {
/**
* @param processingEnv
*/
void attachProcessingEnv(ProcessingEnvironment processingEnv);
/**
* @param roundEnv
* @return
*/
Map<String, List<VariableElement>> handleAnnotation(RoundEnvironment roundEnv);
// Map<String, InjectorInfo> handleAnnotation(RoundEnvironment roundEnv);
}
ViewInjectHandler實現里面的方法
public class ViewInjectHandler implements com.example.annote.handler.AnnotationHandler {
ProcessingEnvironment mProcessingEnv;
private Map<String, InjectorInfo> mInfoMap = new HashMap<>();
@Override
public void attachProcessingEnv(ProcessingEnvironment processingEnv) {
mProcessingEnv = processingEnv;
}
@Override
public Map<String, List<VariableElement>> handleAnnotation(RoundEnvironment roundEnv) {
// public Map<String, InjectorInfo> handleAnnotation(RoundEnvironment roundEnv) {
Map<String, List<VariableElement>> annotationMap = new HashMap<String, List<VariableElement>>();
// 獲取使用ViewInjector注解的所有元素
Set<? extends Element> elementSet = roundEnv.getElementsAnnotatedWith(InjectView.class);
for (Element element : elementSet) {
// 注解的字段
VariableElement varElement = (VariableElement) element;
// 類型的完整路徑名,比如某個Activity的完整路徑
String className = AnnotationUtil.getParentClassName(mProcessingEnv,varElement);
// 獲取這個類型的所有注解,例如某個Activity中的所有View的注解對象
List<VariableElement> cacheElements = annotationMap.get(className);
if (cacheElements == null) {
cacheElements = new LinkedList<VariableElement>();
}
// 將元素添加到該類型對應的字段列表中
cacheElements.add(varElement);
// 以類的路徑為key,字段列表為value,存入map.
// 這里是將所在字段按所屬的類型進行分類
annotationMap.put(className, cacheElements);
}
return annotationMap;
}
}
4,建立接口AdapterWriter
public interface AdapterWriter {
void generate(Map<String, List<VariableElement>> typeMap);
// void generate(Map<String, InjectorInfo> mInfoMap);
}
AbsWriter實現AdapterWriter
public abstract class AbsWriter implements AdapterWriter {
ProcessingEnvironment mProcessingEnv;
Filer mFiler;
public AbsWriter(ProcessingEnvironment processingEnv) {
mProcessingEnv = processingEnv;
mFiler = processingEnv.getFiler();
}
@Override
public void generate(Map<String, List<VariableElement>> typeMap) {
// public void generate(Map<String, InjectorInfo> infoMap) {
Iterator<Map.Entry<String, List<VariableElement>>> iterator = typeMap.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<String, List<VariableElement>> entry = iterator.next();
List<VariableElement> cacheElements = entry.getValue();
if (cacheElements == null || cacheElements.size() == 0) {
continue;
}
// 取第一個元素來構造注入信息
InjectorInfo info = createInjectorInfo(cacheElements.get(0));
Writer writer = null;
JavaFileObject javaFileObject;
try {
mProcessingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, info.getClassFullPath());
javaFileObject = this.mFiler.createSourceFile(info.getClassFullPath());
writer = javaFileObject.openWriter();
// 寫入package, import, class以及findViews函數等代碼段
generateImport(writer, info);
// 寫入該類中的所有字段到findViews方法中
for (VariableElement variableElement : entry.getValue()) {
writeField(writer, variableElement, info);
}
// 寫入findViews函數的大括號以及類的大括號
writeEnd(writer);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (writer != null) {
writer.flush();
}
IOUtil.closeQuitly(writer);
} catch (IOException e) {
e.printStackTrace();
}
}
}
// writeInfo(infoMap);
}
/**
* @param element
* @return
*/
protected InjectorInfo createInjectorInfo(VariableElement element) {
TypeElement typeElement = (TypeElement) element.getEnclosingElement();
String packageName = AnnotationUtil.getPackageName(mProcessingEnv, typeElement);
String className = typeElement.getSimpleName().toString();
return new InjectorInfo(packageName, className);
}
protected abstract void generateImport(Writer writer, InjectorInfo info)
throws IOException;
protected abstract void writeField(Writer writer, VariableElement element, InjectorInfo info)
throws IOException;
protected abstract void writeEnd(Writer writer) throws IOException;
}
DefaultJavaFileWriter繼承AbsWriter
public class DefaultJavaFileWriter extends AbsWriter {
public DefaultJavaFileWriter(ProcessingEnvironment processingEnv) {
super(processingEnv);
}
@Override
protected void generateImport(Writer writer, InjectorInfo info) throws IOException {
writer.write("package " + info.packageName + " ;");
writer.write("\n\n");
writer.write("import com.example.butterknife.InjectAdapter ;");
writer.write("\n");
writer.write("import com.example.butterknife.ViewFinder;");
writer.write("\n\n\n");
writer.write("/* This class is generated by Simple ViewInjector, please don't modify! */ ");
writer.write("\n");
writer.write("public class " + info.newClassName
+ " implements InjectAdapter<" + info.classlName + "> { ");
writer.write("\n");
writer.write("\n");
// 查找方法
writer.write(" public void injects(" + info.classlName
+ " target) { ");
writer.write("\n");
}
@Override
protected void writeField(Writer writer, VariableElement element, InjectorInfo info) throws IOException {
InjectView injector = element.getAnnotation(InjectView.class);
// writer.write(" ViewFinder.setContentView(target," + injector.value() + " ) ; \n");
String fieldName = element.getSimpleName().toString();
writer.write(" target." + fieldName + " = ViewFinder.findViewById(target, "
+ injector.value() + " ) ; ");
writer.write("\n");
}
@Override
protected void writeEnd(Writer writer) throws IOException {
writer.write(" }");
writer.write("\n\n");
writer.write(" } ");
}
@Override
public String generateJavaCode(InjectorInfo info) {
StringBuilder builder = new StringBuilder();
builder.append("http:// Generated code from HyViewInjector. Do not modify!\n");
builder.append("package ").append(info.packageName).append(";\n\n");
builder.append("import android.view.View;\n");
builder.append("import com.example.butterknife.ViewFinder;\n");
builder.append("import com.example.butterknife.InjectAdapter ;\n");
builder.append('\n');
builder.append("public class ").append(info.newClassName);
builder.append(" implements InjectAdapter<T>");
builder.append(" {\n");
generateInjectMethod(builder,info);
builder.append("\n");
builder.append("}\n");
return builder.toString();
}
}
5,ViewInjectorProcessor完整的代碼如下:
@AutoService(Processor.class)
public class ViewInjectorProcessor extends AbstractProcessor {
/**
* 所有注解處理器的列表
*/
List<AnnotationHandler> mHandlers = new LinkedList<AnnotationHandler>();
/**
* 類型與字段的關聯表,用于在寫入Java文件時按類型來寫不同的文件和字段
*/
final Map<String, List<VariableElement>> map = new HashMap<String, List<VariableElement>>();
// final Map<String, InjectorInfo> mInfoMap = new HashMap<>();
/**
*
*/
AdapterWriter mWriter;
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latestSupported();
}
@Override
public Set<String> getSupportedAnnotationTypes() {
return (Collections.singleton(InjectView.class.getCanonicalName()));
}
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
registerHandlers();
this.mWriter = new DefaultJavaFileWriter(processingEnv);
}
private void registerHandlers() {
this.mHandlers.add(new ViewInjectHandler());
}
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
Iterator var4 = this.mHandlers.iterator();
while (var4.hasNext()) {
AnnotationHandler handler = (AnnotationHandler) var4.next();
// 關聯ProcessingEnvironment
handler.attachProcessingEnv(this.processingEnv);
// 解析注解相關的信息
map.putAll(handler.handleAnnotation(roundEnv));
}
// 將解析到的數據寫入到具體的類型中
this.mWriter.generate(map);
return true;
}
}
6,建立Android moduel 名字butterknife,如圖
Paste_Image.png
創建注冊activity或者fragment的視圖接口注冊器
public interface InjectAdapter<T> {
void injects(T target);
}
創建轉化控件的靜態方法類
public class ViewFinder {
public static <T extends View> T findViewById(Activity act, int id) {
return (T) act.findViewById(id);
}
/* public static void setContentView(Activity act, int layoutId) {
act.setContentView(layoutId);
}
*/
public static <T extends View> T findViewById(View rootView, int id) {
return (T) rootView.findViewById(id);
}
public static <T extends View> T findViewById(Fragment fragment, int id) {
return findViewById(fragment.getView(), id);
}
}
綁定視圖的實現類
public final class ButterView {
public static final String SUFFIX = "$ViewAdapter";
static Map<Class<?>, InjectAdapter<?>> sInjectCache = new HashMap();
public ButterView() {
}
public static void inject(Activity activity) {
InjectAdapter adapter = getViewAdapter(activity.getClass());
adapter.injects(activity);
}
public static void inject(Fragment fragment, View rootView) {
InjectAdapter adapter = getViewAdapter(fragment.getClass());
if(fuckTheFragment(fragment, rootView)) {
adapter.injects(fragment);
}
}
private static boolean fuckTheFragment(Fragment fragment, View rootView) {
try {
Class e;
for(e = fragment.getClass(); e != Object.class && !e.equals(Fragment.class); e = e.getSuperclass()) {
;
}
Field rootViewField = e.getDeclaredField("mView");
rootViewField.setAccessible(true);
rootViewField.set(fragment, rootView);
Log.e("", "### getView " + fragment.getView());
return true;
} catch (SecurityException var4) {
var4.printStackTrace();
} catch (NoSuchFieldException var5) {
var5.printStackTrace();
} catch (IllegalArgumentException var6) {
var6.printStackTrace();
} catch (IllegalAccessException var7) {
var7.printStackTrace();
}
return false;
}
public static void inject(View view) {
}
private static <T> InjectAdapter<T> getViewAdapter(Class<?> clazz) {
InjectAdapter adapter = (InjectAdapter)sInjectCache.get(clazz);
if(adapter == null) {
String adapterClassName = clazz.getName() + "$ViewAdapter";
try {
Class e = Class.forName(adapterClassName);
adapter = (InjectAdapter)e.newInstance();
sInjectCache.put(e, adapter);
} catch (ClassNotFoundException var4) {
var4.printStackTrace();
} catch (InstantiationException var5) {
var5.printStackTrace();
} catch (IllegalAccessException var6) {
var6.printStackTrace();
}
}
Log.e("", "### find adapter : " + adapter);
return (InjectAdapter)adapter;
}
}
同樣的需要clean Project ,rebuild,生成Java文件
在MainActivity中驗證:
public class MainActivity extends AppCompatActivity {
@InjectView(R.id.textView)
TextView mTextView;
@InjectView(R.id.textView1)
TextView mTextView1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// mTextView = (TextView) findViewById(R.id.textView);
ButterView.inject(this);
mTextView.setText("我是編譯時的注解");
mTextView1.setText("我是第二個注解");
// showMessage();
}
貼出utils和模型類里面的代碼
public final class AnnotationUtil {
/**
* 獲取包名
* @param processingEnv
* @param element
* @return
*/
public static String getPackageName(ProcessingEnvironment processingEnv, Element element) {
return processingEnv.getElementUtils().getPackageOf(element).getQualifiedName().toString();
}
/**
* 獲取某個字段的完整的路徑
*
* @param varElement
* @return
*/
public static String getParentClassName(ProcessingEnvironment processingEnv,VariableElement varElement) {
// 獲取該元素所在的類型,例如某個View是某個Activity的字段,這里就是獲取這個Activity的類型
TypeElement typeElement = (TypeElement) varElement.getEnclosingElement();
// 獲取typeElement的包名
String packageName = AnnotationUtil.getPackageName(processingEnv, typeElement);
// 類型的完整路徑名,比如某個Activity的完整路徑
return packageName + "." + typeElement.getSimpleName().toString();
}
}
public class InjectorInfo {
public static final String SUFFIX = "$ViewAdapter";
/**
* 被注解的類的包名
*/
public String packageName;
/**
* 被注解的類的類名
*/
public String classlName;
/**
* 要創建的InjectAdapter類的完整路徑,新類的名字為被注解的類名 + "$ViewAdapter", 與被注解的類在同一個包下
*/
public String newClassName;
private TypeElement typeElement;
private int layoutId;
private Map<Integer, ViewInfo> idViewMap = new HashMap<>();
public InjectorInfo(String packageName, String classlName) {
this.packageName = packageName;
newClassName=classlName+SUFFIX;
this.classlName = classlName;
}
public TypeElement getTypeElement() {
return typeElement;
}
public void setTypeElement(TypeElement typeElement) {
this.typeElement = typeElement;
}
public int getLayoutId() {
return layoutId;
}
public void setLayoutId(int layoutId) {
this.layoutId = layoutId;
}
public void putViewInfo(int id, ViewInfo viewInfo) {
idViewMap.put(id, viewInfo);
}
public Map<Integer, ViewInfo> getIdViewMap() {
return idViewMap;
}
public String getClassFullPath(){
return packageName+ "."+newClassName;
}
}
public class ViewInfo {
private int id;
private String type;
private String name;
public ViewInfo(int id, String type, String name) {
this.id = id;
this.type = type;
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
運行后如圖:
Paste_Image.png
這樣一個簡單的編譯時注解框架就打造好了。