Skip to content

代码风格

代码不仅需要让计算机正确执行,更需要让开发者能够快速理解。

统一的代码风格能够:

  • 提升可读性
  • 提升可维护性
  • 降低沟通成本
  • 提高团队协作效率

注释规范

核心重点

注释的核心目标是解释:

  • 为什么这样设计
  • 这样设计的限制条件
  • 与其它模块的关系

而不是简单重复代码本身的含义。

类注释

类注释建议使用标准 Javadoc 格式。

应描述:

  • 类的作用
  • 设计意图(必要时)
  • 与其它类的关系(必要时)
  • 使用方式(复杂组件可选)
java
/**
 * 网络请求管理器
 * 
 * <p> 负责全局拦截器配置与 OkHttpClient 的生命周期管理 </p>
 * 
 * @author 作者
 * @since 2026/6/1 11:34
 */
public class RequestManager { }

/**
 * 统一数据流收集接口,对上层屏蔽不同响应式框架的底座实现。
 * 
 * @param <T> 传递的数据载体类型
 * @see kotlinx.coroutines.flow.Flow
 */
public interface FlowCollect<T> { }
Android Studio 模板配置(可选)
  1. 在 Android Studio 中打开 File > Settings > Editor > Live Templates 界面。
  2. 点击加号新建 Template Group。
  3. 选中新建的 Group 后,新建 Live Template,设置快捷名称等参数。
  4. 添加模板为:
java
/**
 * 
 * @author 你的名字
 * @date $date$ $time$
 */

添加完模板后,点击 Edit Variables 设置 date 和 time 的默认值,这样就会快速生成当前时间。

同类教程:《代码注释规范-IDEA 配置 Java 类方法注释模板》

字段注释

字段注释重点说明字段的真实用途、设计意图,并在必要时注明单位与取值范围。

字段注释核心原则

绝对不要机械地拆分“用途”或“范围”,而是在一条注释里,把后人最关心的隐藏背景(为什么定义它、限制是什么)交代清楚。

正例:

java
/** 最大重试 3 次,防止网络抖动时无限循环卡住守护线程 */
private int maxRetryCount = 3;

/** 核心连接超时时间(单位:毫秒) */
private long timeoutMillis = 5000;

/** 允许上传的离线日志文件最大限制,单位:MB */
private int mMaxFileSize = 100;

/** 遮罩层目标透明度,取值范围在 [0, 255],0 为完全透明 */
private int mAreaAlpha = 100;

/** 使用弱引用持有 Activity,避免后台任务导致内存泄漏 */
private final WeakReference<Activity> activityReference;

反例:

java
/** 默认初始化为 3 */
//(无意义:单纯复述赋值,没写为什么是 3)//
private int mRetryAttempts = 3;

/** 超时时间 */
// (无意义:未标明单位是秒、毫秒还是分钟)//
private long connectTimeoutMillis = 5000L;

/** 设置 Alpha 值 */
// (无意义:没写清楚取值边界是多少)//
private int targetAlpha = 100;

/** 保存 Activity */
// (无意义:没交代用 WeakReference 的核心意图是什么)//
private WeakReference<Activity> activityReference;

方法注释

方法注释只描述调用方无法从方法签名直接获得的信息。

设计原则

方法注释不是翻译方法名,而是补充方法签名无法表达的信息。

也并非每个方法都必须写全所有内容,应根据复杂度综合评估。

重点关注:

  • 参数是否存在特殊业务含义
  • 返回值是否存在特殊约束
  • 是否有调用时机要求
  • 是否存在副作用
  • 是否要求特定线程调用
  • 是否存在使用限制

对于 Getter、Setter、简单工具方法等自解释代码,可不强制编写方法注释。

正例:

java
/**
 * 将待发送数据加入串口发送队列。
 *
 * <p>该方法仅负责入队,不保证立即发送。</p>
 *
 * @param data 待发送的数据帧
 * @return true 表示成功加入队列,false 表示队列已满
 * @throws IllegalArgumentException 数据为空时抛出
 *
 * <p> 线程要求:可在任意线程调用 </p>
 */
