解耦与模块化:鸿蒙平台上的服务注册与查找机制

打印 上一主题 下一主题

主题 1060|帖子 1060|积分 3180

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

x
配景

服务注册和查找机制是一种常见的计划模式,它答应我们在运行时动态地加载和使用服务。本文的配景是为了在鸿蒙上实现我们Android版本中的服务注册和查找机制。在Android中我们利用服务注册和查找机制,将基座和第三方SDK的开辟分脱离来,用户可以按照我们给定的框架,自由开辟他们的SDK,再和基座一起打包成APK。基座则基于服务框架,调用第三方SDK的本领。通过这种方式,将基座开辟和第三方SDK的开辟完全解耦。
一、Java中的服务注册和查找

1.1 原理

在Java中,ServiceLoader类提供了一种服务提供者框架,它答应模块化应用程序在运行时动态加载、查找和使用服务提供者。
ServiceLoader是Java的一种服务提供者加载设施,它遵照了服务提供者框架模式(Service Provider Framework Pattern)。这种模式包罗三个组件:服务接口(Service Interface)、提供者注册API(Provider Registration API)、服务访问API(Service Access API)。在Java中,ServiceLoader类就是提供者注册API和服务访问API的实现。
ServiceLoader的工作原理主要基于Java的类路径(Classpath)搜刮和META-INF/services目录。当使用ServiceLoader.load(Class<S> service)方法时,ServiceLoader会搜刮类路径下全部META-INF/services/目录中名为服务接口全限定名的文件。这个文件是一个简单的文本文件,其中每一行都是一个服务提供者类的全限定名。ServiceLoader会读取这个文件,然后使用类加载器(ClassLoader)加载并实例化这些服务提供者类。
这种机制答应服务提供者在运行时被发现和加载,而无需在编译时进行硬编码,从而提供了很好的模块化和解耦。
1.2 例子

以下是一个简单的例子:

  • 定义一个服务接口:
  1. public interface IService {
  2.     void doSomething();
  3. }
复制代码

  • 实现这个接口:
  1. public class MyService implements IService {
  2.     public void doSomething() {
  3.         System.out.println("Doing something...");
  4.     }
  5. }
复制代码

  • 在META-INF/services/目录下创建一个名为com.example.IService的文件(全限定名),文件内容是MyService的全限定名:
  1. com.example.MyService
复制代码

  • 使用ServiceLoader加载和使用服务:
  1. ServiceLoader<IService> services = ServiceLoader.load(IService.class);
  2. for (IService service : services) {
  3.     service.doSomething();
  4. }
复制代码
在这个例子中,当我们运行上述代码时,ServiceLoader会自动找到并加载MyService,然后调用其doSomething方法。
二、鸿蒙中使用TypeScript实现的服务注册和查找

2.1 使用反射和配置文件模拟Java中的SeviceLoader

2.1.1 SDK侧


  • 定义一个服务接口:
  1. export interface IService {
  2.     doSomething(): void;
  3. }
复制代码

  • 实现这个接口:
  1. export class MyService implements IService {
  2.     doSomething(): void {
  3.         console.log("Doing something...");
  4.     }
  5. }
复制代码

  • 定义一个服务注册接口:
  1. export interface IServiceRegister {
  2.     registerService(): Array<IService>;
  3. }
复制代码

  • 实现服务注册接口:
  1. export class MyServiceRegister implements IServiceRegister {
  2.     registerService(): Array<IService> {
  3.         // 返回一个IService的实例数组
  4.         return [new MyService(), new MyService()];
  5.     }
  6. }
复制代码

  • 在lib_third_library的Index.ets中暴露接口和服务注册实现类:
  1. export { IService, IServiceRegister } from './src/main/ets/third_library/MyServiceRegister'
  2. export { MyServiceRegister } from './src/main/ets/third_library/MyServiceRegister'
