大家使用dagger2時候加酵,通常要寫一些注入代碼拳喻,就算是再base類里面些, 當有新添加還有做修改猪腕。冗澈。其實倒也不麻煩,但是自動生成注入還是蠻爽的陋葡,像spring那樣亚亲。。
本文分三部分來說吧腐缤。
- 第一部分是dagger2簡單應用用一個mvp架構來做例子
- 第二部分是apt生成代碼
- 第三部分是apt自動生成代碼 再為dagger2提供注入捌归。
結合第第一個和第二個來看。 dagger需要我們手寫Component岭粤,和初始化代碼惜索。 新建activity還是有點麻煩。用上apt只要一個注解绍在,就會自動生成Component類 和初始化代碼门扇,使用dagger真正更簡單雹有。
首先時創(chuàng)建一個java工程,跟第二部分apt生成代碼一樣臼寄。創(chuàng)建2個注解霸奕,這里分別時用來標注activity和fragment的。
@Retention(RetentionPolicy.CLASS)
@Target(ElementType.TYPE)
public @interface ActivityInject {
}
@Retention(RetentionPolicy.CLASS)
@Target(ElementType.TYPE)
public @interface FragmentInject {
}
然后還是新建一個java工程吉拳,用來做注解處理器和其他類
總共2個核心類质帅。一個時注解處理器,一個輔助文件生成的留攒。
首先看注解處理器煤惩、
package com.spc;
import com.google.auto.service.AutoService;
import java.lang.annotation.Annotation;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Filer;
import javax.annotation.processing.Messager;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.Processor;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.TypeElement;
import javax.lang.model.util.Elements;
import javax.tools.Diagnostic;
/**
* Created by spc on 17/6/6.
*/
@AutoService(Processor.class)
public class ActivityInjectProcesser extends AbstractProcessor {
private Filer mFiler; //文件相關的輔助類
private Elements mElementUtils; //元素相關的輔助類 許多元素
private Messager mMessager; //日志相關的輔助類
private Map<String, AnnotatedClass> mAnnotatedClassMap;
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
mFiler = processingEnv.getFiler();
mElementUtils = processingEnv.getElementUtils();
mMessager = processingEnv.getMessager();
mAnnotatedClassMap = new TreeMap<>();
}
//掃描到注解會執(zhí)行這里
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
mAnnotatedClassMap.clear();
try {
processActivityCheck(roundEnv);
processFragmentCheck(roundEnv);
} catch (Exception e) {
e.printStackTrace();
error(e.getMessage());
}
for (AnnotatedClass annotatedClass : mAnnotatedClassMap.values()) {
try {
Class fgAnType = Class.forName(TypeUtil.ANNOTATION_FRAGMENT_PATH);
Class acAnType = Class.forName(TypeUtil.ANNOTATION_PATH);
if (annotatedClass.getmTypeElement().getAnnotation(fgAnType) != null) {
annotatedClass.generateFragmentDaggerFile().writeTo(mFiler);
annotatedClass.generateFragmentFile().writeTo(mFiler);
} else if (annotatedClass.getmTypeElement().getAnnotation(acAnType) != null){
annotatedClass.generateActivityDaggerFile().writeTo(mFiler);
annotatedClass.generateActivityFile().writeTo(mFiler);
}
} catch (Exception e) {
error("Generate file failed, reason: %s", e.getMessage());
}
}
return true;
}
private void processActivityCheck(RoundEnvironment roundEnv) throws IllegalArgumentException, ClassNotFoundException {
//check ruleslass forName(String className
for (Element element : roundEnv.getElementsAnnotatedWith((Class<? extends Annotation>) Class.forName(TypeUtil.ANNOTATION_PATH))) {
if (element.getKind() == ElementKind.CLASS) {
getAnnotatedClass(element);
} else
error("ActivityInject only can use in ElementKind.CLASS");
}
}
private void processFragmentCheck(RoundEnvironment roundEnv) throws IllegalArgumentException, ClassNotFoundException {
//check ruleslass forName(String className
for (Element element : roundEnv.getElementsAnnotatedWith((Class<? extends Annotation>) Class.forName(TypeUtil.ANNOTATION_FRAGMENT_PATH))) {
if (element.getKind() == ElementKind.CLASS) {
getAnnotatedClass(element);
} else
error("FragmentInject only can use in ElementKind.CLASS");
}
}
private AnnotatedClass getAnnotatedClass(Element element) {
// tipe . can not use chines so ....
// get TypeElement element is class's --->class TypeElement typeElement = (TypeElement) element
// get TypeElement element is method's ---> TypeElement typeElement = (TypeElement) element.getEnclosingElement();
TypeElement typeElement = (TypeElement) element;
String fullName = typeElement.getQualifiedName().toString();
AnnotatedClass annotatedClass = mAnnotatedClassMap.get(fullName);
if (annotatedClass == null) {
annotatedClass = new AnnotatedClass(typeElement, mElementUtils, mMessager);
mAnnotatedClassMap.put(fullName, annotatedClass);
}
return annotatedClass;
}
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latestSupported();
}
//這個方法,返回要處理什么注解 的一個set集合
@Override
public Set<String> getSupportedAnnotationTypes() {
Set<String> types = new LinkedHashSet<>();
types.add(TypeUtil.ANNOTATION_PATH);
types.add(TypeUtil.ANNOTATION_FRAGMENT_PATH);
return types;
}
private void error(String msg, Object... args) {
mMessager.printMessage(Diagnostic.Kind.ERROR, String.format(msg, args));
}
private void log(String msg, Object... args) {
mMessager.printMessage(Diagnostic.Kind.NOTE, String.format(msg, args));
}
}
然后編譯時候生成類的 代碼
package com.spc;
import com.spc.TypeUtil;
import com.squareup.javapoet.AnnotationSpec;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.TypeName;
import com.squareup.javapoet.TypeSpec;
import javax.annotation.processing.Messager;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.lang.model.util.Elements;
import javax.tools.Diagnostic;
/**
* Created by spc on 17/6/6.
*/
public class AnnotatedClass {
private TypeElement mTypeElement;//activity //fragmemt
private Elements mElements;
private Messager mMessager;//日志打印
public AnnotatedClass(TypeElement typeElement, Elements elements, Messager messager) {
mTypeElement = typeElement;
mElements = elements;
this.mMessager = messager;
}
public JavaFile generateActivityFile() {
// build inject method
MethodSpec.Builder injectMethod = MethodSpec.methodBuilder(TypeUtil.METHOD_NAME)
.addModifiers(Modifier.PUBLIC)
.addParameter(TypeName.get(mTypeElement.asType()), "activity", Modifier.FINAL);
injectMethod.addStatement(TypeUtil.MAIN_ACTIVITY_PATH + ".$L.builder()\n.$L($L)\n" +
".$L(new $L(activity))\n" +
".build()\n.$L(activity)",
"Dagger" + mTypeElement.getSimpleName() + "$$Component",
TypeUtil.APP_Component_Name,
TypeUtil.APPCOMPONENT_PROVIDE_PATH,
TypeUtil.APP_ActivityModule_Name,
TypeUtil.ACTIVITY_MODULE_PATH,
TypeUtil.METHOD_NAME);
//generaClass
TypeSpec injectClass = TypeSpec.classBuilder(mTypeElement.getSimpleName() + "$$InjectActivity")
.addModifiers(Modifier.PUBLIC)
// .addSuperinterface(ParameterizedTypeName.get(TypeUtil.INJET_NAME, TypeName.get(mTypeElement.asType())))
.addMethod(injectMethod.build())
.build();
String packgeName = mElements.getPackageOf(mTypeElement).getQualifiedName().toString();
return JavaFile.builder(packgeName, injectClass).build();
}
public JavaFile generateActivityDaggerFile() {
MethodSpec.Builder injectMethod = MethodSpec.methodBuilder(TypeUtil.METHOD_NAME)
.addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT)
.addParameter(TypeName.get(mTypeElement.asType()), "activity");
//generaClass
TypeSpec injectClass = TypeSpec.interfaceBuilder(mTypeElement.getSimpleName() + "$$Component")
.addModifiers(Modifier.PUBLIC)
.addAnnotation(TypeUtil.ACTIVITY_SCOPE_CLASSNAME)
.addAnnotation(AnnotationSpec.builder(ClassName.get("dagger", "Component"))
.addMember("dependencies", "$L", TypeUtil.APP_COMPONENT_PATH + ".class")
.addMember("modules", "$L", TypeUtil.ACTIVITY_MODULE_PATH + ".class")
.build())
.addMethod(injectMethod.build())
.build();
gradleLog("---->dagger " + mTypeElement.getSimpleName() + " Component buuild success");
String packgeName = mElements.getPackageOf(mTypeElement).getQualifiedName().toString();
return JavaFile.builder(packgeName, injectClass).build();
}
public JavaFile generateFragmentFile() {
// build inject method
MethodSpec.Builder injectMethod = MethodSpec.methodBuilder(TypeUtil.METHOD_NAME)
.addModifiers(Modifier.PUBLIC)
.addParameter(TypeName.get(mTypeElement.asType()), "fragment", Modifier.FINAL);
injectMethod.addStatement(TypeUtil.MAIN_FRAGMENT_PATH + ".$L.builder()\n.$L($L)\n" +
".$L(new $L(fragment))\n" +
".build()\n.$L(fragment)",
"Dagger" + mTypeElement.getSimpleName() + "$$Component",
TypeUtil.APP_Component_Name,
TypeUtil.APPCOMPONENT_PROVIDE_PATH,
TypeUtil.APP_FragmentModule_Name,
TypeUtil.FRAGMENT_MODULE_PATH,
TypeUtil.METHOD_NAME);
//generaClass
TypeSpec injectClass = TypeSpec.classBuilder(mTypeElement.getSimpleName() + "$$InjectFragment")
.addModifiers(Modifier.PUBLIC)
.addMethod(injectMethod.build())
.build();
String packgeName = mElements.getPackageOf(mTypeElement).getQualifiedName().toString();
return JavaFile.builder(packgeName, injectClass).build();
}
public JavaFile generateFragmentDaggerFile() {
MethodSpec.Builder injectMethod = MethodSpec.methodBuilder(TypeUtil.METHOD_NAME)
.addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT)
.addParameter(TypeName.get(mTypeElement.asType()), "fragment");
//generaClass
TypeSpec injectClass = TypeSpec.interfaceBuilder(mTypeElement.getSimpleName() + "$$Component")
.addModifiers(Modifier.PUBLIC)
.addAnnotation(TypeUtil.FRAGMENT_SCOPE_CLASSNAME)
.addAnnotation(AnnotationSpec.builder(ClassName.get("dagger", "Component"))
.addMember("dependencies", "$L", TypeUtil.APP_COMPONENT_PATH + ".class")
.addMember("modules", "$L", TypeUtil.FRAGMENT_MODULE_PATH + ".class")
.build())
.addMethod(injectMethod.build())
.build();
gradleLog("---->dagger " + mTypeElement.getSimpleName() + " Component buuild success");
String packgeName = mElements.getPackageOf(mTypeElement).getQualifiedName().toString();
return JavaFile.builder(packgeName, injectClass).build();
}
private void gradleLog(String msg, Object... args) {
mMessager.printMessage(Diagnostic.Kind.NOTE, String.format(msg, args));
}
public TypeElement getmTypeElement() {
return mTypeElement;
}
}
還有一個常量文件
public class TypeUtil {
/**
* ?????
*/
public static final String MAIN_PROJECT_PACKAGE_NAME = "com.spc.spc.myapplication";
/**
* activity ????
*/
public static final String MAIN_ACTIVITY_PATH = MAIN_PROJECT_PACKAGE_NAME + ".ui.activity";
public static final String MAIN_FRAGMENT_PATH = MAIN_PROJECT_PACKAGE_NAME + ".ui.fragment";
/**
* app???component ???? ???? ??
*/
public static final String APP_Component_Name = "appcomponent";
public static final String APP_Component_Name_Capital = "Appcomponent";
public static final String APP_COMPONENT_PATH = MAIN_PROJECT_PACKAGE_NAME + ".di.component." + APP_Component_Name_Capital;
/**
* activity?Module ???? ????
*/
public static final String APP_ActivityModule_Name = "activityModule";
public static final String APP_ActivityModule_Name_Capital = "ActivityModule";
public static final String ACTIVITY_MODULE_PATH = MAIN_PROJECT_PACKAGE_NAME + ".di.module." + APP_ActivityModule_Name_Capital;
/**
* fragment module
*/
public static final String APP_FragmentModule_Name = "fragmentModule";
public static final String APP_FragmentModule_Name_Capital = "FragmentModule";
public static final String FRAGMENT_MODULE_PATH = MAIN_PROJECT_PACKAGE_NAME + ".di.module." + APP_FragmentModule_Name_Capital;
/**
* ??application????App??? Component???
*/
public static final String APPCOMPONENT_PROVIDE_PATH = MAIN_PROJECT_PACKAGE_NAME + ".base.MyApplication.getInst().getAppComponent()";
/**
* Scope ???
*/
public static final ClassName ACTIVITY_SCOPE_CLASSNAME = ClassName.get(MAIN_PROJECT_PACKAGE_NAME + ".di.scope", "ActivityScope");
public static final ClassName FRAGMENT_SCOPE_CLASSNAME = ClassName.get(MAIN_PROJECT_PACKAGE_NAME + ".di.scope", "FragmentScope");
/**
* ???????
*/
public static final String METHOD_NAME = "inject";
public static final String ANNOTATION_PATH = "com.spc.ActivityInject";
public static final String ANNOTATION_FRAGMENT_PATH = "com.spc.FragmentInject";
// public static final ClassName INJET_NAME = ClassName.get("com.example.injectlib", "Inject");
}
還差最后一個就是注解的初始化代碼
package com.spc.spc.myapplication.di.aptinject;
import android.support.v4.util.ArrayMap;
import android.support.v7.app.AppCompatActivity;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class InjectActivity {
private static final ArrayMap<String, Object> injectMap = new ArrayMap<>();
public static void inject(AppCompatActivity activity) {
String className = activity.getClass().getName();
try {
Object inject = injectMap.get(className);
if (inject == null) {
Class<?> aClass = Class.forName(className + "$$InjectActivity");
inject = aClass.newInstance();
injectMap.put(className, inject);
}
Method m1 = inject.getClass().getDeclaredMethod("inject",activity.getClass());
m1.invoke(inject,activity);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
這樣在activity上標注一個@ActivityInject 就可以完成dagger的初始化了炼邀。
在baseactivity里面初始化就好
看使用
public abstract class BaseMVPActivity<P extends BasePresenter> extends BaseActivity implements BaseMvpViewInterface {
@Inject
protected P mvpPresenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
InjectActivity.inject(this);
mvpPresenter.attachView(this);
}
@Override
protected void onDestroy() {
super.onDestroy();
if (mvpPresenter != null) {
mvpPresenter.detachView();
}
}
}
@ActivityInject//apt注解注入魄揉。不需要手寫
public class MainActivity extends BaseMVPActivity<MainPresenter> implements MainacView {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
就這樣。dagger2不需要component和初始化拭宁,一個注解就完成了洛退。
fragment的 一樣。在注解處理器里面杰标,處理了fragment的狀況