欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Android Google AutoService框架使用詳解

 更新時(shí)間:2022年11月14日 14:22:50   作者:devnn  
AutoService是Google開發(fā)一個(gè)自動(dòng)生成SPI清單文件的框架。看過一些基于APT的三方框架源碼的讀者應(yīng)該有所了解。比如Arouter、EventBus等等

一般我們用它來自動(dòng)幫我們注冊(cè)APT文件(全稱是Annotation Process Tool,或者叫注解處理器,AbstractProcessor的實(shí)現(xiàn))。很多生成SPI文件的框架也是抄襲它的源碼,可見它的作用還不小。

APT其實(shí)就是基于SPI一個(gè)工具,是JDK留給開發(fā)者的一個(gè)在編譯前處理注解的接口。APT也是SPI的一個(gè)應(yīng)用。關(guān)于SPI和APT下文會(huì)詳細(xì)講到。

先講一下它是如何使用的。

AutoService的使用

AutoService框架的作用是自動(dòng)生成SPI清單文件(META-INF/services下的文件)。不用它也行,如果不使用它就需要手動(dòng)去創(chuàng)建這個(gè)文件、手動(dòng)往這個(gè)文件里添加服務(wù)(接口實(shí)現(xiàn))。

AutoService比較常用的場(chǎng)景是幫助注冊(cè)APT(注解處理器)。下面以APT的例子來講解它的使用。

開發(fā)APT需要在Java SE項(xiàng)目中開發(fā),因?yàn)樾枰^承AbstractProcessor,AbstractProcessor作用在Java編譯階段。

先創(chuàng)建Java module,在Android Studio中也可以創(chuàng)建,然后在build.gradle中添加依賴,如下dependencies部分。

通過annotationProcessor添加注解處理器(AutoServiceProcessor.class),同時(shí)需要通過implementation添加annotation依賴,即AutoService.class。

plugins {
    id 'java-library'
}

dependencies {
    annotationProcessor 'com.google.auto.service:auto-service:1.0.1'
    //一般結(jié)合JavaPoet框架來生成Java代碼,這里不對(duì)它進(jìn)行闡述。
    //implementation 'com.squareup:javapoet:1.13.0' 
    implementation 'com.google.auto.service:auto-service-annotations:1.0.1'
}

然后在你處理注解處理器類上方添加@AutoService注解即可,value指定成javax.annotation.processing.Processor類,因?yàn)橐傻腟PI清單文件(META-INF/services下的文件)名稱是

javax.annotation.processing.Processor 這個(gè)Processor是Java內(nèi)置的,Javac編譯前默認(rèn)的注解處理器接口。如果是我們自定義的接口就指定成自己的接口名。

@AutoService(value = {Processor.class})
public class MyProcessor extends AbstractProcessor {
    @Override
    public synchronized void init(ProcessingEnvironment processingEnv) {
        System.out.println("MyProcessor------------init---------------");
        super.init(processingEnv);
    }
    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        System.out.println("MyProcessor------------process---------------");
        return false;
    }
}

AbstractProcessor是繼承自Processor接口:

public abstract class AbstractProcessor implements Processor {
	...
}

AbstractProcessor這個(gè)類是JDK SE中的,Android Framework將它刪除了(因?yàn)椴恍枰灿貌恢?,所以Android Module里面是不存在的。這也說明為什么創(chuàng)建Java SE項(xiàng)目來編寫APT代碼。

AutoService注解的聲明如下,它的value是一個(gè)class集合,可以指定多個(gè)value。

@Documented
@Retention(CLASS)
@Target(TYPE)
public @interface AutoService {
  /** Returns the interfaces implemented by this service provider. */
  Class<?>[] value();
}

以上示例中MyProcessor的作用是處理項(xiàng)目的自定義注解,比如Arouter框架會(huì)利用它來處理@Aouter注解,并自動(dòng)生成路由注冊(cè)類。

編譯這個(gè)Java項(xiàng)目后就會(huì)自動(dòng)將MyProcessor添加到APT的SPI注冊(cè)文件中。