复制代码
2.1.2 基座侧


  • 基座调用方:
  1. interface ClassData {
  2.     className: string[];
  3. }
  4. import('lib_third_library').then((ns: any) => {
  5.     let fileName = 'meta.json';
  6.     let res = $rawfile(fileName);
  7.     // 读取文件内容并转换为字符串
  8.     let jsonString = bytes2Str(ResUtil.getResManagerByResource(res).getRawFileContentSync(fileName));
  9.     // 将字符串解析为 JSON 对象,并将其类型断言为 ClassData
  10.     let classData = JSON.parse(jsonString) as ClassData;
  11.     classData.className.forEach((className) => {
  12.         console.log(className);
  13.         let registerServiceFunctionName = 'registerService';
  14.         // 使用类名实例化类
  15.         let myServiceRegisterImpl: any = new ns[className]();
  16.         // 调用 registerService 方法并获取返回的列表,列表中的每个元素都是 IService 类型
  17.         let serviceList: IService[] = myServiceRegisterImpl[registerServiceFunctionName]();
  18.         serviceList.forEach((service: IService) => {
  19.             // 运行服务函数
  20.             service.doSomething();
  21.         });
  22.     });
  23. });
复制代码

  • meta.json文件:
  1. {
  2.   "className":["MyServiceRegister"]
  3. }
复制代码
在这个例子中,我们首先创建了一个 MyServiceRegister 来注册全部的服务。然后,我们从 MyServiceRegister 中获取 MyService,并调用其 doSomething 方法。
2.2 关键技能点


  • meta.json是为了模拟java的META-INF/services/目录,必要SDK实现方写好实现类的名字:
  1. {
  2.   "className":["MyServiceRegister"]
  3. }
复制代码

  • 通过反射,调用第三方SDK中的服务注册类的注册函数,模拟java中的SeviceLoader.load。


  • registerService 注册服务的函数名字固定不变
  • registerService返回的是service的数组
  1. let registerServiceFunctionName = 'registerService';
  2. // 使用类名实例化类
  3. let myServiceRegisterImpl: any = new ns[className]();
  4. // 调用 registerService 方法并获取返回的列表,列表中的每个元素都是 IService 类型
  5. let serviceList: IService[] = myServiceRegisterImpl[registerServiceFunctionName]();
复制代码
三、优缺点分析

Java和TypeScript中的服务注册和查找机制都有其各自的优缺点:
标准化易用性延长加载服务生命周期管理解耦支持类型检查灵活度Java是,Java的ServiceLoader是Java平台的一部分,全部的Java开辟者都可以使用它,无需额外的库或框架。必要在META-INF/services/目录下创建配置文件,而且必要手动输入服务提供者的全限定名,这可能会导致错误。是,ServiceLoader支持延长加载,只有在真正必要服务提供者时,才会加载和实例化它们。无法管理服务提供者的生命周期,比方,无法控礼服务提供者的创建和销毁。是,通过使用ServiceLoader,服务的使用者和提供者可以完全解耦,它们只必要知道服务接口,而无需知道具体的实现。不支持,Java在编译时并不会检查服务的类型,可能会在运行时出现类型错误。较低,Java的ServiceLoader机制相对固定,不易进行定制。TypeScript否,TypeScript没有标准的服务注册和查找机制,不同的项目可能会有不同的实现。必要实现接口并注册服务,创建一份自定义的配置文件。否,TypeScript的服务注册和查找机制无法实现服务的延长加载。TypeScript的服务注册和查找机制通常无法管理服务的生命周期,除非在服务注册表中添加额外的逻辑。是,通过使用服务注册表,服务的使用者和提供者可以完全解耦,它们只必要知道服务接口,而无需知道具体的实现。是,由于TypeScript支持静态类型检查,因此可以在编译时检查服务的类型,避免了运行时类型错误。较高,TypeScript的服务注册和查找机制更加灵活,可以根据必要自定义服务注册表。 Java和TypeScript中的服务注册和查找机制各有优缺点,选择哪种机制取决于具体的需求和场景。
四、结论

Java的ServiceLoader提供了一种动态的、松耦合的服务加载机制,适合构建模块化的、可扩展的应用程序。本文在鸿蒙上模拟Java的ServiceLoader的机制,给出了一种可以在大型工程中解耦第三方SDK的鸿蒙实现,希望能给读者一些思绪启发。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

尚未崩坏

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表