【开发12年码农教你】Android端简单易用的SPI框架-——-SPA

@Service(priority = 1)
public class APrinterService implements IPrinterService {
@Override
public void print() {
System.out.println(“this is a printer service.”);
}
}
复制代码

B模块 —— BPrinterService

@Service(path=“b_printer”, priority = 2)
public class BPrinterService implements IPrinterService {
@Override
public void print() {
System.out.println(“this is b printer service.”);
}
}
复制代码

C模块 —— CPrinterService

@Service(priority = 3)
public class CPrinterService implements IPrinterService {
@Override
public void print() {
System.out.println(“this is c printer service.”);
}
}
复制代码

最后是Main模块, 下面的逻辑也可以存在于是A,B,C模块中

IPrinterService printer = Spa.getService(IPrinterService.class); //取最高优先级
printer.print(); // 输出: this is c printer service.

APrinterService aprinter = Spa.getFixedService(APrinterService.class);
aprinter.print();// 输出: this is a printer service.

BPrinterService bPrinter = Spa.getFixedService(BPrinterService.class);
bPrinter.print();// 输出: this is b printer service.

// 和上面的Spa.getFixedService(BPrinterService.class)等价
IPrinterService pathPrinter = Spa.getService(“b_printer”); //是不是有路由的感觉
pathPrinter.print(); // 输出: this is b printer service.

复制代码

这就是SPA最基本的用法,到目前为止他已经有了SPI机制的能力了,是不是很简单!!! 难道SPA只有这点内容吗, 当然不是!

SPA创建的对象的生命周期是怎样的?

对于上面的示例大家有没有一个疑问, bPrinter和pathPrinter都是实现类BPrinterService对象,那么这两个对象相等吗, bPrinter == pathPrinter?

下面介绍一下@Service注解的 scope属性

scope定义一个对象的生命周期,SPA内置的scope有

  • normal, 普通对象,每次都返回一个新创建对象, 默认scope
  • global, 全局对象,可以看做是一个单例,每次返回的都是同一个对象, 对象将在第一次被使用时创建
  • weak, 对象使用弱引用缓存,如果没有被gc回收,则不会重新创建
  • soft, 对象使用软引用缓存,如果没有被gc回收,则不会重新创建
  • custom, 自定义缓存策略, 当scope不是上面列出的值时,会被认为是自定义缓存策略,自定义缓存策略将在Spa进阶篇中介绍

那么bPrinter和pathPrinter相等吗? 答案就显而易见了,因为SPA对象默认的生命周期是nornal,也就是每次都会创建一个新对象,所以 bPrinter != pathPrinter。 如果想要 bPrinter == pathPrinter, 只需要将BPrinterService的scope定义为 global!

@Service(path=“b_printer”, priority = 2, scope=Spa.Scope.GLOBAL) //scope 设置为 global
public class BPrinterService implements IPrinterService {
@Override
public void print() {
System.out.println(“this is b printer service.”);
}
}
复制代码

SPA的方法拦截能力

SPA并不是简单的创建并返回一个对象,SPA实际返回的是目标对象的代理,通过代理,对象执行方法时,我们就能对该对象实施拦截,

SPA有灵活的拦截能力,不仅仅可以设置拦截器,还可以设置拦截策略

  • 自定义拦截器,多个拦截器默认按优先级顺序依次执行拦截
  • 自定义拦截策略,多个拦截器时,这些拦截器的执行顺序、执行方式由拦截策略决定

自定义拦截策略放到后面进阶篇,这里先说一下拦截器的用法,我们先看一下SPA执行方法的流程图,流程图演示的是上一节示例的CPrinterService的print方法调用过程

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

拦截器代码中是如何使用的?

实现IServiceInterceptor接口并被@Service标记的类会被SPA认为是一个方法调用拦截器

  • 先定义一个高优先级的拦截器

@Service(priority = Spa.Priority.MAX)
public class MaxPriorityServiceInterceptor implements IServiceInterceptor {
@Override
public void intercept(Class<? extends IService> originClass, IService source, Method method, Object[] args, IServiceInterceptorCallback callback) {
System.out.println(“this is a max priority interceptor.”)
callback.onContinue(method, args);

}
}
复制代码

  • 再定义一个普通优先级的拦截器

