Heim  >  Artikel  >  Java  >  Android (Java)-Code implementiert manuell eine detaillierte technische Erklärung zur Abhängigkeitsinjektion

Android (Java)-Code implementiert manuell eine detaillierte technische Erklärung zur Abhängigkeitsinjektion

php是最好的语言
php是最好的语言Original
2018-07-25 10:56:522232Durchsuche

Abhängigkeitsinjektion in der Android-Codegenerierungstechnologie (Java) Ich glaube, jeder hat in der täglichen Entwicklung von Drittanbietern wie Butterknife und Greendao gehört 🎜>Sofortige Generierung von Code während der Kompilierung, und dann können wir die durch die Kompilierung generierten Klassen verwenden, um unsere Entwicklung zu unterstützen und unsere Arbeitsbelastung zu reduzieren. Wie geht das? Ich habe noch nie gehört, dass Kompilierung auch Code generieren kann. Schauen wir uns diese magische Technologie an!

Prinzip der Codegenerierung zur Kompilierzeit

Zuerst wissen wir vielleicht mehr oder weniger über eine Technologie namens

JavaPoet. Beginnen wir damit Anhand des Quellcodes werden Sie feststellen, dass es anscheinend keine erweiterten Funktionen gibt. Insgesamt gibt es nur ein Dutzend Klassen, und die meisten von ihnen kapseln nur einige Klassen, stellen einige Schnittstellen zur einfachen Verwendung bereit und stellen auch eine Toolklasse für bereit Wir verwenden, wie im Bild gezeigt, nur die folgenden Klassen im Quellcode:

Zu diesem Zeitpunkt sind wir nicht verwirrt über den von dieser Bibliothek generierten Code uns? Android (Java)-Code implementiert manuell eine detaillierte technische Erklärung zur AbhängigkeitsinjektionUm genau zu sein, ist es das nicht. Was ist also seine Funktion? Es vervollständigt tatsächlich nur die Hälfte der von uns erwähnten Funktionen, nämlich
Codegenerierung, und es handelt sich um eine einfache und leicht verständliche Codegenerierung, da es gekapselt ist und im Grunde Antworten auf einige gängige Codegenerierungsanforderungen bietet. Methode, wodurch die Codekomplexität erheblich reduziert wird. Im Vergleich zu dem, was wir wollen, wird beim Kompilieren ein weniger generiert. Tatsächlich müssen wir einige Dinge während der Kompilierung tun. Schauen wir uns zunächst eine Klasse an.

Diese Klasse wird möglicherweise nicht normal verwendet . Um es einfach zu verstehen, können Sie es zunächst als abstrakten

Annotationsprozessor AbstractProcessor verstehen, der sich unten befindet und eine Klasse ist, die zum Scannen und Verarbeiten von Annotationen zur javax.annotation.processing.AbstractProcessor; Kompilierungszeit verwendet wird . Wenn wir Funktionen wie ButterKnife implementieren möchten, müssen wir zuerst unseren eigenen Annotationsprozessor definieren und ihn dann erben. Wie unten gezeigt, müssen wir eine abstrakte Methode namens „Process“ implementieren Diese Methode wird während der Kompilierung ausgeführt. Wie kann man dann den JVM dazu bringen, den Annotationsprozessor aufzurufen, den wir während der Kompilierung geschrieben haben? Eine schnelle Möglichkeit besteht darin, die Open-Source-Bibliothek Auto zu verwenden und sie dann mit der von ihr bereitgestellten AutoService-Annotation zu implementieren Erstellen Sie manuell den angegebenen Ordner und konfigurieren Sie dann den Pfad unseres Anmerkungsprozessors.

Nach Abschluss der oben genannten Arbeiten scannt jvm während der Kompilierung alle

Implementierungsklassen, hier ist
, und ruft dann die Prozessmethode der Implementierungsklasse auf, um die entsprechenden Vorgänge auszuführen, und dann unsere Arbeit Dabei kann die Methode zum Generieren von Code geschrieben werden, und dann kann die spezifische Methode zum Generieren von Code mit Hilfe des JavaPoet-Tools vereinfacht werden.

Da unser Codegenerierungstool

