Android 中使用 ServiceLoader、AutoService 摔坑记录

 

ServiceLoader Demo:https://github.com/mengzhinan/ServiceLoader_test

AutoService Demo:https://github.com/mengzhinan/AutoService_test

 

对 ServiceLoader 和 AutoService 早有耳闻,因各种原因而未对相关技术深入了解。

这两天在整理二者时踩到了无数硬坑,浏览器搜索发现全世界都是某一篇文章的集聚性传播,无法解决我的问题。

耗时 2 天死磕终于完成 Demo,并收获无数坑,现记录于此回报社会。

 

如果想对项目业务之间进行解耦、模块化开发;能使业务组件之间没有强依赖,彼此互相隐藏;且能够随时剔除某一个业务而不影响其他部分。就需要用到组件间通过接口进行业务交叉通讯了。如下图:

1、app 层与下层通讯通过接口,当业务变动需要剔除 A 业务时,对整个工程丝毫不需要修改变动。但是 app 层需要依赖 A 业务组件,目的是为了把代码打进输出包,而非代码层面的依赖调用。

2、A、B 业务层之间互相隐藏,避免业务调用耦合太高。

3、interface 中间层起到上下层隔离和联系作用,方便业务之间的交互。

上图即 Demo 的代码结构,参考 Demo。

 

一、环境说明:

1、Kotlin 开发,版本 1.3.72

2、AS 版本: 4.0

3、Gradle 版本:6.1.1

4、JDK 版本:1.8.0_192

即全部为最新版本,不存在网上说的版本兼容问题。

 

二、ServiceLoader 使用。

在 app 层通过 ServiceLoader.load 方法,扫描 /resources/META-INF/services/packageName.InterfaceName 文件名和内容,找到接口对应的实现类,然后调用。

按照上图的代码结构,还需要在 A 和 B 组件内新建对应的接口和实现类映射文件。简单不多说了,具体依赖和diamante细节看 Demo。

 

三、AutoService 使用。

在上述 Demo 基础上,你会发现还要我们手动新建 /resources/META-INF/... 映射文件,太麻烦了还容易写错。故 Google 爹爹给我们提供了 AutoServcie 工具,通过标记注解,然后使用注解处理器扫描数注解,并代替我们生成  /resources/META-INF/... 文件。其他的与上面方式一样。 代码细节参考 DEMO。

在试验 AutoService 过程中遇到的坑:

1、只在使用到 @AutoService 注解的模块中添加 AutoService 依赖。

注意:下面两行代码不可缺少,否则运行无效果的。

既然使用了 AutoService,就不需要新建 /resources/META-INF/servcies/... 映射文件了。

AutoServcieProcessor 的映射文件,Google 爹爹已经在依赖库中帮你做了。

// 依赖 autoService 库
implementation 'com.google.auto.service:auto-service:1.0-rc7'
annotationProcessor 'com.google.auto.service:auto-service:1.0-rc7'

 2、注意,新建 Android Project 时,默认的 minSdkVersion = 16。此处需要修改可执行的 app 模块 minSdkVersion = 26,否则运行报如下问题。

Invoke-customs are only supported starting with Android O (--min-api 26)

Invoke-customs are only supported starting with Android O (--min-api 26)
Stack trace:
com.android.tools.r8.a: Invoke-customs are only supported starting with Android O (--min-api 26)
	at com.android.tools.r8.dex.r.a(:289)
	at com.android.tools.r8.dex.r.a(:98)
	at com.android.tools.r8.dex.r.b(:188)
	at com.android.tools.r8.dex.b.a(:63)
	at com.google.common.util.concurrent.TrustedListenableFutureTask$TrustedFutureInterruptibleTask.runInterruptibly(TrustedListenableFutureTask.java:125)
	at com.google.common.util.concurrent.InterruptibleTask.run(InterruptibleTask.java:69)
	at com.google.common.util.concurrent.TrustedListenableFutureTask.run(TrustedListenableFutureTask.java:78)
	at com.google.common.util.concurrent.MoreExecutors$DirectExecutorService.execute(MoreExecutors.java:322)
	at java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:134)
	at com.google.common.util.concurrent.AbstractListeningExecutorService.submit(AbstractListeningExecutorService.java:66)
	at com.google.common.util.concurrent.AbstractListeningExecutorService.submit(AbstractListeningExecutorService.java:36)
	at com.android.tools.r8.dex.b.b(:46)
	at com.android.tools.r8.D8.d(:87)
	at com.android.tools.r8.D8.b(:1)
	at com.android.tools.r8.utils.W.a(:30)
	at com.android.tools.r8.D8.run(:11)
	at com.android.builder.dexing.D8DexArchiveBuilder.convert(D8DexArchiveBuilder.java:116)
	at com.android.build.gradle.internal.dependency.BaseDexingTransform.process(DexingTransform.kt:296)
	at com.android.build.gradle.internal.dependency.BaseDexingTransform.processNonIncrementally(DexingTransform.kt:243)
	at com.android.build.gradle.internal.dependency.BaseDexingTransform.doTransform(DexingTransform.kt:153)
	at com.android.build.gradle.internal.dependency.BaseDexingTransform.access$doTransform(DexingTransform.kt:69)
	at com.android.build.gradle.internal.dependency.BaseDexingTransform$transform$1.invoke(DexingTransform.kt:104)
	....................................................
	at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64)
	at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:48)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
	at org.gradle.internal.concurrent.ThreadFactoryImpl$ManagedThreadRunnable.run(ThreadFactoryImpl.java:56)
	at java.lang.Thread.run(Thread.java:748)
	Suppressed: java.util.concurrent.ExecutionException: com.android.tools.r8.a: Invoke-customs are only supported starting with Android O (--min-api 26)
		at com.google.common.util.concurrent.AbstractFuture.getDoneValue(AbstractFuture.java:552)
		at com.google.common.util.concurrent.AbstractFuture.get(AbstractFuture.java:513)
		at com.google.common.util.concurrent.FluentFuture$TrustedFuture.get(FluentFuture.java:86)
		at com.android.tools.r8.utils.S0.a(:14)
		at com.android.tools.r8.dex.b.b(:101)
		... 120 more
	[CIRCULAR REFERENCE:com.android.tools.r8.a: Invoke-customs are only supported starting with Android O (--min-api 26)]

