android apt自動生成dagger2的注入代碼

大家使用dagger2時候加酵,通常要寫一些注入代碼拳喻,就算是再base類里面些, 當有新添加還有做修改猪腕。冗澈。其實倒也不麻煩,但是自動生成注入還是蠻爽的陋葡,像spring那樣亚亲。。

本文分三部分來說吧腐缤。

  1. 第一部分是dagger2簡單應用用一個mvp架構來做例子
  2. 第二部分是apt生成代碼
  3. 第三部分是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的狀況

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

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末兵怯,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子腔剂,更是在濱河造成了極大的恐慌媒区,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,968評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件掸犬,死亡現場離奇詭異袜漩,居然都是意外死亡,警方通過查閱死者的電腦和手機湾碎,發(fā)現死者居然都...
    沈念sama閱讀 88,601評論 2 382
  • 文/潘曉璐 我一進店門噪服,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人胜茧,你說我怎么就攤上這事粘优。” “怎么了呻顽?”我有些...
    開封第一講書人閱讀 153,220評論 0 344
  • 文/不壞的土叔 我叫張陵雹顺,是天一觀的道長。 經常有香客問我廊遍,道長嬉愧,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,416評論 1 279
  • 正文 為了忘掉前任喉前,我火速辦了婚禮没酣,結果婚禮上王财,老公的妹妹穿的比我還像新娘。我一直安慰自己裕便,他們只是感情好绒净,可當我...
    茶點故事閱讀 64,425評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著偿衰,像睡著了一般挂疆。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上下翎,一...
    開封第一講書人閱讀 49,144評論 1 285
  • 那天缤言,我揣著相機與錄音,去河邊找鬼视事。 笑死胆萧,一個胖子當著我的面吹牛,可吹牛的內容都是我干的俐东。 我是一名探鬼主播鸳碧,決...
    沈念sama閱讀 38,432評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼犬性!你這毒婦竟也來了?” 一聲冷哼從身側響起腾仅,我...
    開封第一講書人閱讀 37,088評論 0 261
  • 序言:老撾萬榮一對情侶失蹤乒裆,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后推励,有當地人在樹林里發(fā)現了一具尸體鹤耍,經...
    沈念sama閱讀 43,586評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,028評論 2 325
  • 正文 我和宋清朗相戀三年验辞,在試婚紗的時候發(fā)現自己被綠了稿黄。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,137評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡跌造,死狀恐怖杆怕,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情壳贪,我是刑警寧澤陵珍,帶...
    沈念sama閱讀 33,783評論 4 324
  • 正文 年R本政府宣布,位于F島的核電站违施,受9級特大地震影響互纯,放射性物質發(fā)生泄漏。R本人自食惡果不足惜磕蒲,卻給世界環(huán)境...
    茶點故事閱讀 39,343評論 3 307
  • 文/蒙蒙 一留潦、第九天 我趴在偏房一處隱蔽的房頂上張望只盹。 院中可真熱鬧,春花似錦兔院、人聲如沸殖卑。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽懦鼠。三九已至,卻和暖如春屹堰,著一層夾襖步出監(jiān)牢的瞬間肛冶,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評論 1 262
  • 我被黑心中介騙來泰國打工扯键, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留睦袖,地道東北人。 一個月前我還...
    沈念sama閱讀 45,595評論 2 355
  • 正文 我出身青樓荣刑,卻偏偏與公主長得像馅笙,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子厉亏,可洞房花燭夜當晚...
    茶點故事閱讀 42,901評論 2 345

推薦閱讀更多精彩內容