@Service
public class NormalServiceInterceptor implements IServiceInterceptor {

@Override
public void intercept(Class<? extends IService> originClass, IService source, Method method, Object[] args, IServiceInterceptorCallback callback) {
System.out.println(“this is a normal priority interceptor.”)
callback.onContinue(method, args);
}
}
复制代码

  • 再定义一个低优先级的拦截器

@Service(priority = Spa.Priority.MIN)
public class MinPriorityServiceInterceptor implements IServiceInterceptor {
@Override
public void intercept(Class<? extends IService> originClass, IService source, Method method, Object[] args, IServiceInterceptorCallback callback) {
System.out.println(“this is a min priority interceptor.”)
if (“chao.sample.c.CPrinterService”.equalse(originClass.getName()) && “print”.equals(method.getName())) { // 当拦截的是CPrinterService的print方法时,拦截!
callback.onInterrupt(null); //如果方法有返回值,null可以替换为拦截的值
} else {
callback.onContinue(method, args);
}
}
}

  • 执行print方法

IPrinterService printService = Spa.getService(IPrinterService.class); //cPrinter
printService.print();

  • 最后看输出结果

this is a max priority interceptor.
this is a normal priority interceptor.
this is a min priority interceptor.

this is c printer service. cPrinter的print被拦截,没有被执行,所以不会有这条输出

  • 再看下整个流程的时序图

SPA应用实战1 —— 子模块如何获取主模块的BuildConfig信息

多模块开发/组件化开发过程中,主模块(plugin为com.android.application的模块,一般指app模块)可以依赖任何模块,但是子模块无法依赖主模块,如果子模块想拿主模块的内容要怎么办呢? 下面演示如何通过Spa来获取主模块的Context和BuildConfig中的内容。 先在接口层定义一个BuildService

BuildService.java

public interface BuildService extends IService {
String buglyId(); // build.gradle中使用buildConfigField定义的buglyId

boolean debuggable();

String versionName();

int versionCode();

String applicationId();

String buildType();
}

在app模块中,实现这个service接口并使用@Service标记

  1. BuildServiceImpl.java

@Service(scope = Spa.Scope.GLOBAL) //Global可以看做是单例
public class BuildServiceImpl implements BuildService {
@Override
public String buglyId() {
return BuildConfig.BUGLY_ID;
}

@Override
public boolean debuggable() {
return BuildConfig.DEBUG;
}

@Override
public String versionName() {
return BuildConfig.VERSION_NAME;
}

@Override
public int versionCode() {
return BuildConfig.VERSION_CODE;
}

@Override
public String applicationId() {
return BuildConfig.APPLICATION_ID;
}

@Override
public String buildType() {
return BuildConfig.BUILD_TYPE;
}
}

准备工作已经完成,现在我们在pages模块的BuildInfoActivity中应用它

BuildInfoActivity.java

public class BuildInfoActivity extends AppCompatActivity {

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
BuildInfoPageBinding viewBinding = BuildInfoPageBinding.inflate(LayoutInflater.from(this));
setContentView(viewBinding.getRoot());

BuildService buildService = Spa.getService(BuildService.class);
viewBinding.applicationId.setText("applicationId: " + buildService.applicationId());
viewBinding.versionName.setText("versionName: " + buildService.versionName());
viewBinding.versionCode.setText("versionCode: " + buildService.versionCode() + “”);
viewBinding.buildType.setText("buildType: " + buildService.buildType());
viewBinding.debuggable.setText("debuggable: " + buildService.debuggable());
viewBinding.buglyId.setText(“buglyId:” + buildService.buglyId());
}
}}

看看最终的效果

这是SPA最简单的一个应用场景,更多应用实战将会在SPA的进阶篇中介绍

上面涉及到的所有示例代码都在这里

进阶篇链接:

  • SPA进阶篇1 —— 服务分发
  • SPA进阶篇2 —— 路由分发SPRouter
  • SPA进阶篇3 —— 组件Mock
  • SPA进阶篇4 —— RPC通信SPRpc

总结