public boolean enqueue(byte[] data) {
    // ...
}
java
/**
 * 获取当前连接设备
 *
 * @return 若设备未连接则返回 null
 */
@Nullable
public Device getCurrentDevice() {
    // ...
}

反例:

无意义:注释只是重复方法名,没有补充任何调用方真正关心的信息。

java
/**
 * 获取当前连接设备
 */
@Nullable
public Device getCurrentDevice() {
    // ...
}
java
/**
 * 发送数据
 */
public boolean enqueue(byte[] data) {
    // ...
}

关键行注释

行注释的唯一目的:解释代码“为什么”要这样写,而不是解释代码“在做什么”。

什么时候写?

  • 有特殊原因:这样写是为了规避 Bug、兼容历史逻辑或满足业务规则。
  • 防止后人踩坑:逻辑存在隐含约束,后续维护时容易被误删或错误重构。
  • 代码无法自解释:阅读代码仍无法理解设计意图或处理原因。
  • 存在边界条件:特殊场景、协议约束、异常处理需要额外说明。
  • 用于逻辑分段:将较长的方法,通过注释划分为多个清晰的处理阶段。

正例:

kotlin
// 连续 3 次通讯失败后才判定离线,避免网络抖动导致误报
if (failCount >= 3) {
    device.isOnline = false
}

反例:

kotlin
if (failCount >= 3) {
    // 设置设备离线
    device.isOnline = false
}

注释只是翻译代码,没有解释业务规则或设计原因

标记注释

常用标记:

标记说明适用场景
TODO待实现临时规划的完整功能或未完工的逻辑
FIXME待修复已知存在偶发 Bug 或异常隐患,需紧急跟进
REVIEW待确认架构设计或公共 API 变更,需要团队二次评审
OPTIMIZE待优化功能已跑通,但存在性能、内存、或技术债隐患

示例:

java
// TODO ...
// FIXME ...

代码排版

类成员顺序

推荐顺序

  1. 常量/伴生对象(Kotlin 的 companion object 放在最顶部)
  2. 静态字段与静态方法
  3. 成员变量(由高隐蔽性向低隐蔽性排列:private -> protected -> public)
  4. 构造方法与初始化块(init 块)
  5. 公开方法
  6. 私有方法
  7. 内部类/接口声明

kotlin 中的 init

在 kotlin 中 init 方法要比构造函数先执行,所以一定要放在构造函数之前。

kotlin
class ImageLoader private constructor() {
    
    init {
        // 主构造系统初始化(最先执行)
        System.loadLibrary("image_decoder")
    }

    constructor(context: Context) : this() {
        // 次级构造扩展
        initDiskCache(context.cacheDir)
    }
}

空格与空行

空行用于表达逻辑分组。

当代码进入新的逻辑阶段时,应使用空行进行视觉隔离。

不要为了“好看”随意插入空行,也不要把多个逻辑阶段挤在一起。

正例(适当空行,层次分明):

java
@Override
public void run() {
    RealTimeCurveTask timeCurveTask = mRealTimeCurveTask.get();
    if (timeCurveTask == null) {
        return;
    }

    // 刷新曲线图数据
    timeCurveTask.refresh(mEntity);

    // 获取一次新的延迟时间,继续循环。
    int second = getDelayedTime(mEntity);
    timeCurveTask.postDelayed(this, second * 1000L);
}

反例(代码紧凑,层次模糊):

java

@Override
public void run() {
    RealTimeCurveTask timeCurveTask = mRealTimeCurveTask.get();
    if(timeCurveTask==null) return;
    timeCurveTask.refresh(mEntity);
    int second = getDelayedTime(mEntity);
    timeCurveTask.postDelayed(this, second * 1000L);
}

单行语句规范

即使 ifelsefor 语句后面只有一行代码,也必须保留大括号。

正例:

java
/**
 * 更新用户头像
 *
 * @param user 用户信息
 */
private void updateAvatar(User user) {
    if (user == null) {
        return;
    }

    imageLoader.load(user.getAvatarUrl());
}

反例:

java
private void updateAvatar(User user) {
    if (user == null) 
        return; // 极其危险!后续维护极易因为缩进错觉引发逻辑漏洞

    imageLoader.load(user.getAvatarUrl());
}
java
private void updateAvatar(User user) {
    if (user == null) return; // 不建议

    imageLoader.load(user.getAvatarUrl());
}

