dagger2结合apt自动生成注入代码搭建mvp

来源:互联网 发布:海森伯格矩阵的用途 编辑:程序博客网 时间:2024/06/14 03:58

大家使用dagger2时候,通常要写一些注入代码,就算是再base类里面些, 当有新添加还有做修改。。其实倒也不麻烦,但是自动生成注入还是蛮爽的,像spring那样。。

本文分三部分来说吧。

  1. 第一部分是dagger2简单应用用一个mvp架构来做例子
  2. 第二部分是apt生成代码
  3. 第三部分是apt自动生成代码 再为dagger2提供注入。
    结合第第一个和第二个来看。 dagger需要我们手写Component,和初始化代码。 新建activity还是有点麻烦。用上apt只要一个注解,就会自动生成Component类 和初始化代码,使用dagger真正更简单。

首先时创建一个java工程,跟第二部分apt生成代码一样。创建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<>();    }    //扫描到注解会执行这里    @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的状况

demo地址https://github.com/836154942/dagger2_mvpDemo