热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

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

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

大家使用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 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 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) 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) 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 getSupportedAnnotationTypes() {
        Set 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 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


推荐阅读
  • Java程序设计第4周学习总结及注释应用的开发笔记
    本文由编程笔记#小编为大家整理,主要介绍了201521123087《Java程序设计》第4周学习总结相关的知识,包括注释的应用和使用类的注释与方法的注释进行注释的方法,并在Eclipse中查看。摘要内容大约为150字,提供了一定的参考价值。 ... [详细]
  • HashMap的相关问题及其底层数据结构和操作流程
    本文介绍了关于HashMap的相关问题,包括其底层数据结构、JDK1.7和JDK1.8的差异、红黑树的使用、扩容和树化的条件、退化为链表的情况、索引的计算方法、hashcode和hash()方法的作用、数组容量的选择、Put方法的流程以及并发问题下的操作。文章还提到了扩容死链和数据错乱的问题,并探讨了key的设计要求。对于对Java面试中的HashMap问题感兴趣的读者,本文将为您提供一些有用的技术和经验。 ... [详细]
  • Android中高级面试必知必会,积累总结
    本文介绍了Android中高级面试的必知必会内容,并总结了相关经验。文章指出,如今的Android市场对开发人员的要求更高,需要更专业的人才。同时,文章还给出了针对Android岗位的职责和要求,并提供了简历突出的建议。 ... [详细]
  • 本文介绍了OC学习笔记中的@property和@synthesize,包括属性的定义和合成的使用方法。通过示例代码详细讲解了@property和@synthesize的作用和用法。 ... [详细]
  • 欢乐的票圈重构之旅——RecyclerView的头尾布局增加
    项目重构的Git地址:https:github.comrazerdpFriendCircletreemain-dev项目同步更新的文集:http:www.jianshu.comno ... [详细]
  • Android系统源码分析Zygote和SystemServer启动过程详解
    本文详细解析了Android系统源码中Zygote和SystemServer的启动过程。首先介绍了系统framework层启动的内容,帮助理解四大组件的启动和管理过程。接着介绍了AMS、PMS等系统服务的作用和调用方式。然后详细分析了Zygote的启动过程,解释了Zygote在Android启动过程中的决定作用。最后通过时序图展示了整个过程。 ... [详细]
  • Week04面向对象设计与继承学习总结及作业要求
    本文总结了Week04面向对象设计与继承的重要知识点,包括对象、类、封装性、静态属性、静态方法、重载、继承和多态等。同时,还介绍了私有构造函数在类外部无法被调用、static不能访问非静态属性以及该类实例可以共享类里的static属性等内容。此外,还提到了作业要求,包括讲述一个在网上商城购物或在班级博客进行学习的故事,并使用Markdown的加粗标记和语句块标记标注关键名词和动词。最后,还提到了参考资料中关于UML类图如何绘制的范例。 ... [详细]
  • GreenDAO快速入门
    前言之前在自己做项目的时候,用到了GreenDAO数据库,其实对于数据库辅助工具库从OrmLite,到litePal再到GreenDAO,总是在不停的切换,但是没有真正去了解他们的 ... [详细]
  • 本文介绍了MVP架构模式及其在国庆技术博客中的应用。MVP架构模式是一种演变自MVC架构的新模式,其中View和Model之间的通信通过Presenter进行。相比MVC架构,MVP架构将交互逻辑放在Presenter内部,而View直接从Model中读取数据而不是通过Controller。本文还探讨了MVP架构在国庆技术博客中的具体应用。 ... [详细]
  • 本文整理了Java面试中常见的问题及相关概念的解析,包括HashMap中为什么重写equals还要重写hashcode、map的分类和常见情况、final关键字的用法、Synchronized和lock的区别、volatile的介绍、Syncronized锁的作用、构造函数和构造函数重载的概念、方法覆盖和方法重载的区别、反射获取和设置对象私有字段的值的方法、通过反射创建对象的方式以及内部类的详解。 ... [详细]
  • 本文介绍了在实现了System.Collections.Generic.IDictionary接口的泛型字典类中如何使用foreach循环来枚举字典中的键值对。同时还讨论了非泛型字典类和泛型字典类在foreach循环中使用的不同类型,以及使用KeyValuePair类型在foreach循环中枚举泛型字典类的优势。阅读本文可以帮助您更好地理解泛型字典类的使用和性能优化。 ... [详细]
  • JDK源码学习之HashTable(附带面试题)的学习笔记
    本文介绍了JDK源码学习之HashTable(附带面试题)的学习笔记,包括HashTable的定义、数据类型、与HashMap的关系和区别。文章提供了干货,并附带了其他相关主题的学习笔记。 ... [详细]
  • 如何查询zone下的表的信息
    本文介绍了如何通过TcaplusDB知识库查询zone下的表的信息。包括请求地址、GET请求参数说明、返回参数说明等内容。通过curl方法发起请求,并提供了请求示例。 ... [详细]
  • 本文介绍了OpenStack的逻辑概念以及其构成简介,包括了软件开源项目、基础设施资源管理平台、三大核心组件等内容。同时还介绍了Horizon(UI模块)等相关信息。 ... [详细]
  • Whatsthedifferencebetweento_aandto_ary?to_a和to_ary有什么区别? ... [详细]
author-avatar
sdr700724
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有