我的编程空间,编程开发者的网络收藏夹
学习永远不晚

Spi机制在Android开发的应用示例详解

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

Spi机制在Android开发的应用示例详解

Spi机制介绍

SPI 全称是 Service Provider Interface,是一种将服务接口与服务实现分离以达到解耦、可以提升程序可扩展性的机制。嘿嘿,看到这个概念很多人肯定是一头雾水了,没事,我们直接就可以简单理解为是一种反射机制,即我们不需要知道具体的实现方,只要定义好接口,我们就能够在运行时找到一个实现接口的类,我们具体看一下官方定义。

举个例子

加入我是一个库设计者,我希望把一个接口暴露给使用者实现具体的逻辑,那么我肯定不能够写死实现类对吧,不然我们怎么扩展嘛!比如我们有以下接口

package com.example.newtestproject
interface TestSpi {
    fun getSpi();
}

如果我在使用的过程中,想不关心具体的实现类/又或者想兼容多个实现,那么怎么办呢?(比如日常开发的gradle,如果我想兼容多个agp版本在自己库的运行,一个个写死肯定是一个非常糟糕的实现,如果出了新版本,那么我们还要更改代码),我们能不能只定义一个规范,即上面的TestSpi就可以不关心以后的扩展呢?很简单,我们只需要在resource/META-INF/services目录下定义一个以该接口为名称的文件,文件内容是具体的接口实现类即可,如图

内容是实现类的全名称,假如我们有以下实现类

class TheTestSpi:TestSpi {
    override fun getSpi() {
        Log.i("hello","i am the interface implementation from TheTestSpi")
    }
}

那么文件只需要写入com.example.newtestproject.TheTestSpi即可。

在我们想要用到TestSpi的功能的时候,就可以通过以下方式进行使用,从而不用关心具体的实现,达到了解耦合的目的

// spi test
val load = ServiceLoader.load(TestSpi::class.java)
load.forEach {
    it.getSpi()
    if(it is TheTestSpi){
        Log.i("hello","theTestSpi")
    }
}

ServiceLoader.load

从上面我们可以看到,最关键的是调用了ServiceLoader.load方法,这个就是Spi具体的实现了,本质是什么呢?相信都能够猜到了,其实就是反射,我们来跟一下源代码

public static <S> ServiceLoader<S> load(Class<S> service) {
    // 获取了一个当前类的classloader,做好了准备
    ClassLoader cl = Thread.currentThread().getContextClassLoader();
    return ServiceLoader.load(service, cl);
}

紧接着就会调用到


private boolean hasNextService() {
    if (nextName != null) {
        return true;
    }
    if (configs == null) {
        try {
            String fullName = PREFIX + service.getName();
            if (loader == null)
                configs = ClassLoader.getSystemResources(fullName);
            else
                configs = loader.getResources(fullName);
        } catch (IOException x) {
            fail(service, "Error locating configuration files", x);
        }
    }
    while ((pending == null) || !pending.hasNext()) {
        if (!configs.hasMoreElements()) {
            return false;
        }
        pending = parse(service, configs.nextElement());
    }
    nextName = pending.next();
    return true;
}

我们可以看到,执行的时候有这么一句String fullName = PREFIX + service.getName();,很容易想到,如果我们要反射的话,是不是需要类全称,PREFIX 其实就是

private static final String PREFIX = "META-INF/services/";

可以看到,之所以我们需要在resource下定一个文件路径是META-INF/services,是因为在ServiceLoader中定义好了,所以ServiceLoader会按照约定的路径,去该文件夹下查找service.getName()(TestSpi)的实现类,最后通过

