文章目录
- 背景
- 一、Java中的服务注册和查找
- 1.1 原理
- 1.2 例子
- 二、鸿蒙中使用TypeScript实现的服务注册和查找
- 2.1 使用反射和配置文件模拟Java中的SeviceLoader
- 2.1.1 SDK侧
- 2.1.2 基座侧
- 2.2 关键技术点
- 三、优缺点分析
- 四、结论
背景
服务注册和查找机制是一种常见的设计模式,它允许我们在运行时动态地加载和使用服务。本文的背景是为了在鸿蒙上实现我们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 例子
以下是一个简单的例子:
- 定义一个服务接口:
java">public interface IService {void doSomething();
}
- 实现这个接口:
java">public class MyService implements IService {public void doSomething() {System.out.println("Doing something...");}
}
- 在
META-INF/services/
目录下创建一个名为com.example.IService
的文件(全限定名),文件内容是MyService
的全限定名:
com.example.MyService
- 使用
ServiceLoader
加载和使用服务:
java">ServiceLoader<IService> services = ServiceLoader.load(IService.class);
for (IService service : services) {service.doSomething();
}
在这个例子中,当我们运行上述代码时,ServiceLoader
会自动找到并加载MyService
,然后调用其doSomething
方法。
二、鸿蒙中使用TypeScript实现的服务注册和查找
2.1 使用反射和配置文件模拟Java中的SeviceLoader
2.1.1 SDK侧
- 定义一个服务接口:
typescript">export interface IService {doSomething(): void;
}
- 实现这个接口:
typescript">export class MyService implements IService {doSomething(): void {console.log("Doing something...");}
}
- 定义一个服务注册接口:
typescript">export interface IServiceRegister {registerService(): Array<IService>;
}
- 实现服务注册接口:
typescript">export class MyServiceRegister implements IServiceRegister {registerService(): Array<IService> {// 返回一个IService的实例数组return [new MyService(), new MyService()];}
}
- 在
lib_third_library
的Index.ets
中暴露接口和服务注册实现类:
typescript">export { IService, IServiceRegister } from './src/main/ets/third_library/MyServiceRegister'
export { MyServiceRegister } from './src/main/ets/third_library/MyServiceRegister'
2.1.2 基座侧
- 基座调用方:
typescript">interface ClassData {className: string[];
}import('lib_third_library').then((ns: any) => {let fileName = 'meta.json';let res = $rawfile(fileName);// 读取文件内容并转换为字符串let jsonString = bytes2Str(ResUtil.getResManagerByResource(res).getRawFileContentSync(fileName));// 将字符串解析为 JSON 对象,并将其类型断言为 ClassDatalet classData = JSON.parse(jsonString) as ClassData;classData.className.forEach((className) => {console.log(className);let registerServiceFunctionName = 'registerService';// 使用类名实例化类let myServiceRegisterImpl: any = new ns[className]();// 调用 registerService 方法并获取返回的列表,列表中的每个元素都是 IService 类型let serviceList: IService[] = myServiceRegisterImpl[registerServiceFunctionName]();serviceList.forEach((service: IService) => {// 运行服务函数service.doSomething();});});
});
- meta.json文件:
{"className":["MyServiceRegister"]
}
在这个例子中,我们首先创建了一个 MyServiceRegister
来注册所有的服务。然后,我们从 MyServiceRegister
中获取 MyService
,并调用其 doSomething
方法。
2.2 关键技术点
- meta.json是为了模拟java的
META-INF/services/
目录,需要SDK实现方写好实现类的名字:
{"className":["MyServiceRegister"]
}
- 通过反射,调用第三方SDK中的服务注册类的注册函数,模拟java中的SeviceLoader.load。
- registerService 注册服务的函数名字固定不变
- registerService返回的是service的数组
let registerServiceFunctionName = 'registerService';
// 使用类名实例化类
let myServiceRegisterImpl: any = new ns[className]();
// 调用 registerService 方法并获取返回的列表,列表中的每个元素都是 IService 类型
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的鸿蒙实现,希望能给读者一些思路启发。