Android中SPI的使用

1. 简介

SPI全称是Service Provider Interface,服务提供方接口,服务通常是指一个接口或者一个抽象类,服务提供方是对这个接口或者抽象类的具体实现,由第三方来实现接口提供具体的服务。SPI提供了一种动态的对应用程序进行扩展的机制,通常用作框架服务的拓展或者可替换的服务组件,但是在Android中并没有广泛的使用。

SPI运用场景主要有:

官方介绍文档:https://docs.oracle.com/javase/tutorial/sound/SPI-intro.html

2. SPI机制

  • 在resources/META-INF/services/目录中创建以服务全限定名命名的文件,该文件内容为服务的具体实现类的全限定名,文件中可以写多个服务的具体实现类
  • 使用ServiceLoader类动态加载服务的具体实现类
  • 服务具体的实现类必须有一个不带参数的构造方法
  • 如果服务的具体实现类在Jar包中,则需要放在主程序的classPath中

3. SPI的使用

SPI在Android工程中很少被使用到,主要因为SPI是针对厂商或者第三方服务商设置的机制,在平常的项目中很少会用到SPI。在业务越来越复杂的今天,很多应用趋向于组件化,项目中会依赖很多组件,这样会有一些问题,主工程和组件怎么通信,组件之间又怎么通信。当然有很多可以解决通信的方法,这里举个SPI例子。


图-1 举例示意图

主工程中依赖有三个组件,分别提供三个服务ABC,在用户切换后台后,主程序想要关闭定位,但是由于服务ABC可能对定位信息变化非常敏感,需要由服务的具体场景来告诉主程序是否能够关闭定位。

3.1 定义接口或者抽象类

定义接口

public interface IService {
    boolean keep();
}

3.2 实现接口

定义三个服务实现该接口,简化逻辑直接返回。

public class AService implements IService {
    @Override
    public boolean keep() {
        return false;
    }
}

public class BService implements IService {
    @Override
    public boolean keep() {
        return false;
    }
}

public class CService implements IService {
    @Override
    public boolean keep() {
        return true;
    }
}

3.3 创建spi描述文件

在工程的main目录下新建目录resources/META-INF/services,以服务接口名为文件名新建spi描述文件,内容为具体的服务实现类权限定名,可以有多个。


图-2 目录结构图

文件内容如下:

com.spi.demo.AService
com.spi.demo.BService
com.spi.demo.CService

3.4 调用具体服务

使用ServiceLoader去加载具体服务类,然后遍历具体的实现类,ServiceLoader其实就是去META-INFO/services目录下读取文件内容,然后实例化。

mBtn.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        ServiceLoader<IService> loader = ServiceLoader.load(IService.class);
        Iterator<IService> iterator = loader.iterator();
        boolean isKeepLoc = false;

        while (iterator.hasNext()) {
            if(iterator.next().keep()){
                isKeepLoc = true;
                break;
            }
        }

        Toast.makeText(MainActivity.this, "Keep location : " + isKeepLoc, Toast.LENGTH_SHORT).show();
    }
});

4. SPI的优点

只提供服务接口,具体服务由其他组件实现,接口和具体实现分离,同时能够通过系统的ServiceLoader拿到这些实现类的集合,统一处理。

5. SPI的缺点

  • Java中SPI是随jar发布的,每个不同的jar都可以包含一系列的SPI配置,而Android平台上,应用在构建的时候最终会将所有的jar合并,这样很容易造成相同的SPI冲突,常见的问题是DuplicatedZipEntryException异常
  • 读取SPI配置信息是在运行时从jar包中读取,由于apk是签过名的,在从jar中读取的时候,签名校验的耗时问题会造成性能损失

6. 优化思路

Java中使用ServiceLoader去读取SPI配置信息是在程序运行时,我们可以将这个读取配置信息提前,在编译时候就搞定,通过gradle插件,去扫描class文件,找到具体的服务类(可以通过标注来确定),然后生成新的java文件,这个文件中包含了具体的实现类。

这样程序在运行时,就已经知道了所有的具体服务类,缺点就是编译时间会加长,自己需要重新写一套读取SPI信息、生成java文件等逻辑。

经过优化后,SPI已经偏离了原本的初衷,但是可以做更多的事,可以将业务服务分离,通过SPI找到业务服务入口,业务组件化,抽成单独的aar,独立成工程。

7. 总结

SPI是java中原有的机制,在Android中可以直接使用,但是有上述提到的一些缺点,我们可以参考Java中的SPI机制,结合Android的环境,输出一套新的SPI机制。

时间: 2024-10-11 11:40:51

Android中SPI的使用的相关文章

谈谈-Android中的接口回调技术

Android中的接口回调技术有很多应用的场景,最常见的:Activity(人机交互的端口)的UI界面中定义了Button,点击该Button时,执行某个逻辑. 下面参见上述执行的模型,讲述James对Android接口回调技术的理解(结合前人的知识和自己的实践). 使用一个比喻很形象地说明:客户端有个疑问打电话请教服务端,但服务端无法现场给出解答,相互之间约定:服务端一旦有答案,使用电话的方式反馈给客户端. 以上有三个主体:客户端.服务端和接口(方式). 接口回调的原理框图说明: Demo界面

关于android中事件传递和分发的一些小理解

android中 当我们的手指触摸屏幕将产生一个事件, (假设 这个过程中如果没有显示的去拦截该事件的话)   这个事件会逐级传递到视图的最底层,即使在中间某些视图会响应这个事件( 这个视图也不会去消费这个事件),     仍然是会传递到底层(底层不响应该事件),然后再由底层回传到顶层,在传回顶层的过程中 ,   原先会响应该事件的视图才会去消费这个事件 例如在左图中                                 A                               

浅谈android中仅仅使用一个TextView实现高仿京东,淘宝各种倒计时

今天给大家带来的是仅仅使用一个TextView实现一个高仿京东.淘宝.唯品会等各种电商APP的活动倒计时.最近公司一直加班也没来得及时间去整理,今天难得休息想把这个分享给大家,只求共同学习,以及自己后续的复习.为什么会想到使用一个TextView来实现呢?因为最近公司在做一些优化的工作,其中就有一个倒计时样式,原来开发的这个控件的同事使用了多个TextView拼接在一起的,实现的代码冗余比较大,故此项目经理就说:小宏这个就交给你来优化了,并且还要保证有一定的扩展性,当时就懵逼了.不知道从何处开始

Android中监听ListView滑动到底部

Android中的应用就是ListView中向下滑动加载更多的功能,不要再onScroll方法中进行判断,那样当滑动到底部的时候,可能我们需要触发点什么事件,比如加载更多.隐藏某个控件等等. 第一种方法是直接滑动到底部就触发 listview.setOnScrollListener(new OnScrollListener(){       @Override       public void onScrollStateChanged(AbsListView view, int scrollS

Android基础入门教程——8.1.3 Android中的13种Drawable小结 Part 3

Android基础入门教程--8.1.3 Android中的13种Drawable小结 Part 3 标签(空格分隔): Android基础入门教程 本节引言: 本节我们来把剩下的四种Drawable也学完,他们分别是: LayerDrawable,TransitionDrawable,LevelListDrawable和StateListDrawable, 依旧贴下13种Drawable的导图: 1.LayerDrawable 层图形对象,包含一个Drawable数组,然后按照数组对应的顺序来

Android基础入门教程——8.1.2 Android中的13种Drawable小结 Part 2

Android基础入门教程--8.1.2 Android中的13种Drawable小结 Part 2 标签(空格分隔): Android基础入门教程 本节引言: 本节我们继续来学习Android中的Drawable资源,上一节我们学习了: ColorDrawable:NinePatchDrawable: ShapeDrawable:GradientDrawable!这四个Drawable~ 而本节我们继续来学习接下来的五个Drawable,他们分别是: BitmapDrawable:Insert

Android中微信抢红包插件原理解析和开发实现

一.前言 自从去年中微信添加抢红包的功能,微信的电商之旅算是正式开始正式火爆起来.但是作为Android开发者来说,我们在抢红包的同时意识到了很多问题,就是手动去抢红包的速度慢了,当然这些有很多原因导致了.或许是网络的原因,而且这个也是最大的原因.但是其他的不可忽略的因素也是要考虑到进去的,比如在手机充电锁屏的时候,我们并不知道有人已经开始发红包了,那么这时候也是让我们丧失了一大批红包的原因.那么关于网络的问题,我们开发者可能用相关技术无法解决(当然在Google和Facebook看来的话,他们

Android中View绘制流程以及invalidate()等相关方法分析

前言: 本文是我读<Android内核剖析>第13章----View工作原理总结而成的,在此膜拜下作者 .同时真挚地向渴望了解 Android 框架层的网友,推荐这本书,希望你们能够在Android开发里学到更多的知识 . 整个View树的绘图流程是在ViewRoot.java类的performTraversals()函数展开的,该函数做的执行过程可简单概况为 根据之前设置的状态,判断是否需要重新计算视图大小(measure).是否重新需要安置视图的位置(layout).以及是否需要重绘 (d

Android中View的绘制过程 onMeasure方法简述

Android中View的绘制过程 当Activity获得焦点时,它将被要求绘制自己的布局,Android framework将会处理绘制过程,Activity只需提供它的布局的根节点. 绘制过程从布局的根节点开始,从根节点开始测量和绘制整个layout tree. 每一个ViewGroup 负责要求它的每一个孩子被绘制,每一个View负责绘制自己. 因为整个树是按顺序遍历的,所以父节点会先被绘制,而兄弟节点会按照它们在树中出现的顺序被绘制. 绘制是一个两遍(two pass)的过程:一个mea