Android DataSource 层实现

2024-06-15 13:18
文章标签 实现 android datasource

本文主要是介绍Android DataSource 层实现,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

前言

在 Android 日益成熟和完善的阶段,我们的项目也日益的开始注重性能和可维护性.今天要和大家聊一聊Android 项目里面的 DataSource

首先我们可以理解 DataSource 是一个单独的 Module,可以对外提供很多的数据操作
- 网络请求
- 本地的一些数据的持久化
- 一些第三方的数据封装
- 项目中一些Runtime的数据(比如用户信息,是否登录了等等)

目标架构

在我们的项目中,我们希望我们在使用 DataSource 的时候是可以使用一个,全部的地方都使用这一个. 但是我们又希望在我们维护 DataSource 的时候是可以分成多个来写的

这里写图片描述

实现这个架构

因为这样子不仅使用方便,更便于维护,但是要实现这样子的架构,却不太好实现.
假设我们现在要提供一个 ShareadPrefrence 存储的 DataSource 和 城市列表的数据的 DataSource

你写了一个 SpDataSource 接口和一个 SpDataSourceImpl 实现类
又写了一个 CityDataSource 接口和一个 CityDataSourceImpl 实现类

但是我们希望我们使用的时候是使用一个总的 DataSource

那你现在如何让一个总的 DataSource 拥有各个DataSource 的功能呢?

使用动态代理

你可以写一个 DataSource 接口继承所有的其他的 DataSource 接口

class DataSource extends DataSource1,DataSource2..... {
}

然后使用动态代理代理这个 DataSource 接口,然后在代理的拦截方法中判断方法的归属的类,然后调用相应的 DataSourceImpl

使用动态代理的弊端(我开始采取的就是这个方案)

  • 调试的时候, DataSource 实现类的 代码调试不了
  • 每一次有一个新的 DataSource,我就得修改 DataSource 总接口和代理中的代码

其中最为致命的还是低一点调试不了,所以我就开始寻求另一个方式来实现

使用代码生成

还记得之前说的目标吗?我们要实现使用的时候是一个 DataSource ,维护的是时候是多个 DataSource

如果我们能扫描到所有的 DataSource,并且让每一个 DataSource 中的方法都生成到 一个总的 DataSource 接口和 DataSource 实现类,那么问题不就解决了吗?

还有一个小问题,每一个子 DataSource 中方法还可能有和其他子 DataSource 名字重复的方法,所以生成的时候应该支持给每一个子 DataSource 添加一个前缀

这里就可以使用注解驱动器来实现啦

这里写图片描述

类似于这种用注解标记这个 DataSource 接口,标明实现类

这里写图片描述

最终会生成这三个类,一个总的 DataSource 接口,一个总的 DataSource 实现类 和 一个 DataSource 的管理类

最终代码实现

