AndroidAutoSize源码解读:Preconditions.checkNotNull实现
一、为什么需要参数校验?
在Android开发中,空指针异常(NullPointerException)是最常见的运行时错误之一。根据Google Play控制台的崩溃报告统计,空指针异常占所有崩溃的38%以上。AndroidAutoSize作为一个专注于屏幕适配的框架,需要在各种设备环境和使用场景下保持稳定性,而参数校验正是防止空指针异常的第一道防线。
Preconditions.checkNotNull方法的核心价值在于:
- 提前暴露问题:在开发阶段而非运行时捕获空指针问题
- 明确错误原因:通过自定义错误信息提供精确的调试线索
- 保障框架稳定性:防止外部传入的空值破坏内部逻辑
- 增强代码可读性:通过显式校验表明方法对参数的要求
二、Preconditions类结构分析
AndroidAutoSize的Preconditions类位于me.jessyan.autosize.utils包下,采用了工具类的经典设计模式:
public final class Preconditions {
// 私有构造函数防止实例化
private Preconditions() {
throw new IllegalStateException("you can't instantiate me!");
}
// 核心方法...
}
该类提供了三大类校验功能:
- 参数校验(checkArgument系列)
- 状态校验(checkState系列)
- 空值校验(checkNotNull系列)
其中checkNotNull方法有三个重载版本,构成了完整的空值校验体系。
三、checkNotNull方法实现详解
3.1 基础版实现
public static <T> T checkNotNull(T reference) {
if (reference == null) {
throw new NullPointerException();
} else {
return reference;
}
}
核心逻辑:
- 接收泛型参数T,保证类型安全
- 判断参数是否为null
- 若为null则抛出NullPointerException
- 若非null则原封不动返回参数
使用场景:简单的空值校验,不需要自定义错误信息
3.2 带错误信息版
public static <T> T checkNotNull(T reference, Object errorMessage) {
if (reference == null) {
throw new NullPointerException(String.valueOf(errorMessage));
} else {
return reference;
}
}
增强功能:
- 支持传入任意类型的错误信息
- 通过String.valueOf()统一转换为字符串
- 保留了原参数返回特性,支持链式调用
使用场景:需要简单描述空值原因的场景
3.3 格式化错误信息版
public static <T> T checkNotNull(T reference, String errorMessageTemplate, Object... errorMessageArgs) {
if (reference == null) {
throw new NullPointerException(format(errorMessageTemplate, errorMessageArgs));
} else {
return reference;
}
}
高级特性:
- 支持类似String.format()的模板语法
- 通过可变参数实现动态参数替换
- 结合内部format()方法实现复杂错误信息构建
format()方法实现:
static String format(String template, Object... args) {
template = String.valueOf(template);
StringBuilder builder = new StringBuilder(template.length() + 16 * args.length);
int templateStart = 0;
int i;
int placeholderStart;
for (i = 0; i < args.length; templateStart = placeholderStart + 2) {
placeholderStart = template.indexOf("%s", templateStart);
if (placeholderStart == -1) {
break;
}
builder.append(template.substring(templateStart, placeholderStart));
builder.append(args[i++]);
}
builder.append(template.substring(templateStart));
if (i < args.length) {
builder.append(" [");
builder.append(args[i++]);
while (i < args.length) {
builder.append(", ");
builder.append(args[i++]);
}
builder.append(']');
}
return builder.toString();
}
这个自定义format方法与String.format()相比有两个特点:
- 仅支持%s占位符,简化实现
- 当参数数量超过占位符数量时,自动将多余参数追加到末尾
四、在AndroidAutoSize中的应用场景
通过搜索AndroidAutoSize源码,我们发现checkNotNull方法主要应用在以下关键场景:
4.1 初始化配置校验
在AutoSizeConfig类中,对核心配置参数进行校验:
public AutoSizeConfig setUnitsManager(UnitsManager unitsManager) {
this.mUnitsManager = Preconditions.checkNotNull(unitsManager,
"unitsManager == null");
return this;
}
4.2 外部传入对象校验
在ExternalAdaptManager中,确保添加的适配规则不为null:
public void addExternalAdaptInfoOfActivity(Class<? extends Activity> activity, ExternalAdaptInfo info) {
Preconditions.checkNotNull(activity, "activity == null");
Preconditions.checkNotNull(info, "info == null");
mActivityExternalAdaptInfos.put(activity, info);
}
4.3 工具类方法入参校验
在ScreenUtils等工具类中,对上下文对象进行严格校验:
public static int getStatusBarHeight(Context context) {
Preconditions.checkNotNull(context, "context == null");
// ...实现逻辑
}
五、与其他校验方式的对比分析
| 校验方式 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| if (obj == null) 手动校验 | 简单直接,无额外开销 | 代码冗余,错误信息不统一 | 简单场景,临时校验 |
| Objects.requireNonNull() | 标准API,简洁 | 错误信息定制能力弱 | Java 7+环境,简单校验 |
| Preconditions.checkNotNull() | 错误信息丰富,支持格式化,返回原对象 | 增加方法调用开销 | 框架开发,复杂参数校验 |
| @NonNull注解 | 编译时提醒,不影响运行时 | 仅IDE提示,无法真正防止NPE | 辅助文档,静态代码分析 |
AndroidAutoSize选择自定义Preconditions.checkNotNull(),正是看中了其丰富的错误信息定制能力和统一的校验标准,这对于框架稳定性至关重要。
六、最佳实践与使用建议
6.1 方法参数的前置校验
推荐:在方法开头对所有必要参数进行校验
public void registerListener(OnAdaptListener listener) {
// 前置校验,快速失败
this.mListener = Preconditions.checkNotNull(listener,
"OnAdaptListener must not be null");
// 业务逻辑...
}
6.2 提供有意义的错误信息
推荐:包含参数名和预期状态
// 推荐写法
Preconditions.checkNotNull(adapter, "adapter must not be null, please set it before use");
// 不推荐
Preconditions.checkNotNull(adapter); // 错误信息不明确
6.3 链式调用场景
利用checkNotNull返回原对象的特性,可以实现流畅的链式调用:
// 配置构建链式调用
AutoSizeConfig.getInstance()
.setDesignWidthInDp(360)
.setDesignHeightInDp(690)
.setUnitsManager(Preconditions.checkNotNull(
new UnitsManager()
.setSupportDP(true)
.setSupportSP(true)
));
6.4 避免过度校验
不推荐:对局部变量或私有方法参数进行不必要的校验
private void processData(List<String> data) {
// 私有方法,调用方可控,可省略校验
Preconditions.checkNotNull(data); // 过度校验
// ...
}
七、源码设计亮点
7.1 泛型方法实现类型安全
public static <T> T checkNotNull(T reference)
通过泛型方法确保返回值类型与入参一致,避免类型转换。
7.2 私有构造函数防止实例化
private Preconditions() {
throw new IllegalStateException("you can't instantiate me!");
}
明确标识这是工具类,防止被错误实例化。
7.3 轻量级字符串格式化
自定义的format方法仅支持%s占位符,在保证功能的同时减少了性能开销,比String.format()更高效。
7.4 异常类型精准匹配
根据校验类型选择最合适的异常类型:
- 参数校验失败:IllegalArgumentException
- 状态校验失败:IllegalStateException
- 空值校验失败:NullPointerException
八、总结与思考
Preconditions.checkNotNull看似简单,实则体现了AndroidAutoSize框架的设计哲学:用最小的开销换取最大的稳定性。通过精心设计的参数校验机制,框架能够在问题发生前及时发现并反馈,大大提升了调试效率和用户体验。
作为开发者,我们应该:
- 重视参数校验,将其视为代码质量的重要组成部分
- 选择合适的校验方式,平衡开发效率和运行时性能
- 提供有意义的错误信息,为调试提供有效线索
- 在框架设计中建立统一的校验标准
AndroidAutoSize的Preconditions实现为我们提供了一个优秀的参数校验范例,值得在日常开发中学习和借鉴。
九、扩展思考:参数校验的性能影响
虽然方法调用会带来微小的性能开销,但现代JVM的即时编译(JIT)会对这类简单方法进行内联优化。通过Android Studio的Profiler工具测试发现,checkNotNull方法的调用开销在实际应用中几乎可以忽略不计,而其带来的稳定性提升却是显著的。
在性能敏感的代码路径中,可以通过以下方式优化:
- 减少循环内部的重复校验
- 对私有方法的内部参数适当放宽校验
- 利用@NonNull注解辅助静态代码分析
权衡之下,参数校验带来的收益远大于其性能开销,特别是对于框架类库而言。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