要注意的是,這個(gè)時(shí)候MyProcessor是沒有起作用的,init和process方法都不會(huì)執(zhí)行。因?yàn)樽⒔馓幚黼A段它并不在SPI注冊(cè)文件中,注解處理階段完成后它才注冊(cè)進(jìn)去。將Java項(xiàng)目打包成jar,這個(gè)MyProcessor才會(huì)在SPI注冊(cè)文件中。別的項(xiàng)目依賴這個(gè)jar,MyProcessor的代碼才會(huì)執(zhí)行。

以上是AutoService的使用。講了這些,可能有人看不懂。沒關(guān)系,先了解一下SPI技術(shù)。

關(guān)于SPI

什么是SPI呢,了解SPI是讀懂AutoService的基礎(chǔ)。

SPI是Service Provider Interface的簡(jiǎn)稱,是JDK默認(rèn)提供的一種將接口和實(shí)現(xiàn)類進(jìn)行分離的機(jī)制。這種機(jī)制能將接口和實(shí)現(xiàn)進(jìn)行解耦,大大提升系統(tǒng)的可擴(kuò)展性。

SPI機(jī)制約定:當(dāng)一個(gè)Jar包需要提供一個(gè)接口的實(shí)現(xiàn)類時(shí),這個(gè)Jar包需要在META-INF/services目錄里同時(shí)創(chuàng)建一個(gè)以服務(wù)接口命名的文件。該文件里就是實(shí)現(xiàn)該服務(wù)接口的具體實(shí)現(xiàn)類。而當(dāng)外部程序裝配這個(gè)模塊的時(shí)候,就能通過該Jar包META-INF/services/里的配置文件找到具體的實(shí)現(xiàn)類名,并裝載實(shí)例化,完成模塊的注入。

SPI示例

比如有一個(gè)接口IMyService

package com.devnn.demo.interface
public interface IMyService {
    void hello();
}

它的實(shí)現(xiàn)類有:

package com.devnn.demo.impl
import com.devnn.demo.interfaces.devnnService;
public class MyServiceImpl_1 implements IMyService {
    @Override
    public void hello() {
        System.out.println("Hi,I am MyServiceImpl_1");
    }
}
package com.devnn.demo.impl;
import com.devnn.demo.interfaces.devnnService;
public class MyServiceImpl_2 implements IMyService {
    @Override
    public void hello() {
        System.out.println("Hi,I am MyServiceImpl_2");
    }
}

resource/META-INF/services目錄下創(chuàng)建文件com.devnn.demo.interface.IMyService,內(nèi)容為所有實(shí)現(xiàn)類的完整名稱:

com.devnn.demo.impl.MyServiceImpl_1
com.devnn.demo.impl.MyServiceImpl_2

項(xiàng)目結(jié)構(gòu):

加載IMyService接口的所有子類:

public class SPI_Demo {
    public static void main(String[] agrs) {
       //使用jdk提供的類ServiceLoader來加載IMyService的子類
       ServiceLoader<IMyService> loaders = ServiceLoader.load(IMyService.class);
       //遍歷并調(diào)用子類方法
        for (IMyService service : loaders) { 
            service.hello();
        }
    }
}

運(yùn)行就會(huì)打?。?/p>

Hi,I am MyServiceImpl_1
Hi,I am MyServiceImpl_2

是不是很神奇,通過一個(gè)接口,就可以找到它的實(shí)現(xiàn)類,這就是SPI的作用。

APT技術(shù)

然后再說下APT,開頭說了APT是SPI的一個(gè)應(yīng)用。為什么這么說呢?APT其實(shí)就是Java給我們提供的內(nèi)置的SPI接口,作用是在編譯java前處理java源碼中的注解。

APT的服務(wù)接口就是這個(gè)

javax.annotation.processing.Processor

跟META_INF/service下的文件名是一致的。

Java編譯器讀取這個(gè)清單文件,加載實(shí)現(xiàn)這個(gè)接口的所有類,完成用戶的注解處理邏輯。

AutoService源碼

然后再回到AutoService,結(jié)合源碼對(duì)它進(jìn)行剖析,AutoService主要代碼就一個(gè)類,即AutoServiceProcessor.java,為了方便閱讀,筆者先將它原封不動(dòng)copy在這里,后面再對(duì)它進(jìn)行解析。

/*
 * Copyright 2008 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.google.auto.service.processor;
import static com.google.auto.common.AnnotationMirrors.getAnnotationValue;
import static com.google.auto.common.MoreElements.getAnnotationMirror;
import static com.google.auto.common.MoreStreams.toImmutableSet;
import static com.google.common.base.Throwables.getStackTraceAsString;
import com.google.auto.common.MoreElements;
import com.google.auto.common.MoreTypes;
import com.google.auto.service.AutoService;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Multimap;
import com.google.common.collect.Sets;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.SortedSet;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Filer;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedOptions;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.AnnotationValue;
import javax.lang.model.element.Element;
import javax.lang.model.element.PackageElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.SimpleAnnotationValueVisitor8;
import javax.lang.model.util.Types;
import javax.tools.Diagnostic.Kind;
import javax.tools.FileObject;
import javax.tools.StandardLocation;
/**
 * Processes {@link AutoService} annotations and generates the service provider
 * configuration files described in {@link java.util.ServiceLoader}.
 * <p>
 * Processor Options:<ul>
 *   <li>{@code -Adebug} - turns on debug statements</li>
 *   <li>{@code -Averify=true} - turns on extra verification</li>
 * </ul>
 */
@SupportedOptions({"debug", "verify"})
public class AutoServiceProcessor extends AbstractProcessor {
  @VisibleForTesting
  static final String MISSING_SERVICES_ERROR = "No service interfaces provided for element!";
  private final List<String> exceptionStacks = Collections.synchronizedList(new ArrayList<>());
  /**
   * Maps the class names of service provider interfaces to the
   * class names of the concrete classes which implement them.
   * <p>
   * For example,
   *   {@code "com.google.apphosting.LocalRpcService" ->
   *   "com.google.apphosting.datastore.LocalDatastoreService"}
   */
  private final Multimap<String, String> providers = HashMultimap.create();
  @Override
  public ImmutableSet<String> getSupportedAnnotationTypes() {
    return ImmutableSet.of(AutoService.class.getName());
  }
  @Override
  public SourceVersion getSupportedSourceVersion() {
    return SourceVersion.latestSupported();
  }
  /**
   * <ol>
   *  <li> For each class annotated with {@link AutoService}<ul>
   *      <li> Verify the {@link AutoService} interface value is correct
   *      <li> Categorize the class by its service interface
   *      </ul>
   *
   *  <li> For each {@link AutoService} interface <ul>
   *       <li> Create a file named {@code META-INF/services/<interface>}
   *       <li> For each {@link AutoService} annotated class for this interface <ul>
   *           <li> Create an entry in the file
   *           </ul>
   *       </ul>
   * </ol>
   */
  @Override
  public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
    try {
      processImpl(annotations, roundEnv);
    } catch (RuntimeException e) {
      // We don't allow exceptions of any kind to propagate to the compiler
      String trace = getStackTraceAsString(e);
      exceptionStacks.add(trace);
      fatalError(trace);
    }
    return false;
  }
  ImmutableList<String> exceptionStacks() {
    return ImmutableList.copyOf(exceptionStacks);
  }
  private void processImpl(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
    if (roundEnv.processingOver()) {
      generateConfigFiles();
    } else {
      processAnnotations(annotations, roundEnv);
    }
  }
  private void processAnnotations(
      Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
    Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(AutoService.class);
    log(annotations.toString());
    log(elements.toString());
    for (Element e : elements) {
      // TODO(gak): check for error trees?
      TypeElement providerImplementer = MoreElements.asType(e);
      AnnotationMirror annotationMirror = getAnnotationMirror(e, AutoService.class).get();
      Set<DeclaredType> providerInterfaces = getValueFieldOfClasses(annotationMirror);
      if (providerInterfaces.isEmpty()) {
        error(MISSING_SERVICES_ERROR, e, annotationMirror);
        continue;
      }
      for (DeclaredType providerInterface : providerInterfaces) {
        TypeElement providerType = MoreTypes.asTypeElement(providerInterface);
        log("provider interface: " + providerType.getQualifiedName());
        log("provider implementer: " + providerImplementer.getQualifiedName());
        if (checkImplementer(providerImplementer, providerType, annotationMirror)) {
          providers.put(getBinaryName(providerType), getBinaryName(providerImplementer));
        } else {
          String message =
              "ServiceProviders must implement their service provider interface. "
                  + providerImplementer.getQualifiedName()
                  + " does not implement "
                  + providerType.getQualifiedName();
          error(message, e, annotationMirror);
        }
      }
    }
  }
  private void generateConfigFiles() {
    Filer filer = processingEnv.getFiler();
    for (String providerInterface : providers.keySet()) {
      String resourceFile = "META-INF/services/" + providerInterface;
      log("Working on resource file: " + resourceFile);
      try {
        SortedSet<String> allServices = Sets.newTreeSet();
        try {
          // would like to be able to print the full path
          // before we attempt to get the resource in case the behavior
          // of filer.getResource does change to match the spec, but there's
          // no good way to resolve CLASS_OUTPUT without first getting a resource.
          FileObject existingFile =
              filer.getResource(StandardLocation.CLASS_OUTPUT, "", resourceFile);
          log("Looking for existing resource file at " + existingFile.toUri());
          Set<String> oldServices = ServicesFiles.readServiceFile(existingFile.openInputStream());
          log("Existing service entries: " + oldServices);
          allServices.addAll(oldServices);
        } catch (IOException e) {
          // According to the javadoc, Filer.getResource throws an exception
          // if the file doesn't already exist.  In practice this doesn't
          // appear to be the case.  Filer.getResource will happily return a
          // FileObject that refers to a non-existent file but will throw
          // IOException if you try to open an input stream for it.
          log("Resource file did not already exist.");
        }
        Set<String> newServices = new HashSet<>(providers.get(providerInterface));
        if (!allServices.addAll(newServices)) {
          log("No new service entries being added.");
          continue;
        }
        log("New service file contents: " + allServices);
        FileObject fileObject =
            filer.createResource(StandardLocation.CLASS_OUTPUT, "", resourceFile);
        try (OutputStream out = fileObject.openOutputStream()) {
          ServicesFiles.writeServiceFile(allServices, out);
        }
        log("Wrote to: " + fileObject.toUri());
      } catch (IOException e) {
        fatalError("Unable to create " + resourceFile + ", " + e);
        return;
      }
    }
  }
  /**
   * Verifies {@link ServiceProvider} constraints on the concrete provider class. Note that these
   * constraints are enforced at runtime via the ServiceLoader, we're just checking them at compile
   * time to be extra nice to our users.
   */
  private boolean checkImplementer(
      TypeElement providerImplementer,
      TypeElement providerType,
      AnnotationMirror annotationMirror) {
    String verify = processingEnv.getOptions().get("verify");
    if (verify == null || !Boolean.parseBoolean(verify)) {
      return true;
    }
    // TODO: We're currently only enforcing the subtype relationship
    // constraint. It would be nice to enforce them all.
    Types types = processingEnv.getTypeUtils();
    if (types.isSubtype(providerImplementer.asType(), providerType.asType())) {
      return true;
    }
    // Maybe the provider has generic type, but the argument to @AutoService can't be generic.
    // So we allow that with a warning, which can be suppressed with @SuppressWarnings("rawtypes").
    // See https://github.com/google/auto/issues/870.
    if (types.isSubtype(providerImplementer.asType(), types.erasure(providerType.asType()))) {
      if (!rawTypesSuppressed(providerImplementer)) {
        warning(
            "Service provider "
                + providerType
                + " is generic, so it can't be named exactly by @AutoService."
                + " If this is OK, add @SuppressWarnings(\"rawtypes\").",
            providerImplementer,
            annotationMirror);
      }
      return true;
    }
    return false;
  }
  private static boolean rawTypesSuppressed(Element element) {
    for (; element != null; element = element.getEnclosingElement()) {
      SuppressWarnings suppress = element.getAnnotation(SuppressWarnings.class);
      if (suppress != null && Arrays.asList(suppress.value()).contains("rawtypes")) {
        return true;
      }
    }
    return false;
  }
  /**
   * Returns the binary name of a reference type. For example,
   * {@code com.google.Foo$Bar}, instead of {@code com.google.Foo.Bar}.
   *
   */
  private String getBinaryName(TypeElement element) {
    return getBinaryNameImpl(element, element.getSimpleName().toString());
  }
  private String getBinaryNameImpl(TypeElement element, String className) {
    Element enclosingElement = element.getEnclosingElement();
    if (enclosingElement instanceof PackageElement) {
      PackageElement pkg = MoreElements.asPackage(enclosingElement);
      if (pkg.isUnnamed()) {
        return className;
      }
      return pkg.getQualifiedName() + "." + className;
    }
    TypeElement typeElement = MoreElements.asType(enclosingElement);
    return getBinaryNameImpl(typeElement, typeElement.getSimpleName() + "$" + className);
  }
  /**
   * Returns the contents of a {@code Class[]}-typed "value" field in a given {@code
   * annotationMirror}.
   */
  private ImmutableSet<DeclaredType> getValueFieldOfClasses(AnnotationMirror annotationMirror) {
    return getAnnotationValue(annotationMirror, "value")
        .accept(
            new SimpleAnnotationValueVisitor8<ImmutableSet<DeclaredType>, Void>(ImmutableSet.of()) {
              @Override
              public ImmutableSet<DeclaredType> visitType(TypeMirror typeMirror, Void v) {
                // TODO(ronshapiro): class literals may not always be declared types, i.e.
                // int.class, int[].class
                return ImmutableSet.of(MoreTypes.asDeclared(typeMirror));
              }
              @Override
              public ImmutableSet<DeclaredType> visitArray(
                  List<? extends AnnotationValue> values, Void v) {
                return values.stream()
                    .flatMap(value -> value.accept(this, null).stream())
                    .collect(toImmutableSet());
              }
            },
            null);
  }
  private void log(String msg) {
    if (processingEnv.getOptions().containsKey("debug")) {
      processingEnv.getMessager().printMessage(Kind.NOTE, msg);
    }
  }
  private void warning(String msg, Element element, AnnotationMirror annotation) {
    processingEnv.getMessager().printMessage(Kind.WARNING, msg, element, annotation);
  }
  private void error(String msg, Element element, AnnotationMirror annotation) {
    processingEnv.getMessager().printMessage(Kind.ERROR, msg, element, annotation);
  }
  private void fatalError(String msg) {
    processingEnv.getMessager().printMessage(Kind.ERROR, "FATAL ERROR: " + msg);
  }
}

AutoService源碼分析

主要邏輯在process方法中,通過實(shí)現(xiàn)AbstractProcessor的process方法來實(shí)現(xiàn)功能。

process委托給了processImpl:

 private void processImpl(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
   if (roundEnv.processingOver()) { //本輪注解處理完畢
      generateConfigFiles();//生成SPI注冊(cè)文件
    } else { //未處理完畢,繼續(xù)處理
      processAnnotations(annotations, roundEnv);//整理需要注冊(cè)的文件,放入緩存
    }
 }

再看processAnnotations方法,筆者已經(jīng)加了注釋:

private void processAnnotations(
      Set<? extends TypeElement> annotations, RoundEnvironment roundEnv){
	//獲取所有加了AutoService注解的類
    Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(AutoService.class);
    for (Element e : elements) {
      //將Element轉(zhuǎn)成TypeElement
      TypeElement providerImplementer = MoreElements.asType(e);
      //獲取AutoServce注解指定的value
      AnnotationMirror annotationMirror = getAnnotationMirror(e, AutoService.class).get();
      //獲取value集合
      Set<DeclaredType> providerInterfaces = getValueFieldOfClasses(annotationMirror);
      //如果沒有指定value,報(bào)錯(cuò)
      if (providerInterfaces.isEmpty()) {
        error(MISSING_SERVICES_ERROR, e, annotationMirror);
        continue;
      }
      //遍歷所有的value,獲取value的完整類名(例如javax.annotation.processing.Processor)
      for (DeclaredType providerInterface : providerInterfaces) {
        TypeElement providerType = MoreTypes.asTypeElement(providerInterface);
		//判斷是否是繼承關(guān)系,是則放入providers緩存起來,否則報(bào)錯(cuò)
        if (checkImplementer(providerImplementer, providerType, annotationMirror)) {
          providers.put(getBinaryName(providerType), getBinaryName(providerImplementer));
        } else {
         //報(bào)錯(cuò)代碼,略
        }
      }
    }
  }