大括号规范

统一采用 Kernighan & Ritchie (K&R) 风格(左括号不换行,右括号换行)。

Google 样式指南

谷歌在安卓官方的 Android Kotlin Style Guide 与 AOSP Java 规范 中明确将 K&R 风格 定为硬性标准。

参考官方文档:https://developer.android.com/kotlin/style-guide?hl=zh-cn#non-empty_blocks

推荐:

java
/**
 * 在工作线程中执行
 *
 * @param task 要添加到队列中的任务
 */
public void runOnWorkThread(@Nullable Runnable task) {
    if (task == null) {
        return;
    }
    getThreadHandler().runOnWorkThread(task);
}

避免(Allman 风格,拉长代码行数,在 Android 项目中略显臃肿):

java
public void runOnWorkThread(@Nullable Runnable task)
{ 
    if (task == null) {
        return;
    }
    getThreadHandler().runOnWorkThread(task);
}

长参数换行

当方法声明或调用时参数过多(通常超过 120 个字符宽),应当在逗号后换行,且每个参数独立一行,保持对齐。

推荐:

kotlin
fun downloadFile(
    context: Context,
    url: String,
    destinationPath: String,
    forceDownload: Boolean,
    callback: DownloadCallback
) {
    // 业务逻辑
}

避免:

kotlin
fun downloadFile(context: Context, url: String, destinationPath: String, forceDownload: Boolean, callback: DownloadCallback) { 
    // ...
}

链式调用

使用 Builder 模式或 Stream API 时,每个操作符点号 . 必须独占一行。

推荐:

java
OkHttpClient client = new OkHttpClient.Builder()
        .connectTimeout(10, TimeUnit.SECONDS)
        .readTimeout(10, TimeUnit.SECONDS)
        .addInterceptor(new HttpLoggingInterceptor())
        .build();

避免:

java
OkHttpClient client = new OkHttpClient.Builder().connectTimeout(10, TimeUnit.SECONDS).readTimeout(10, TimeUnit.SECONDS).build(); 

函数设计规范

单一职责

一个函数应该仅承担一个职责,完成一项具体的任务。

如果一个函数的功能过于复杂或处理多个不同的任务,就容易导致代码难以理解、修改和测试。

正例:

kotlin
fun handleUserData(jsonStr: String) {
    val user = parseUserJson(jsonStr) ?: return
    saveUserToDatabase(user)
    updateUserUi(user)
}

反例(既解析、又存库、还刷新 UI 的“全能型”函数):

kotlin
fun processUser(jsonStr: String) {
    // 解析 Json
    val gson = Gson()
    val user = gson.fromJson(jsonStr, User::class.java)
    // 存数据库
    val db = dbHelper.writableDatabase
    db.execSQL("INSERT INTO user ...")
    // 刷 UI
    nameTextView.text = user.name
}

函数长度

一个函数原则上应控制在 50 行代码以内。

超过 50 行通常意味着:

  • 承担了多个职责
  • 存在重复逻辑
  • 存在可抽取的业务语义

如果在 Android Studio 中,一屏还装不下它,请果断进行抽取重构。

参数数量控制

函数的参数应当控制在 4 个以内。超过 4 个时,应考虑将其封装为配置类/数据对象(Data Class)。

推荐:

kotlin
data class ImageConfig(
    val url: String,
    val placeholderRes: Int = R.drawable.placeholder,
    val errorRes: Int = R.drawable.error,
    val isCircle: Boolean = false
)

fun displayImage(config: ImageConfig) { /* ... */ }

避免:

kotlin
fun displayImage(url: String, placeholder: Int, error: Int, isCircle: Boolean, cacheInMemory: Boolean, animate: Boolean) { 
    // 极难调用,极易传错参数位置(尤其是连续几个 Boolean 或 Int 的情况)
}

异常处理

禁止吞掉异常

捕获异常后什么都不做(吞掉异常),会导致极其难以排查的暗箱 Bug。

正例:

java
public void test() {
    try {
        outStream.write(data);
    } catch (IOException exception) {
        Log.e(TAG, "Failed to flush backup data to local storage", exception);
        // 必要时向上传播或转换为业务自定义异常
        throw new BackupException("Storage standard error", exception); 
    }
}

反例:

java
public void test() {
    try {
        outStream.write(data);
    } catch (IOException exception) {
        // 假装什么都没发生,线上出了问题两眼一抹黑
    }
}

避免滥用 Throwable

除非是在框架顶层做全局未捕获异常捕获,否则不要直接兜底捕获 Throwable 或 Exception。

正例:

kotlin

try {
    val number = encryptedString.toInt()
} catch (ex: NumberFormatException) { // 精准捕获目标异常
    Log.w(TAG, "Invalid digital format inside string")
}

反例:

kotlin
try {
    parseData()
} catch (throwable: Throwable) { // 连 OOM、NullPointerException 一并吞掉了
    // 盲目兜底
}

控制流结构

避免嵌套过深

嵌套超过 3 层的 if-else 就是可读性的灾难。

因为多层嵌套会不断增加阅读负担,边界条件应优先返回

正例:

java
public void onReceive(byte[] buffer) {
    if (buffer == null) {
        return;
    }
    
    if (buffer.length < MIN_FRAME_LENGTH) {
        return;
    }

    if (!protocolParser.isValidFrame(buffer)) {
        return;
    }

    parseFrame(buffer);
}

反例:

java
public void onReceive(byte[] buffer) {
    if (buffer != null) {
        if (buffer.length >= MIN_FRAME_LENGTH) {
            if (protocolParser.isValidFrame(buffer)) {
                parseFrame(buffer);
            }
        }
    }
}

健壮性规范

明确空值约束

方法参数与返回值应明确声明是否允许为空,避免调用方无法判断空值约束。

推荐:

java
@Nullable
public Device getCurrentDevice() { }

public void updateDevice(@NonNull Device device) { }

避免:

java
public Device getCurrentDevice() { }

public void updateDevice(Device device) { }

优先返回空集合

尽量不返回 null,让调用方无需额外判空。

推荐:

java
@NonNull
public List<Device> getDevices() {
    return Collections.emptyList();
}

避免:

java
public List<Device> getDevices() {
    return null;
}

硬编码规范

常量管理

避免硬编码常量分散在代码中,就会导致如果需要修改或调整,必须找到所有的并逐个更改,易造成遗漏和错误。

硬编码的内容也会让代码意图不明确,导致可读性差。

正例:

kotlin
object HttpConfig {
    /** 浏览器及代理服务器缓存有效期:24 小时 */
    const val CACHE_MAX_AGE_SECONDS = 86400
}

反例:

kotlin
// 3个月后没人知道 86400 是什么鬼数字
request.addHeader("Cache-Control", "max-age=86400") 

资源配置管理

以下内容禁止直接写死在代码中:

  • 用户可见文本
  • 颜色
  • 尺寸
  • 动画时长
  • 业务配置项

统一通过资源管理:

  • strings.xml
  • colors.xml
  • dimens.xml
  • config 类

这样既方便国际化,也方便后续主题换肤与业务调整。

AndroidManifest 规范

AndroidManifest.xml 文件是 Android 应用的入口大纲与系统级配置清单。

随着项目业务的迭代、权限、组件和配置信息会持续增加。

如果缺乏统一规范,Manifest 很容易造成配置冲突,加增安全隐患,届时就是一个难以维护的“大杂烩”。

权限分组清晰

权限分组与级次要分明,按照 基础普通权限、运行时敏感权限、系统/签名级特权(需 Root 或签名特许) 进行分块组织,并附带权限注释。

四大组件分块管理

application 节点内部应按照以下顺序组织:

text
Activity
Service
Receiver
Provider

禁止不同类型组件交叉混排。

主入口 Activity 置顶

包含:

xml
<intent-filter>
    <action android:name="android.intent.action.MAIN" />

    <category android:name="android.intent.category.LAUNCHER" />
</intent-filter>

的启动页 Activity 必须放置于 Activity 分组的第一位,便于快速定位应用入口。

标签属性统一排序:

组件标签建议优先保证 android:name 位于属性列表首位。