private S nextService() {
    if (!hasNextService())
        throw new NoSuchElementException();
    String cn = nextName;
    nextName = null;
    Class&lt;?&gt; c = null;
    try {
        c = Class.forName(cn, false, loader);
    } catch (ClassNotFoundException x) {
        fail(service,
             // Android-changed: Let the ServiceConfigurationError have a cause.
             "Provider " + cn + " not found", x);
             // "Provider " + cn + " not found");
    }
    .....

Class.forName(cn, false, loader) 去进行了类查找操作,因为我们通过foreach去遍历,其实就是通过迭代器去获取了每个实例,所以才能够调用到了实现类TheTestSpi的getSpi() 方法。

在Android中的应用

SPI机制在很多库的设计上都有应用,比如Coroutine(kotlin协程库)就有用到,比如我们经常用到的 MainCoroutineDispatcher,如果我们希望一个任务运行在主线程,在Android就可以通过handler的方式去向主线程post一个消息,那如果在其他环境呢?

kotlin不仅仅是想在android的世界立足,还有很多比如如果在native环境呢?在服务器环境呢?多平台环境呢(如KMM),那就不一定有Handler这个概念对不对!但是都有一个主线程的概念,所以Coroutine把这部分就通过SPI的方式去实现了,如

定义了这个接口 MainDispatcherFactory::class.java 用于给具体环境的主线程实现类进行实现,具体的实现类就是

internal class AndroidDispatcherFactory : MainDispatcherFactory {
    override fun createDispatcher(allFactories: List&lt;MainDispatcherFactory&gt;) =
        HandlerContext(Looper.getMainLooper().asHandler(async = true))
    override fun hintOnError(): String? = "For tests Dispatchers.setMain from kotlinx-coroutines-test module can be used"
    override val loadPriority: Int
        get() = Int.MAX_VALUE / 2
}

可以看到,在Android中就通过了Handler去实现,最后我们可以在源码中看到,SPI相关的注册信息

总结

通过SPI技术去实现的解耦合工作的出色工程还有很多很多,比如我们用的APT,还有didi开源的Booster,都有用到这方面的知识。

以上就是Spi机制在Android开发的应用示例详解的详细内容,更多关于Android开发Spi机制的资料请关注编程网其它相关文章!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

Spi机制在Android开发的应用示例详解

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

Spi机制在Android开发的应用示例详解

这篇文章主要为大家介绍了Spi机制在Android开发的应用示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
2022-11-13

详解MVP模式在Android开发中的应用

一、MVP介绍 随着UI创建技术的功能日益增强,UI层也履行着越来越多的职责。为了更好地细分视图(View)与模型(Model)的功能,让View专注于处理数据的可视化以及与用户的交互,同时让Model只关系数据的处理,基于MVC概念的MV
2022-06-06

Android同步屏障机制syncbarrier实例应用详解

这篇文章主要介绍了Android同步屏障机制syncbarrier实例应用,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习吧
2023-02-07

Golang在爬虫开发中的应用案例详解

go 语言以并发性和高性能著称,使其成为网络爬虫开发的理想选择。创建网站爬虫: go 语言提供简洁易学的语法,适用于快速编写爬虫。分布式爬虫: go 语言的 goroutine 和消息队列支持创建可扩展且可靠的分布式爬虫。部署和监控: go
Golang在爬虫开发中的应用案例详解
2024-05-12

GraphQL在react中的应用示例详解

这篇文章主要为大家介绍了GraphQL在react中的应用示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
2022-11-13

Android的VSYNC机制和UI刷新流程示例详解

这篇文章主要为大家介绍了Android的VSYNC机制和UI刷新流程示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
2022-12-09

Android开发笔记之:AsyncTask的应用详解

AsyncTask的介绍及基本使用方法关于AsyncTask的介绍和基本使用方法可以参考官方文档和《Android开发笔记之:深入理解多线程AsyncTask》这里就不重复。AsyncTask引发的一个问题上周遇到了一个极其诡异的问题,一个
2022-06-06

详解Java编程中的反射在Android开发中的应用

反射定义 “反射”(Reflection)能够让运行于JVM中的程序检测和修改运行时的行为。 为何需要反射 反射带来的好处包括: 在运行时检测对象的类型。 动态构造某个类的对象。 检测类的属性和方法。 任意调用对象的
2022-06-06

实例讲解Android应用开发中Fragment生命周期的控制

一、Fragment的生命周期初探 因为Fragment必须嵌入在Acitivity中使用,所以Fragment的生命周期和它所在的Activity是密切相关的。 如果Activity是暂停状态,其中所有的Fragment都是暂停状态;如果
2022-06-06

Android应用开发中Fragment与Activity间通信示例讲解

首先,如果你想在android3.0及以下版本使用fragment,你必须引用android-support-v4.jar这个包 然后你写的activity不能再继承自Activity类了,而是要继承android.support.v4.a
2022-06-06

android模拟器开发和测试nfc应用实例详解

从Android2.3开始支持NFC。不过NFC应用只能在Android手机(或平板电脑)上测试和开发,而且Android手机还必须有NFC芯 片。而且如果测试NFC传输文件时至少需要两部支持NFC的手机。当然,如果测试读写NFC标签,还需
2022-06-06

Android开发之开门狗在程序锁中的应用实例

本文实例讲述了Android开发之开门狗在程序锁中的应用方法。分享给大家供大家参考,具体如下:protected static final String TAG = "WatchDogService"; private AppLockDao
2022-06-06

Android应用开发中使用GridView网格布局的代码示例

基本布局演示 1. 定义包含GridView 的 main.xmk 2022-06-06

实例讲解Android应用开发中TabHost的使用要点

Tab与TabHost:这就是Tab,而盛放Tab的容器就是TabHost 。 如何实现?? 每一个Tab还对应了一个布局,这个就有点好玩了。一个Activity,对应了多个功能布局。 新建一个Tab项目,注意,不要生成main Act
2022-06-06

详解Qt中的双缓冲机制与实例应用

所谓双缓冲机制,是指在绘制控件时,首先将要绘制的内容绘制在一个图片中,再将图片一次性地绘制到控件上。本文主要为大家介绍了Qt中的双缓冲机制与实例应用,希望对大家有所帮助
2023-03-11

详解Android应用开发中Intent的作用及使用方法

Intent是一种运行时绑定(run-time binding)机制,它能在程序运行过程中连接两个不同的组件。通过Intent,你的程序可以向Android表达某种请求或者意愿,Android会根据意愿的内容选择适当的组件来完成请求。比如,
2022-06-06

编程热搜

  • Android:VolumeShaper
    VolumeShaper(支持版本改一下,minsdkversion:26,android8.0(api26)进一步学习对声音的编辑,可以让音频的声音有变化的播放 VolumeShaper.Configuration的三个参数 durati
    Android:VolumeShaper
  • Android崩溃异常捕获方法
    开发中最让人头疼的是应用突然爆炸,然后跳回到桌面。而且我们常常不知道这种状况会何时出现,在应用调试阶段还好,还可以通过调试工具的日志查看错误出现在哪里。但平时使用的时候给你闹崩溃,那你就欲哭无泪了。 那么今天主要讲一下如何去捕捉系统出现的U
    Android崩溃异常捕获方法
  • android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
    系统的设置–>电池–>使用情况中,统计的能耗的使用情况也是以power_profile.xml的value作为基础参数的1、我的手机中power_profile.xml的内容: HTC t328w代码如下:
    android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
  • Android SQLite数据库基本操作方法
    程序的最主要的功能在于对数据进行操作,通过对数据进行操作来实现某个功能。而数据库就是很重要的一个方面的,Android中内置了小巧轻便,功能却很强的一个数据库–SQLite数据库。那么就来看一下在Android程序中怎么去操作SQLite数
    Android SQLite数据库基本操作方法
  • ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
    工作的时候为了方便直接打开编辑文件,一些常用的软件或者文件我们会放在桌面,但是在ubuntu20.04下直接直接拖拽文件到桌面根本没有效果,在进入桌面后发现软件列表中的软件只能收藏到面板,无法复制到桌面使用,不知道为什么会这样,似乎并不是很
    ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
  • android获取当前手机号示例程序
    代码如下: public String getLocalNumber() { TelephonyManager tManager =
    android获取当前手机号示例程序
  • Android音视频开发(三)TextureView
    简介 TextureView与SurfaceView类似,可用于显示视频或OpenGL场景。 与SurfaceView的区别 SurfaceView不能使用变换和缩放等操作,不能叠加(Overlay)两个SurfaceView。 Textu
    Android音视频开发(三)TextureView
  • android获取屏幕高度和宽度的实现方法
    本文实例讲述了android获取屏幕高度和宽度的实现方法。分享给大家供大家参考。具体分析如下: 我们需要获取Android手机或Pad的屏幕的物理尺寸,以便于界面的设计或是其他功能的实现。下面就介绍讲一讲如何获取屏幕的物理尺寸 下面的代码即
    android获取屏幕高度和宽度的实现方法
  • Android自定义popupwindow实例代码
    先来看看效果图:一、布局
  • Android第一次实验
    一、实验原理 1.1实验目标 编程实现用户名与密码的存储与调用。 1.2实验要求 设计用户登录界面、登录成功界面、用户注册界面,用户注册时,将其用户名、密码保存到SharedPreference中,登录时输入用户名、密码,读取SharedP
    Android第一次实验

目录