概述
Android Framework源码阅读计划
Android Framework源码阅读计划(1)——LocationManager.java
Android Framework源码阅读计划(2)——LocationManagerService.java
前言
本篇主要介绍LocationManagerService中用的设计模式和原则,由于LocationManagerService是个非常重要的类,也是非常大的类,可能会有很多的点没有讲到,水平有限,欢迎补充指正。
主要设计单例模式,里氏替换原则,建造者模式
一、LocationManagerService
管理 LocationProviders 并发布位置更新和警报的服务类。
以上就是源码关于这个类的注释,非常简单。代码是最好的注释,通过类名基本可以猜到这个类的主要作用。上篇有提到,LocationManager作为外观类,将LocationManagerService的复制性隐藏了,也能体现出LocationManagerService的复杂。
二、使用的设计模式和原则
2.1 单例模式
2.1.1 定义
单例属于创建型模式,确保系统中某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。
优点:减少了内存开支,系统的性能开销
2.1.2 源码
从后缀就可以看到,这是一个Service,而且还是系统的服务。其中有个内部类Lifecycle继承了SystemService,用来管理LocationManagerService的生命周期。
/**
* Controls lifecycle of LocationManagerService.
*/
public static class Lifecycle extends SystemService {
private final SystemInjector mSystemInjector;
private final LocationManagerService mService;
public Lifecycle(Context context) {
super(context);
mUserInfoHelper = new LifecycleUserInfoHelper(context);
mSystemInjector = new SystemInjector(context, mUserInfoHelper);
// LocationManagerService初始化
mService = new LocationManagerService(context, mSystemInjector);
}
@Override
public void onStart() {
}
@Override
public void onBootPhase(int phase) {
}
...
}
基本上所有系统服务都是以该方法实现控制生命周期的,也是通过这种方式实现的单例模式。由于注册系统服务不是多线程的,所以不需要通过双重校验等方式来确保单例。
//SystemServiceRegistry.java
registerService(Context.LOCATION_SERVICE, LocationManager.class,
new CachedServiceFetcher<LocationManager>() {
@Override
public LocationManager createService(ContextImpl ctx) throws ServiceNotFoundException {
IBinder b = ServiceManager.getServiceOrThrow(Context.LOCATION_SERVICE);
return new LocationManager(ctx, ILocationManager.Stub.asInterface(b));
}});
由于系统服务需要被很多地方调用,所以并没有使用传统的方式使用getInstance()方法获取单例对象,还是在系统服务注册的时候,就将所有服务存放到了一个叫做SYSTEM_SERVICE_FETCHERS的容器,其本质就是就一个ArrayMap.java。统一管理,节省资源,使用起来也更加方便。
/**
* SystemServiceRegistry.java
* 使用上下文静态注册系统服务。
* This method must be called during static initialization only.
*/
private static <T> void registerService(@NonNull String serviceName,
@NonNull Class<T> serviceClass, @NonNull ServiceFetcher<T> serviceFetcher) {
SYSTEM_SERVICE_NAMES.put(serviceClass, serviceName);
SYSTEM_SERVICE_FETCHERS.put(serviceName, serviceFetcher);
SYSTEM_SERVICE_CLASS_NAMES.put(serviceName, serviceClass.getSimpleName());
}
以上两个代码块,充分展现了设计模式之美。通过策略模式,实现对不同ServiceFetcher的初始化,降低代码的耦合,代码更加简洁(SystemServiceRegistry中有100多个系统服务在这里注册)。之所以能过策略模式,最后实现将所有方法注册到通一个容器,依靠的是继承和多态,这里就不得不提到的是里氏替换原则。
2.2 里氏替换原则
2.2.1 定义
1.如果对每一个类型为 T1的对象 o1,都有类型为 T2 的对象o2,使得以 T1定义的所有程序 P 在所有的对象 o1 都代换成 o2 时,程序 P 的行为没有发生变化,那么类型 T2 是类型 T1 的子类型
2.所有引用基类的地方必须能透明地使用其子类的对象
里式替换原则最重要一点是子类必须能够替换其基类,也因此100+的系统方法都可以通过一个方法实现注册。里氏替换原则是对于开闭原则的补充,对继承进行了规则上的约束。
子类必须实现父类的抽象方法,但不得重写(覆盖)父类的非抽象(已实现)方法。
子类中可以增加自己特有的方法。
当子类覆盖或实现父类的方法时,方法的前置条件(即方法的形参)要比父类方法的输入参数更宽松。
当子类的方法实现父类的抽象方法时,方法的后置条件(即方法的返回值)要比父类更严格。
关于里氏替换原则,可以查看参考链接中的‘‘里氏替换原则’’。里氏替换原则虽然很好,但是只是指导性的原则,不是强制原则,具体实现还是要结合实际情况。
2.3建造者模式
2.3.1 定义
将一个复杂对象的构建与其表示分离,使得同样的构建过程可以创建不同的表示。属于创建型模式,采用链式调用一步一步创建一个复杂的对象,它允许用户只通过指定复杂对象的类型和内容就可以构建它们,用户不需要知道内部的具体构建细节。
当一个类的构造函数参数个数超过4个,而且这些参数有些是可选的参数,考虑使用构造者模式。
2.3.2 源码
ProviderProperties properties = new ProviderProperties.Builder()
.setHasNetworkRequirement(Boolean.parseBoolean(fragments[1]))
.setHasSatelliteRequirement(Boolean.parseBoolean(fragments[2]))
.setHasCellRequirement(Boolean.parseBoolean(fragments[3]))
.setHasMonetaryCost(Boolean.parseBoolean(fragments[4]))
.setHasAltitudeSupport(Boolean.parseBoolean(fragments[5]))
.setHasSpeedSupport(Boolean.parseBoolean(fragments[6]))
.setHasBearingSupport(Boolean.parseBoolean(fragments[7]))
.setPowerUsage(Integer.parseInt(fragments[8]))
.setAccuracy(Integer.parseInt(fragments[9]))
.build();
除了这个其实还有很多用到建造者模式类,比如Intent, LocationRequest。只是使用到建造者模式的类,LocationManagarService并不是。
建造者模式,主要包含
Builder:抽象建造者 上面的源码指Builder抽象基类,但是ProviderProperties.Builder并没有基类
ConcreteBuilder:上面的源码指Builder具体实现类
Director:指挥者 上面的源码没有用到,并非必要
Product:产品角色 上面的源码指最后返回的properties实例
// ProviderProperties.Builder
public static final class Builder {
...
public Builder() {
...
}
public Builder(@NonNull ProviderProperties providerProperties) {
...
}
/**
* Sets whether a provider requires network access. False by default.
*/
public @NonNull Builder setHasNetworkRequirement(boolean requiresNetwork) {
...
return this;
}
/**
* Sets whether a provider requires satellite access. False by default.
*/
public @NonNull Builder setHasSatelliteRequirement(boolean requiresSatellite) {
...
return this;
}
...
public @NonNull ProviderProperties build() {
return new ProviderProperties(mHasNetworkRequirement, mHasSatelliteRequirement,
mHasCellRequirement, mHasMonetaryCost, mHasAltitudeSupport, mHasSpeedSupport,
mHasBearingSupport, mPowerUsage, mAccuracy);
}
}
通过Builder源码可以看到,除build方法,其他方法均是对builder实例进行相关操作,返回的也是更加复制完整的builder对象。最后通过build方法,获取Product。
2.3.3 扩展
除了使用建造者模式,参数过多且存在非必选项的,可以采用折叠构造函数,以及JAVABean的方式。
- 折叠构造函数
通过重载,使用不同参数、不同数量的参数来对类进行初始化。这种方式比较常见,除了用在类的初始化,在方法的调用的时候也有类似的思想, 利用重载,可以使用多种方式发起定位请求。使用重载方法的例子,只是没找到使用折叠构造函数初始化话的类,参考链接关于建造者模式的链接中,也很详细例子
在没有好的注释情况下,多个参数可能是同类型的,容易传错。
// LocationManager
@RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
public void requestLocationUpdates(@NonNull String provider, long minTimeMs, float minDistanceM,
@NonNull LocationListener listener) {...}
@RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
public void requestLocationUpdates(@NonNull String provider, long minTimeMs, float minDistanceM,
@NonNull LocationListener listener, @Nullable Looper looper) {...}
@RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
public void requestLocationUpdates(
@NonNull String provider,
long minTimeMs,
float minDistanceM,
@NonNull @CallbackExecutor Executor executor,
@NonNull LocationListener listener) {...}
...
- Javabean
这个就没有什么好讲的了,学习java的同学这个应该都很熟。由于是分步设置的,用的时候需要判断必须值是否为空。
建造者模式可以解决上诉其他两种方式的问题,但是在特定场合下使用那两种方式或许也是不错的选择。
建造者模式适用情况包括:
需要生成的产品对象有复杂的内部结构,这些产品对象通常包含多个成员属性;
需要生成的产品对象的属性相互依赖,需要指定其生成顺序;(笔者的理解是把需要生成的放到构造器中)
对象的创建过程独立于创建该对象的类;(创建多个不同类型的产品的时候,可以使用Director)
隔离复杂对象的创建和使用,并使得相同的创建过程可以创建不同类型的产品。
参考链接
虽然看了很多文档,但是只会列出一些笔者觉得对我帮助很大的
里氏替换原则
秒懂设计模式之建造者模式
建造者模式
总结
其中还有用到很多东西,比如代理模式,但是LocationManagerService不是代理模式,只是用到了,和建造模式一样,相比而言,建造者模式的类比较简单,所以放到这里写了,其他会拆出来写。当然也存在水平不足没发现的,欢迎各位大佬提出指正了。
须知少年凌云志,曾许人间第一流
最后
以上就是自然翅膀为你收集整理的Android Framework源码阅读计划(2)——LocationManagerService.java前言一、LocationManagerService二、使用的设计模式和原则参考链接总结的全部内容,希望文章能够帮你解决Android Framework源码阅读计划(2)——LocationManagerService.java前言一、LocationManagerService二、使用的设计模式和原则参考链接总结所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复