先写一个注解类
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.CLASS)
public @interface EHiDataSourceAnno {/*** 生成的方法的前缀,用于区分** @return*/String value() default "";/*** 用于内部区分,不能为相同的** @return*/String uniqueCode();/*** 实现类,会被加入到缓存中*/String impl() default "";/*** 如果实现类不是直接创建的,那么传入调用的方式** @return*/String callPath() default "";}
编写对应的 AnnotationProcessor
/*** 实现项目中的 retrofit 接口的代理接口和实现类生成,生成的类作为 datasource 层* time   : 2018/08/10** @author : xiaojinzi 30212*/
@AutoService(Processor.class)
@SupportedSourceVersion(SourceVersion.RELEASE_7)
@SupportedAnnotationTypes({"com.ehi.api.EHiDataSourceAnno"})
public class EHiDatasourceProcessor extends AbstractProcessor {private TypeMirror typeString;private TypeMirror typeVoid;private Filer mFiler;private Messager mMessager;private Types mTypes;private Elements mElements;private String classNameApi = "com.ehi.datasource.DataSourceApi";private String classNameApiImpl = "com.ehi.datasource.DataSourceApiImpl";private String classNameApiManager = "com.ehi.datasource.DataSourceManager";;@Overridepublic synchronized void init(ProcessingEnvironment processingEnvironment) {super.init(processingEnvironment);mFiler = processingEnv.getFiler();mMessager = processingEnvironment.getMessager();mTypes = processingEnv.getTypeUtils();mElements = processingEnv.getElementUtils();typeString = mElements.getTypeElement("java.lang.String").asType();typeVoid = mElements.getTypeElement("java.lang.Void").asType();}@Overridepublic boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {if (CollectionUtils.isNotEmpty(set)) {Set<? extends Element> routeElements = roundEnvironment.getElementsAnnotatedWith(EHiDataSourceAnno.class);parseAnno(routeElements);return true;}return false;}/*** 解析注解** @param currElements*/private void parseAnno(Set<? extends Element> currElements) {List<TypeElement> list = new ArrayList<>();Set<String> prefixSet = new HashSet<>();Set<String> uniqueSet = new HashSet<>();for (Element element : currElements) {TypeMirror tm = element.asType();if (!(element instanceof TypeElement)) {mMessager.printMessage(Diagnostic.Kind.ERROR, element + " is not a 'TypeElement' ");continue;}EHiDataSourceAnno anno = element.getAnnotation(EHiDataSourceAnno.class);if (anno.impl().isEmpty() && anno.callPath().isEmpty()) {mMessager.printMessage(Diagnostic.Kind.ERROR, element.toString() + ": EHiDataSourceAnno's impl and EHiDataSourceAnno's callPath are both empty");continue;}if (!anno.value().isEmpty()) {if (prefixSet.contains(anno.value())) {mMessager.printMessage(Diagnostic.Kind.ERROR, element.toString() + ": EHiDataSourceAnno's value is already exist");continue;}prefixSet.add(anno.value());}if (uniqueSet.contains(anno.uniqueCode())) {mMessager.printMessage(Diagnostic.Kind.ERROR, element.toString() + ": EHiDataSourceAnno's uniqueCode is not unique");continue;}uniqueSet.add(anno.uniqueCode());list.add((TypeElement) element);}try {createDataSourceApi(list);createDataSourceApiManager(list);createDataSourceApiImpl(list);} catch (Exception e) {mMessager.printMessage(Diagnostic.Kind.ERROR, "createDataSource fail: " + e.getMessage());e.printStackTrace();}}private void createDataSourceApi(List<TypeElement> apiClassList) throws IOException {// pkgString pkgApi = classNameApi.substring(0, classNameApi.lastIndexOf("."));// simpleNameString cnApi = classNameApi.substring(classNameApi.lastIndexOf(".") + 1);TypeSpec.Builder typeSpecBuilder = TypeSpec.interfaceBuilder(cnApi).addModifiers(Modifier.PUBLIC);typeSpecBuilder.addJavadoc("所有用注解标记的DataSource都会被整合到这里\n\n");for (TypeElement typeElement : apiClassList) {EHiDataSourceAnno remoteAnno = typeElement.getAnnotation(EHiDataSourceAnno.class);if (remoteAnno == null) {continue;}typeSpecBuilder.addJavadoc("@see " + typeElement.toString() + "\n");generateMethods(typeElement.getEnclosedElements(), typeSpecBuilder, remoteAnno, false);}TypeSpec typeSpec = typeSpecBuilder.build();JavaFile.builder(pkgApi, typeSpec).indent("    ").build().writeTo(mFiler);}private void createDataSourceApiManager(List<TypeElement> apiClassList) throws IOException {// pkgString pkgApi = classNameApiManager.substring(0, classNameApiManager.lastIndexOf("."));// simpleNameString cnApi = classNameApiManager.substring(classNameApiManager.lastIndexOf(".") + 1);TypeSpec.Builder typeSpecBuilder = TypeSpec.classBuilder(cnApi).addModifiers(Modifier.PUBLIC);ClassName mapClassName = ClassName.get("java.util", "Map");TypeName typeNameMap = ParameterizedTypeName.get(mapClassName, TypeName.get(typeString), TypeName.get(mElements.getTypeElement("java.lang.Object").asType()));FieldSpec.Builder mapFieldSpecBuilder = FieldSpec.builder(typeNameMap, "map", Modifier.PRIVATE).initializer("java.util.Collections.synchronizedMap(new java.util.HashMap<String,Object>())");typeSpecBuilder.addField(mapFieldSpecBuilder.build());for (TypeElement typeElement : apiClassList) {EHiDataSourceAnno anno = typeElement.getAnnotation(EHiDataSourceAnno.class);if (anno == null || anno.impl().isEmpty()) {continue;}TypeMirror returnType = typeElement.asType();MethodSpec.Builder methodSpecBuilder = MethodSpec.methodBuilder(anno.uniqueCode() + "DataSource").returns(TypeName.get(returnType));String implClassName = anno.impl();methodSpecBuilder.addStatement(implClassName + " value = (" + implClassName + ") map.get(\"" + anno.uniqueCode() + "\");").beginControlFlow("if (value == null)").addStatement("value = " + " new " + implClassName + "()").addStatement("map.put(\"" + anno.uniqueCode() + "\", value);").endControlFlow().addStatement("return value").addModifiers(Modifier.PUBLIC);MethodSpec methodSpec = methodSpecBuilder.build();typeSpecBuilder.addMethod(methodSpec);}// 内部的Holder 静态类TypeSpec.Builder typeSpecHolderBuilder = TypeSpec.classBuilder("Holder").addModifiers(Modifier.PUBLIC, Modifier.STATIC);typeSpecHolderBuilder.addField(FieldSpec.builder(ClassName.get(classNameApiManager.substring(0, classNameApiManager.lastIndexOf(".")), classNameApiManager.substring(classNameApiManager.lastIndexOf(".") + 1)), "instance").initializer("new " + classNameApiManager + "()").addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL).build());TypeSpec apiTypeSpec = typeSpecBuilder// 添加一个私有的构造函数.addMethod(MethodSpec.constructorBuilder().addModifiers(Modifier.PRIVATE).build()).addType(typeSpecHolderBuilder.build()).build();JavaFile.builder(pkgApi, apiTypeSpec).indent("    ").build().writeTo(mFiler);}private void createDataSourceApiImpl(List<TypeElement> apiClassList) throws IOException {// pkgString pkg = classNameApiImpl.substring(0, classNameApiImpl.lastIndexOf("."));// simpleNameString cn = classNameApiImpl.substring(classNameApiImpl.lastIndexOf(".") + 1);TypeSpec.Builder typeSpecBuilder = TypeSpec.classBuilder(cn).addModifiers(Modifier.PUBLIC);for (TypeElement typeElement : apiClassList) {EHiDataSourceAnno remoteAnno = typeElement.getAnnotation(EHiDataSourceAnno.class);if (remoteAnno == null) {continue;}List<? extends Element> enclosedElements = typeElement.getEnclosedElements();generateMethods(enclosedElements, typeSpecBuilder, remoteAnno, true);}ClassName superInterface = ClassName.get(classNameApi.substring(0, classNameApi.lastIndexOf(".")), classNameApi.substring(classNameApi.lastIndexOf(".") + 1));typeSpecBuilder.addSuperinterface(superInterface);TypeSpec typeSpec = typeSpecBuilder.build();JavaFile.builder(pkg, typeSpec).indent("    ").build().writeTo(mFiler);}private void generateMethods(List<? extends Element> enclosedElements, TypeSpec.Builder typeSpecBuilder, EHiDataSourceAnno anno, boolean isAddStatement) {for (Element elementItem : enclosedElements) {if (!(elementItem instanceof ExecutableElement)) {continue;}// 可执行的方法ExecutableElement executableElement = (ExecutableElement) elementItem;String methodName = "";if (anno.value().isEmpty()) {methodName = executableElement.getSimpleName().toString();} else {methodName = anno.value() + firstCharToUp(executableElement.getSimpleName().toString());}MethodSpec.Builder methodSpecBuilder = MethodSpec.methodBuilder(methodName);String parameterStr = generateParameters(executableElement, methodSpecBuilder);methodSpecBuilder.returns(TypeName.get(executableElement.getReturnType()));if (isAddStatement) {methodSpecBuilder.addModifiers(Modifier.PUBLIC);String returnStr = null;if (anno.callPath().isEmpty()) {returnStr = classNameApiManager + ".Holder.instance." + anno.uniqueCode() + "DataSource()." + executableElement.getSimpleName().toString() + "(" + parameterStr + ")";} else {returnStr = anno.callPath() + "." + executableElement.getSimpleName().toString() + "(" + parameterStr + ")";}if ("void".equals(executableElement.getReturnType().toString())) {methodSpecBuilder.addStatement(returnStr);} else {methodSpecBuilder.addStatement("return " + returnStr);}} else {methodSpecBuilder.addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT);}MethodSpec methodSpec = methodSpecBuilder.build();typeSpecBuilder.addMethod(methodSpec);}}private String generateParameters(ExecutableElement executableElement, MethodSpec.Builder methodSpecBuilder) {List<? extends VariableElement> typeParameters = executableElement.getParameters();StringBuffer sb = new StringBuffer();for (VariableElement typeParameter : typeParameters) {//mMessager.printMessage(Diagnostic.Kind.NOTE, "typeParameter ====== " + typeParameter.asType().toString());//TypeName.get(mElements.getTypeElement(""))TypeName typeName = TypeName.get(typeParameter.asType());String parameterName = typeParameter.getSimpleName().toString();ParameterSpec parameterSpec = ParameterSpec.builder(typeName, parameterName).build();if (sb.length() == 0) {sb.append(parameterName);} else {sb.append(",").append(parameterName);}methodSpecBuilder.addParameter(parameterSpec);}return sb.toString();}private String firstCharToUp(String str) {if (str == null || str.length() == 0) {return "";}String str1 = str.substring(0, 1).toUpperCase();String str2 = str.substring(1);return str1 + str2;}}

源码下载

DataSourceImpl

这篇关于Android DataSource 层实现的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1063562

相关文章

hdu1043(八数码问题,广搜 + hash(实现状态压缩) )

利用康拓展开将一个排列映射成一个自然数,然后就变成了普通的广搜题。 #include<iostream>#include<algorithm>#include<string>#include<stack>#include<queue>#include<map>#include<stdio.h>#include<stdlib.h>#include<ctype.h>#inclu

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

【Prometheus】PromQL向量匹配实现不同标签的向量数据进行运算

✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 🏆 作者简介:景天科技苑 🏆《头衔》:大厂架构师,华为云开发者社区专家博主,阿里云开发者社区专家博主,CSDN全栈领域优质创作者,掘金优秀博主,51CTO博客专家等。 🏆《博客》:Python全栈,前后端开发,小程序开发,人工智能,js逆向,App逆向,网络系统安全,数据分析,Django,fastapi

让树莓派智能语音助手实现定时提醒功能

最初的时候是想直接在rasa 的chatbot上实现,因为rasa本身是带有remindschedule模块的。不过经过一番折腾后,忽然发现,chatbot上实现的定时,语音助手不一定会有响应。因为,我目前语音助手的代码设置了长时间无应答会结束对话,这样一来,chatbot定时提醒的触发就不会被语音助手获悉。那怎么让语音助手也具有定时提醒功能呢? 我最后选择的方法是用threading.Time

Android实现任意版本设置默认的锁屏壁纸和桌面壁纸(两张壁纸可不一致)

客户有些需求需要设置默认壁纸和锁屏壁纸  在默认情况下 这两个壁纸是相同的  如果需要默认的锁屏壁纸和桌面壁纸不一样 需要额外修改 Android13实现 替换默认桌面壁纸: 将图片文件替换frameworks/base/core/res/res/drawable-nodpi/default_wallpaper.*  (注意不能是bmp格式) 替换默认锁屏壁纸: 将图片资源放入vendo

C#实战|大乐透选号器[6]:实现实时显示已选择的红蓝球数量

哈喽,你好啊,我是雷工。 关于大乐透选号器在前面已经记录了5篇笔记,这是第6篇; 接下来实现实时显示当前选中红球数量,蓝球数量; 以下为练习笔记。 01 效果演示 当选择和取消选择红球或蓝球时,在对应的位置显示实时已选择的红球、蓝球的数量; 02 标签名称 分别设置Label标签名称为:lblRedCount、lblBlueCount

Android平台播放RTSP流的几种方案探究(VLC VS ExoPlayer VS SmartPlayer)

技术背景 好多开发者需要遴选Android平台RTSP直播播放器的时候,不知道如何选的好,本文针对常用的方案,做个大概的说明: 1. 使用VLC for Android VLC Media Player(VLC多媒体播放器),最初命名为VideoLAN客户端,是VideoLAN品牌产品,是VideoLAN计划的多媒体播放器。它支持众多音频与视频解码器及文件格式,并支持DVD影音光盘,VCD影

Kubernetes PodSecurityPolicy:PSP能实现的5种主要安全策略

Kubernetes PodSecurityPolicy:PSP能实现的5种主要安全策略 1. 特权模式限制2. 宿主机资源隔离3. 用户和组管理4. 权限提升控制5. SELinux配置 💖The Begin💖点点关注,收藏不迷路💖 Kubernetes的PodSecurityPolicy(PSP)是一个关键的安全特性,它在Pod创建之前实施安全策略,确保P

工厂ERP管理系统实现源码(JAVA)

工厂进销存管理系统是一个集采购管理、仓库管理、生产管理和销售管理于一体的综合解决方案。该系统旨在帮助企业优化流程、提高效率、降低成本,并实时掌握各环节的运营状况。 在采购管理方面,系统能够处理采购订单、供应商管理和采购入库等流程,确保采购过程的透明和高效。仓库管理方面,实现库存的精准管理,包括入库、出库、盘点等操作,确保库存数据的准确性和实时性。 生产管理模块则涵盖了生产计划制定、物料需求计划、

C++——stack、queue的实现及deque的介绍

目录 1.stack与queue的实现 1.1stack的实现  1.2 queue的实现 2.重温vector、list、stack、queue的介绍 2.1 STL标准库中stack和queue的底层结构  3.deque的简单介绍 3.1为什么选择deque作为stack和queue的底层默认容器  3.2 STL中对stack与queue的模拟实现 ①stack模拟实现