这样能够在大量组件配置中快速识别目标组件。

同时:

  • 有子节点时使用标准开始/结束标签
  • 无子节点时统一使用 /> 自闭合

推荐:

xml
<service
    android:name=".service.SyncService"
    android:exported="false" />

避免出现空标签闭合形式

xml
<service
    android:name=".service.SyncService"
    android:exported="false">
</service>

完整示例

xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <!-- ==================== 网络权限 ==================== -->

    <!-- 允许应用访问网络 -->
    <uses-permission android:name="android.permission.INTERNET" />

    <!-- 获取当前网络连接状态 -->
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

    <!-- 获取 Wi-Fi 状态信息 -->
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />

    <!-- 修改 Wi-Fi 状态 -->
    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />

    <!-- ==================== 定位权限 ==================== -->

    <!-- 粗略定位,用于 Wi-Fi 扫描等场景 -->
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

    <!-- 精确定位 -->
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

    <!-- ==================== 存储权限 ==================== -->

    <!-- Android 11+ 全文件访问权限 -->
    <uses-permission
        android:name="android.permission.MANAGE_EXTERNAL_STORAGE"
        tools:ignore="ScopedStorage" />

    <!-- Android 10 及以下写存储权限 -->
    <uses-permission
        android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

    <!-- Android 10 及以下读存储权限 -->
    <uses-permission
        android:name="android.permission.READ_EXTERNAL_STORAGE" />

    <!-- ==================== 系统权限 ==================== -->

    <!-- 安装应用 -->
    <uses-permission
        android:name="android.permission.INSTALL_PACKAGES" />

    <!-- 卸载应用 -->
    <uses-permission
        android:name="android.permission.DELETE_PACKAGES" />

    <!-- 多用户环境交互权限 -->
    <uses-permission
        android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />

    <!-- ==================== 其他权限 ==================== -->

    <!-- 开机启动 -->
    <uses-permission
        android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
    
    <!-- 悬浮窗权限 -->
    <uses-permission
        android:name="android.permission.SYSTEM_ALERT_WINDOW" />

    <application
        android:name=".app.AppApplication"
        android:icon="@mipmap/water"
        android:roundIcon="@mipmap/water"
        android:label="@string/app_name"
        android:theme="@style/Theme.AppTheme"
        android:allowBackup="true"
        android:supportsRtl="true"
        android:largeHeap="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
        android:fullBackupContent="@xml/backup_rules"
        android:requestLegacyExternalStorage="true"
        tools:targetApi="n">

        <!-- AutoSize Ui 设计稿 宽高配置 -->
        <meta-data
            android:name="design_width_in_dp"
            android:value="600" />

        <meta-data
            android:name="design_height_in_dp"
            android:value="1024" />

        <!-- ==================== Activity ==================== -->

        <activity
            android:name=".ui.activity.InitActivity"
            android:exported="true"
            android:alwaysRetainTaskState="true"
            android:configChanges="orientation|keyboardHidden|screenSize"
            android:screenOrientation="fullSensor"
            android:theme="@style/AppTheme.NoBarAppTheme.SplashTheme">

            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <activity
            android:name=".ui.activity.MainActivity"
            android:launchMode="singleTask"
            android:screenOrientation="fullSensor"/>

        <activity
            android:name="com.zt.app.water.ui.activity.SensorInfoActivity"
            android:launchMode="singleTop"
            android:screenOrientation="fullSensor"/>

        <!-- ==================== Service ==================== -->

        <!-- TCP 长连接服务 -->
        <service
            android:name=".services.SocketTcpService"
            android:enabled="true"
            android:exported="false" />

        <!-- ==================== Receiver ==================== -->

        <!-- 开机启动广播 -->
        <receiver
            android:name=".receivers.DeviceBootReceiver"
            android:exported="false">
            <intent-filter>
                <action android:name="android.intent.action.BOOT_COMPLETED" />
            </intent-filter>
        </receiver>
    </application>
</manifest>

总结

代码最终是写给人看的,顺便给机器执行。

规范的价值不在于统一格式,而在于降低理解成本、维护成本和沟通成本。

基于 MIT 许可发布