während der Kompilierung ausgeführt wirdAbstractProcessor, wird der endgültig generierte Java-Code zusammen mit dem gewöhnlichen Java-Code kompiliert und als gewöhnliche Klasse verwendet, sodass er keine Auswirkungen auf das endgültige Programm hat Aufgrund der Laufzeitleistung ist die Kompilierungsgeschwindigkeit höchstens etwas langsamer, da zusätzliche Klassen und Code generiert werden müssen. MyProcessor
Bisher wissen wir, wie man Code während der Kompilierung generiert, und wir haben die grundlegende Implementierungsidee, aber es gibt immer noch ein Problem. Wie verwende ich einen Anmerkungsprozessor, um eine Anmerkung zu verarbeiten und einen ähnlichen Effekt wie @BindView zu erzielen? Zunächst müssen wir natürlich eine Anmerkung anpassen. Wenn Sie die spezifische benutzerdefinierte Anmerkung nicht kennen, ist sie recht einfach und hat nicht viel Inhalt Ich werde es nicht zu sehr erklären. Sie können es natürlich auch einfach in den Demo-Link am Ende des Artikels einfügen. Laden Sie den Quellcode herunter, um ihn zu lernen. Nachdem wir eine Anmerkung definiert haben, sehen wir uns zuerst den

-Parameter an 🎜> Methode unseres