3、发现现在使用 AS 新建 Java 类时,默认没有 public 关键字。此处使用 ServiceLoader.load 调用的接口的实现类必须是 public 类型的,且必须有公共无参的构造函数,否则报下面错误:

java.util.ServiceConfigurationError: com.duke.libinterface.IBook: Provider com.duke.liba.BookPythonImpl could not be instantiated

2020-06-21 18:31:20.459 9537-9537/com.duke.autoservice_test E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.duke.autoservice_test, PID: 9537
    java.util.ServiceConfigurationError: com.duke.libinterface.IBook: 
Provider com.duke.liba.BookPythonImpl could not be instantiated
        at java.util.ServiceLoader.fail(ServiceLoader.java:233)
        at java.util.ServiceLoader.access$100(ServiceLoader.java:183)
        at java.util.ServiceLoader$LazyIterator.nextService(ServiceLoader.java:392)
        at java.util.ServiceLoader$LazyIterator.next(ServiceLoader.java:416)
        at java.util.ServiceLoader$1.next(ServiceLoader.java:494)
        at com.duke.autoservice_test.ServiceLoaderHelper.loadServices(ServiceLoaderHelper.kt:21)
        at com.duke.autoservice_test.MainActivity.onResume(MainActivity.kt:27)
        at android.app.Instrumentation.callActivityOnResume(Instrumentation.java:1456)
        at android.app.Activity.performResume(Activity.java:8119)
        at android.app.ActivityThread.performResumeActivity(ActivityThread.java:4333)
        at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:4375)
        at android.app.servertransaction.ResumeActivityItem.execute(ResumeActivityItem.java:52)
        at android.app.servertransaction.TransactionExecutor.executeLifecycleState(TransactionExecutor.java:176)
        at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:97)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2049)
        at android.os.Handler.dispatchMessage(Handler.java:106)
        at android.os.Looper.loop(Looper.java:223)
        at android.app.ActivityThread.main(ActivityThread.java:7523)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:941)
     Caused by: java.lang.IllegalAccessException: java.lang.Class<com.duke.liba.BookPythonImpl> is not accessible from java.lang.Class<java.util.ServiceLoader$LazyIterator>
        at java.lang.Class.newInstance(Native Method)

同时不要忘记了使用 AutoService 注解,正确代码:

@AutoService(IBook.class)
public class BookTCPIPImpl implements IBook {
    @Nullable
    @Override
    public String getBookName() {
        return "book b";
    }
}

 

4、最后天坑,不报错也无效果。看我的 Demo 后会发现,虽然代码不多,但是都是使用 kotlin 写的,只有接口的实现类是用 java 写的(如上面实现类代码)。否则的话运行无效果,还会让你怀疑代码写错了、环境配置有问题。

为什么实现类不能用 kotlin 写?我不知道,可能是 kotlin 版本的问题、AS 的问题、AutoServcie 的问题、ServcieLoader 的问题。这个坑卡了我 2 天,否则不会消耗我那么久的时间,本来技术点就不难。

大佬不信?你试试,把上面的实现类自动转换为 kotlin 代码后再运行。

 

ServiceLoader Demo:https://github.com/mengzhinan/ServiceLoader_test

AutoService Demo:https://github.com/mengzhinan/AutoService_test

 

 

  • 2
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
Javaautoservice注解是一种用于服务发现的机制,它能够自动将服务的实现类注册到指定的配置文件,一般是META-INF/services目录下的文件。 然而,的确有一些情况下autoservice注解无法生成META-INF文件。这可能是由于以下几种原因导致的: 1. 编译问题:可能是由于编译配置的问题,导致编译器无法正确处理autoservice注解。这种情况下,我们可以尝试检查编译器的配置,或者使用其他IDE或编译工具进行尝试。 2. 库或框架限制:有些库或框架可能不支持autoservice注解,或者在使用autoservice注解时会有特殊的要求。我们可以查看相关库或框架的文档,了解其对autoservice注解的支持情况,或者尝试其他方式实现服务发现。 3. 配置文件缺失或错误:autoservice注解生成META-INF文件的前提是META-INF目录存在,并且配置文件的名称和路径正确。如果自动生成的META-INF文件不存在,或者文件名或路径有误,就无法实现服务发现。我们需要检查项目的文件结构,确认META-INF目录是否存在,并且配置文件的名称和路径是否正确。 总之,虽然autoservice注解通常可以自动将服务实现类注册到META-INF文件,但在某些情况下可能会遇到无法生成META-INF文件的问题。我们需要仔细检查编译配置、库或框架限制,以及配置文件是否正确等因素,以找出问题所在,并采取相应的解决方案。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值