本文主要介绍了Android端简单易用的SPI框架 —— SPA(Service Pool for Android)的能力和用法, 并和Java的SPI机制做了对比。相信大家看得出SPA更强大,更简洁而且消耗更低。
作者:小码哥哥
链接:https://juejin.​
im/post/6872335132229894158

最后

小编这些年深知大多数初中级Android工程师,想要提升自己,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助

因此我收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人

都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

资料⬅专栏获取
深知大多数初中级Android工程师,想要提升自己,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助**。

因此我收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。

[外链图片转存中…(img-S9aOrPM3-1719095282892)]一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人

都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

资料⬅专栏获取

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/734698.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

监控 Promethus的监控告警Alertmanager、Grafana

Promethus的监控告警Alertmanager Alertmanager 介绍 Prometheus的一个组件&#xff0c;用于定义和发送告警通知&#xff0c;内置多种第三方告警通知方式&#xff0c;同时还提供了对Webhook通知的支持基于警报规则对规则产生的警报进行分组、抑制和路由&#xff0c;并把告警发…

华硕笔记本重装系统详细操作,图文教程体验Win11如何重装系统

随着科技的不断发展&#xff0c;电脑操作系统的步骤也在不断更新迭代。对于华硕笔记本用户来说&#xff0c;升级到Windows 11操作系统可以带来更好的使用体验。本文将通过图文教程的形式&#xff0c;详细介绍华硕笔记本重装Windows 11系统的操作步骤&#xff0c;帮助用户顺利完…

用AI打败AI,利用ai指令对头条文章进行查重测试,结果出乎意料

前言&#xff1a;现在的ai真的太火爆了&#xff0c;让人不得不感叹ai的神奇之处&#xff0c;让我们一起来探讨下ai的强大之处吧&#xff01;本文仅限学习研究。 背景&#xff1a;最近看到很多人用ai写文章&#xff0c;然后被头条判定为疑似ai生成&#xff0c;所以想研究学习下…

ES6 解构赋值详解

ES6是JavaScript语言的一次重大更新&#xff0c;引入了许多新特性和语法改进&#xff0c;其中解构赋值是一个非常实用和灵活的语法特性。它可以让我们从数组或对象中提取值&#xff0c;并赋给对应的变量&#xff0c;让代码变得更加简洁和易读。本文将深入探讨ES6解构赋值的语法…

ROS | 常见故障排查

1.开启后发出一个WIFI WIFI名字&#xff1a;WHEELTEC接数字 安全密钥&#xff1a;dongguan 2.显示屏接口 USB接口接键鼠 3.远程登录命令 ssh -Y wheeltec192.168.0.100 是小车发出的WIFI的一个IP地址 4. 登录后确保IP地址 ip a 看一下 当前ip地址 倒数第四行-当前ip地址 1…

简易部署的设备日志采集工具

永久免费: Gitee下载 最新版本 使用说明: Moretl 企业级采集文件工具 优势: A. 开箱即用. 解压直接运行.不需额外安装. B. 批管理设备. 设备配置均在后台管理. C. 无人值守 客户端自启动,自更新. D. 稳定安全. 架构简单,内存占用小,通过授权访问.

数据结构---二叉树前中后序遍历

1. 某完全二叉树按层次输出&#xff08;同一层从左到右&#xff09;的序列为 ABCDEFGH 。该完全二叉树的前序序列为() A: ABDHECFG B: ABCDEFGH C: HDBEAFCG D: HDEBFGCA 2. 二叉树的先序遍历和中序遍历如下&#xff1a;先序遍历: EFHIGJK; 中序遍历: HFIEJKG. 则二叉…

[数据集][目标检测]药片药丸检测数据集VOC+YOLO格式152张1类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;152 标注数量(xml文件个数)&#xff1a;152 标注数量(txt文件个数)&#xff1a;152 标注类别…

企业级Web项目中应该如何做单元测试、集成测试和功能测试?

先自我介绍下&#xff1a; 本人有过10年测试经验&#xff0c;也参与过公安部网络安全产品测试交付、华为4G 网络设备测试交付、腾讯QQ空间APP产品测试交付。 关于“企业级Web项目中应该如何做单元测试、集成测试和功能测试”这个问题&#xff0c;我想给大家唠唠&#xff0c;我…

Django数据驾驶舱

Django数据驾驶舱 1.项目介绍2.项目结构3.库表结构3.1 appcsdn的models3.2 appssq的models3.3 appweather的models3.4 appweibo的models 4.功能展示5.解决问题5.1 路由配置5.2 后端数据与前端echarts展示5.3 长图表丝滑滚动条 6.遗留问题7.资源分享 1.项目介绍 这里介绍本人最…

excel数据透视

Excel中&#xff0c;数据透视图&#xff08;PivotChart&#xff09;和数据透视表&#xff08;PivotTable&#xff09;是两个紧密相关的工具&#xff0c;用于分析数据。数据透视表是数据透视图的数据源&#xff0c;也就是说&#xff0c;数据透视图是基于数据透视表中的数据创建的…

判断题无答案22届期末复习

判断: 1-3.结构体变量不能进行整体输入输出。 1-4.不同类型的结构变量之间也可以直接赋值。 1-5假设结构指针p已定义并正确赋值,其指向的结构变量有一个成员是int型的num,则语句 (*p).num = 100; 等价于p->num=1…

Linux下多进程访问同一个共享库处理流程

两个测试程序实现调用同一个SO库: ​​​​​​​ #include <stdio.h> #include "a/a.h" #include <unistd.h> int main() { int a = 4,b = 5; sum(a, b); int ret = get(); printf("ret=%d\n", ret); sleep(100)…

如何用好swoole/webman/workerman/hyperf呢

Webman框架的依赖 "require": { "php": ">7.2", "workerman/webman-framework": "^1.5.0",// "monolog/monolog": "^2.0" }, 依赖的核心框架也是很久的了 webman-framework的核心依赖 &q…

Ubuntu下FastDDS的源码编译和简单测试

FastDDS是eprosima公司开发的DDS&#xff08;Data Distribution Service&#xff09;库&#xff0c;使用的语言是C&#xff0c;自称是"The Most Complete Open Source DDS Middleware"&#xff0c;其官网是https://eprosima.com/&#xff0c;FastDDS源码在https://gi…

【面试干货】HashSet 和 TreeSet 的区别

【面试干货】HashSet 和 TreeSet 的区别 1、实现方式HashSetTreeSet 2、性能添加、删除和查找操作的时间复杂度HashSetTreeSet 3、元素唯一性4、迭代顺序HashSetTreeSet 5、使用场景HashSetTreeSet 6、示例代码 &#x1f496;The Begin&#x1f496;点点关注&#xff0c;收藏不…

Android 屏幕旋转 处理 AsyncTask 和 ProgressDialog 的最佳方案

初始化数据 */ private void initData(Bundle savedInstanceState) { if (savedInstanceState ! null) mDatas savedInstanceState.getStringArrayList(“mDatas”); if (mDatas null) { mLoadingDialog new LoadingDialog(); mLoadingDialog.show(getFragmentMana…

AI网络爬虫:搜狗图片的时间戳反爬虫应对策略

如何批量爬取下载搜狗图片搜索结果页面的图片&#xff1f;以孙允珠这个关键词的搜索结果为例&#xff1a; https://pic.sogou.com/pics?query%E5%AD%99%E5%85%81%E7%8F%A0&mode2 翻页规律如下&#xff1a; https://pic.sogou.com/napi/pc/searchList?mode2&start38…

Python: HexBinDecOct

因为&#xff1a; f0b1001110# 十进制 int()a0*2**01*2**11*2**21*2**30*2**40*2**51*2**6print(a)# 八进制 oct()print(78/8,78%8)# 110 001 001 8 116print(1*2**00*2**10*2**2,1*2**00*2**10*2**2,0*2**01*2**11*2**2)#十六进制 hex()#0 100 1110 16 4Eprint(sixteenFoo(0*…

2024-06-23 编译原理实验5——目标代码生成

文章目录 一、实验要求二、实验设计三、实验结果四、附完整代码 补录与分享本科实验&#xff0c;以示纪念。 一、实验要求 在词法分析、语法分析、语义分析和中间代码生成程序的基础上&#xff0c;将C−−源代码翻译为MIPS32指令序列&#xff08;可以包含伪指令&#xff09;&…