注解處理完畢,就會(huì)生成SPI注冊(cè)文件。如果SPI路徑上文件已經(jīng)存在,先要把已存在的SPI清單讀進(jìn)內(nèi)存,再把新的provider加進(jìn)去,然后全部寫出,覆蓋原來的文件。這部分邏輯如下:

  private void generateConfigFiles() {
    Filer filer = processingEnv.getFiler();//獲取文件工具類,processingEnv是AbstractProcessor的成員變量,直接拿來用。
	//遍歷之前解析的providers緩存
    for (String providerInterface : providers.keySet()) {
     //providerInterface就是value字段指定的接口,例如javax.annotation.processing.Processor
      String resourceFile = "META-INF/services/" + providerInterface;
      log("Working on resource file: " + resourceFile);
      try {
        SortedSet<String> allServices = Sets.newTreeSet();
        try {
        //已經(jīng)存在的SPI文件
          FileObject existingFile =
              filer.getResource(StandardLocation.CLASS_OUTPUT, "", resourceFile);
          //SPI文件中的service條目清單
          Set<String> oldServices = ServicesFiles.readServiceFile(existingFile.openInputStream());
          log("Existing service entries: " + oldServices);
          allServices.addAll(oldServices);
        } catch (IOException e) {
          log("Resource file did not already exist.");
        }
		//新的service條目清單
        Set<String> newServices = new HashSet<>(providers.get(providerInterface));
        //如果已經(jīng)存在,則不處理
        if (!allServices.addAll(newServices)) {
          log("No new service entries being added.");
          continue;
        }
		//以下是將緩存的services寫入文件中。
        log("New service file contents: " + allServices);
        FileObject fileObject =
            filer.createResource(StandardLocation.CLASS_OUTPUT, "", resourceFile);
        try (OutputStream out = fileObject.openOutputStream()) {
          ServicesFiles.writeServiceFile(allServices, out);
        }
        log("Wrote to: " + fileObject.toUri());
      } catch (IOException e) {
        fatalError("Unable to create " + resourceFile + ", " + e);
        return;
      }
    }
  }

可見AutoServiceProcessor的主要功能就是將加了AutoService注解的類,加到SPI注冊(cè)文件中。SPI文件名稱(或者叫服務(wù))可以通過value指定。

下面將AutoService從mavenCentral倉庫中下載下來(一個(gè)jar包),解壓查看它的內(nèi)容:

可以看到它里面內(nèi)容并不多,主要就是一個(gè)AutoServiceProcessor類和一個(gè)APT清單文件。打開這個(gè)清單文件,里面就是AutoServiceProcessor類的全路徑:

所以我們將AutoService加到j(luò)ava項(xiàng)目中,其實(shí)就是引入了AutoServiceProcessor這個(gè)注解處理器,幫助我們處理@AutoService注解,將我們的服務(wù)(一般是APT類,也可以是其它的類,通過value指定)自動(dòng)注冊(cè)進(jìn)SPI文件中。

看到這里,不知道讀者有沒有領(lǐng)悟。

AutoService是一個(gè)注解處理器,我們自己開發(fā)的APT也是注解處理器,它們都是注解處理器,AutoSevice是自動(dòng)幫我們注冊(cè)注解處理器的注解處理器。是不是有點(diǎn)繞?

當(dāng)然AutoService的作用不僅在于注冊(cè)APT,還可以注冊(cè)其它服務(wù)。只是注冊(cè)APT我們比較常見。

再舉一個(gè)AutoService的使用場(chǎng)景:

在組件化架構(gòu)app中,有一個(gè)主Module和若干業(yè)務(wù)Module,如何在主Module中初始化各個(gè)業(yè)務(wù)Module?這可以使用SPI技術(shù),在業(yè)務(wù)Module中創(chuàng)建一個(gè)初始化類實(shí)現(xiàn)一個(gè)共同的接口,然后在這個(gè)類上加AutoService注解,在主Module中就可以通過SPI機(jī)制加載這些業(yè)務(wù)Module的初始化類,調(diào)用初始化接口。

AutoService不僅是一個(gè)自動(dòng)注冊(cè)APT的框架,它還是一個(gè)SPI技術(shù)的模板,有時(shí)候我們需要自己開發(fā)一個(gè)基于APT同時(shí)又要注冊(cè)自定義service的框架,它的源碼是一個(gè)很好的參考。AutoServiceProcessor里面的大部分代碼是可以復(fù)制拿來用。再比如,ServiceFiles.java是SPI資源文件讀取和寫入的工具類,直接復(fù)制到我們項(xiàng)目中即可。

到此這篇關(guān)于Android Google AutoService框架使用詳解的文章就介紹到這了,更多相關(guān)Android Google AutoService內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Android Volley框架使用源碼分享

    Android Volley框架使用源碼分享

    這篇文章主要為大家分享了詳細(xì)的Android Volley框架使用流程源碼,具有參考價(jià)值,感興趣的小伙伴們可以參考一下
    2016-05-05
  • Android Activity跳轉(zhuǎn)動(dòng)畫效果

    Android Activity跳轉(zhuǎn)動(dòng)畫效果

    這篇文章主要介紹了Android Activity跳轉(zhuǎn)動(dòng)畫效果,本文圖文并茂給大家介紹的非常詳細(xì),需要的朋友可以參考下
    2017-02-02
  • Android通過代碼控制ListView上下滾動(dòng)的方法

    Android通過代碼控制ListView上下滾動(dòng)的方法

    今天小編就為大家分享一篇關(guān)于Android通過代碼控制ListView上下滾動(dòng)的方法,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧
    2018-12-12
  • Android Native fdsan檢測(cè)工具介紹

    Android Native fdsan檢測(cè)工具介紹

    這篇文章主要為大家介紹了Android Native fdsan檢測(cè)工具介紹,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-02-02
  • Android編程設(shè)計(jì)模式之備忘錄模式詳解

    Android編程設(shè)計(jì)模式之備忘錄模式詳解

    這篇文章主要介紹了Android編程設(shè)計(jì)模式之備忘錄模式,結(jié)合實(shí)例形式詳細(xì)分析了Android備忘錄模式的概念、原理、應(yīng)用場(chǎng)景、用法及相關(guān)操作注意事項(xiàng),需要的朋友可以參考下
    2017-12-12
  • Android自定義View實(shí)現(xiàn)兩種二維碼的掃描效果

    Android自定義View實(shí)現(xiàn)兩種二維碼的掃描效果

    這篇文章主要為大家詳細(xì)介紹了Android如何自定義View實(shí)現(xiàn)兩種二維碼的掃描效果,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2024-01-01
  • Android中AlarmManager+Notification實(shí)現(xiàn)定時(shí)通知提醒功能

    Android中AlarmManager+Notification實(shí)現(xiàn)定時(shí)通知提醒功能

    本篇文章主要介紹了Android中AlarmManager+Notification實(shí)現(xiàn)定時(shí)通知提醒功能,非常具有實(shí)用價(jià)值,需要的朋友可以參考下
    2017-10-10
  • Android MIUI通知類短信權(quán)限的坑

    Android MIUI通知類短信權(quán)限的坑

    本篇文章主要介紹了Android MIUI通知類短信權(quán)限的坑,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2018-03-03
  • Flutter自定義搜索框效果

    Flutter自定義搜索框效果

    這篇文章主要為大家詳細(xì)介紹了Flutter自定義搜索框效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-07-07
  • 去掉activity默認(rèn)動(dòng)畫效果的簡(jiǎn)單方法

    去掉activity默認(rèn)動(dòng)畫效果的簡(jiǎn)單方法

    下面小編就為大家?guī)硪黄サ鬭ctivity默認(rèn)動(dòng)畫效果的簡(jiǎn)單方法。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2016-12-12

最新評(píng)論