Annotationsprozessors. Dieser Parameter wird zum Speichern aller gescannten Daten während der Kompilierung verwendet (Nicht-Meta-Anmerkungen sind auch benutzerdefinierte Anmerkungen. Insgesamt gibt es vier Meta-Anmerkungen. Mit Ausnahme von Meta-Anmerkungen. der Rest sind benutzerdefinierte Anmerkungen. Dann durchlaufen wir diese Sammlung und wenn wir unsere benutzerdefinierten Anmerkungen erhalten, führen wir die entsprechende Logik aus. Der spezifische Code lautet wie folgt, wobei XXXAnnotation der Name Ihrer benutzerdefinierten Annotation ist

public class MyProcessor extends AbstractProcessor{
    @Override
    public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {        return false;
    }
}

Dann müssen wir auch die MyProcessor-Methode und die process()-Methode der annotations-Klasse sowie die

-Methode wird verwendet, um anzugeben, welche Annotation verarbeitet werden soll. Die

-Methode wird verwendet, um die Java-Version anzugeben, und der Wert ist im Allgemeinen AbstractProcessor. getSupportedAnnotationTypes()

完成以上工作后,对于自定义注解作用的对象,编译期间就会自动执行相应process() 里的逻辑,比如生成辅助类,这个辅助类其实就可以理解为依赖类,这个编译期间通过注解生成辅助类的过程,就相当于实现了注入。
到这里,一整个依赖注入的思路和实现方法已经全部打通。
下面我们来动手实现一个小例子吧!

动手实现

首先我们新建一个Android工程,按照默认的配置就好,新建完毕后,会有个默认的app的module,我们暂且不管它,然后直接新建一个java module,新建方式为file -> New -> New Module
然后选择Java Libary,这里给libary取名为javapoet
Android (Java)-Code implementiert manuell eine detaillierte technische Erklärung zur Abhängigkeitsinjektion然后在module对应的build.gradle下,加入下面两个依赖

implementation &#39;com.squareup:javapoet:1.11.1&#39;implementation &#39;com.google.auto.service:auto-service:1.0-rc4&#39;

第一个依赖是javaPoet的依赖,第二个是Google开源库auto的依赖
 上面提到让jvm加载我们自己的注解处理器有两种方式,这里我们先试一下用谷歌的这个开源库

这个module用来编写我们的注解处理器的实现类,先放着,待会再写。
继续新建一个java module,我这里取名为libannotation,然后在里面新建一个自定义注解,内容如下

@Retention(RetentionPolicy.CLASS)@Target(ElementType.TYPE)public @interface HelloAnnotation {}

@Retention(RetentionPolicy.CLASS)表示我们定义的这个注解会留存在class字节码文件中 ,但是在运行时是没有的,@Target(ElementType.TYPE) 表示我们的这个注解作用对象是类,然后我们看下整个目录的样子,
Android (Java)-Code implementiert manuell eine detaillierte technische Erklärung zur Abhängigkeitsinjektion

TestGenerator是一个我写的测试JavaPoet的测试类,可以忽略

现在我们开始写代码,首先在javapoet的build.gradle中加入libannotation  module的依赖,如下

implementation project(path: &#39;:libannotation&#39;)

然后在javapoet module中新建HelloProcessor类,用AutoService标注它,然后继承AbstractProcessor方法,重写相应的方法,具体怎么写,原理里解释的比较详细,然后注释里我作了详细说明,如下

import com.aiiage.libannotation.HelloAnnotation;import com.google.auto.service.AutoService;import com.squareup.javapoet.JavaFile;import com.squareup.javapoet.MethodSpec;import com.squareup.javapoet.TypeSpec;import java.io.IOException;import java.util.Collections;import java.util.Set;import javax.annotation.processing.AbstractProcessor;import javax.annotation.processing.Filer;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.Modifier;import javax.lang.model.element.TypeElement;/**
 * Created By HuangQing on 2018/7/20 15:38
 **/@AutoService(Processor.class)public class HelloProcessor extends AbstractProcessor {
    private Filer filer;    @Override
    public synchronized void init(ProcessingEnvironment processingEnv) {        super.init(processingEnv);
        filer = processingEnv.getFiler(); // 获得filer对象,用来创建文件
    }    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {        for (TypeElement element : annotations) {//遍历扫描到的注解集合
            if (element.getQualifiedName().toString().equals(HelloAnnotation.class.getCanonicalName())) {                // 当前注解是我们自定义的注解,也就是HelloAnnotation时,执行下列代码
                TypeSpec helloWorld = TypeSpec.classBuilder("HelloWorld")//声明类名为HelloWorld
                        .addModifiers(Modifier.PUBLIC, Modifier.FINAL)//声明类的修饰符为 public final
                        .addMethod(getMethodSpec("hello1", "Hello"))//为HelloWorld类添加名为hello1的方法,返回值为Hello
                        .addMethod(getMethodSpec("hello2", "Java"))//同上
                        .addMethod(getMethodSpec("hello3", "Poet!"))//同上
                        .build();                try {                    // 建立 com.aiiage.testjavapoet.HelloWorld.java 对象
                    JavaFile javaFile = JavaFile.builder("com.aiiage.testjavapoet", helloWorld)
                            .addFileComment(" This codes are generated automatically. Do not modify!")
                            .build();                    // 写入文件
                    javaFile.writeTo(filer);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }        return true;
    }    /**
     * @param methodStr  方法名
     * @param returnStr  返回值
     * @return
     */
    private static MethodSpec getMethodSpec(String methodStr, String returnStr) {        return MethodSpec.methodBuilder(methodStr)
                .addModifiers(Modifier.PUBLIC, Modifier.STATIC)//指定方法修饰符为 public static
                .returns(String.class) //指定返回值为String类型
                .addStatement("return $S", returnStr) //拼接返回值语句
                .build();
    }    @Override
    public Set<String> getSupportedAnnotationTypes() {        return Collections.singleton(HelloAnnotation.class.getCanonicalName());
    }    @Override
    public SourceVersion getSupportedSourceVersion() {        return SourceVersion.latestSupported();
    }
}

上述代码中,一定不要忘了加AutoService的注解,通过这个注解,我们的HelloProcessor注解处理器相当于执行了一个注册的过程,这样才会被jvm在编译时加载,代码生成部分最终生成的文件代码长下面这个样子,可以结合注释对着体会下,还是很方便的,有了javapoet之后

import java.lang.String;public final class HelloWorld {
  public static String hello1() {    return "Hello";
  }  public static String hello2() {    return "Java";
  }  public static String hello3() {    return "Poet!";
  }
}

ok,代码方面准备完毕。
接下来我们为app module添加依赖,如下

//必须使用annotationProcessor,而不是implementation//annotationProcessor修饰的,最终不会打包到apk中,可理解为在编译时执行的annotationProcessor project(&#39;:javapoet&#39;)implementation project(&#39;:libannotation&#39;)

然后我们手动编译一下项目,build -> make project,然后我们来到熟悉的MainActivity,首先使用我们的自定义注解标注MainActivity,要标在class上面,因为我们自定义的注解作用范围是Type,然后用一个TextView简单显示一下注入的HelloWorld类的几个静态方法,如下

@HelloAnnotationpublic class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        TextView tv=findViewById(R.id.tv);
        String str1="";
        str1+=HelloWorld.hello1()+" ";
        str1+=HelloWorld.hello2()+" ";
        str1+=HelloWorld.hello3();
        tv.setText(str1);
    }
}

编译运行,看到Hello Java Poet字样即表示成功!

然后我们上面提到,除了使用Google开源库auto实现注册注解处理器外,还可以使用手动配置的方式,手动配置的方式如下

在自定义注解处理器的module中,这里也就是javapoet的module中,在main目录下创建出resources目录,然后在resources目录下创建出META-INF目录,然后在META-INF目录下创建出services目录,然后在services目录下创建一个名为javax.annotation.processing.Processor 的文件,在该文件中声明我们的注解处理器:

com.aiiage.javapoet.HelloProcessor;

结语

这样我们就完成了一个最简单的依赖注入的实现,掌握其原理后,我们就不难明白类似ButterKnife、GreenDao是怎么实现的了,不过我们这个只是一个简单的功能,不得不感叹要生成那么多的代码,这得是一个多么心细和考验耐心的活啊!

相关推荐:

php生成Android客户端扫描可登录的二维码,android客户端

视频:Java视频教程_免费Java教程在线学习

Das obige ist der detaillierte Inhalt vonAndroid (Java)-Code implementiert manuell eine detaillierte technische Erklärung zur Abhängigkeitsinjektion. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn