Flutter项目改名改包名换图标完整指南与避坑手册

发布时间:2026/10/5 3:23:19
Flutter项目改名改包名换图标完整指南与避坑手册
先直接说结论Flutter 项目的改名、改包名、换图标说难不难说简单也真没那么简单。我见过太多人在这一套流程上栽跟头不是改完 Android 崩溃就是 iOS 上架被拒要么就是桌面图标死活不更新。这篇教程我尽量把 Android 和 iOS 两端一次性讲透从原理到操作从命令到排查尽量让你照着走一遍就能干净利落地搞定。先说清楚一个概念很多人一开始就搞混了App 名称、包名、图标是三件独立的事但它们又互相牵连。改名只动配置文件改包名要动 Gradle、目录和源码 import 语句换图标则要处理一堆不同尺寸的切图。再加上 Flutter 本身还有一层缓存机制你改完不清理构建缓存经常会出现“明明改了却不生效”的诡异情况。1. 动手前的整体认知这三件事到底在改什么1.1 三个概念分清楚后面少走弯路先花两分钟把概念理清这是我每次带新人必讲的部分。App 名称是用户在桌面和任务管理器中看到的名字它定义在 Android 的AndroidManifest.xml和 iOS 的Info.plist里。包名是应用的唯一标识Android 叫applicationIdiOS 叫Bundle Identifier这个标识决定了你在应用商店的唯一身份也影响支付、推送、分享等第三方 SDK 的配置。应用图标则是展示给用户的视觉入口Android 需要一堆不同密度的mipmap文件iOS 需要一个AppIcon.appiconset目录里的多张尺寸图。这三者修改的优先级不一样。名称和图标属于“显示层”改完重装就能看到。包名属于“标识层”不仅项目里到处有引用还牵涉到第三方服务后台的配置。我把最常被忽略的一点先提出来修改包名时Android 端要区分namespace和applicationId这两个字段职责完全不同。namespace决定 R 类资源索引类和 BuildConfig 的生成位置它必须匹配实际源码目录结构否则编译直接报错。applicationId才是应用市场的唯一标识对应安装后的包名它可以直接改不影响目录结构。很多教程只说改applicationId结果你改了之后确实能装但源码里的import com.xxx.MainActivity会飘红因为namespace没跟着改。新旧教程混用还会遇到另一个问题新版 Flutter 模板里package字段和namespace是分开声明的老文章里让你改的那一行可能根本不存在。所以动手前先看清自己项目根目录android/app/build.gradle的实际内容别照搬网上代码。1.2 先备份再动手三个文件加一个目录既然要改配置就一定要有“改了能回滚”的底气。我的习惯是动手前先复制一份完整的工程目录如果项目不大直接压缩成 zip 丢一边。重点记住这四处容易被改坏的地方android/app/src/main/AndroidManifest.xmlAndroid 名称与图标入口android/app/build.gradleAndroid 包名配置ios/Runner/Info.plistiOS 名称配置ios/Runner.xcodeproj/project.pbxprojiOS 包名配置另外图标还涉及android/app/src/main/res/下的多个mipmap-*文件夹以及ios/Runner/Assets.xcassets/AppIcon.appiconset/目录。备份时最好把这两个资源目录也带上。不要觉得麻烦我见过有人改了applicationId后第三方登录直接不能用回滚时一脸懵就是因为没备份。修改前备份这是所有配置类操作的第一原则。2. 修改 App 名称只改两处但有一处最容易被忽略2.1 Android 端修改 label注意桌面显示名与任务名Android 的应用名称通常由AndroidManifest.xml中application节点的android:label属性控制。默认情况下这个值是string/app_name也就是引用res/values/strings.xml里定义的字符串。直接改android:label我的应用也能生效但我不推荐这么做因为很多库和系统 UI 组件也会引用app_name字符串你把它改死后面维护就是灾难。我的做法是改strings.xml里的string nameapp_name值。这里有个细节要提醒android:label不仅影响桌面图标下的文字也影响任务列表、系统设置里的应用名称。你如果只想改桌面显示名可以单独建一个values/strings.xml之外的值但普通场景下直接改app_name最稳妥。另一个容易被忽略的点如果你的项目适配了 Android 12API 31及以上系统会把应用图标连同名称一起放进主题环境里某些厂商的 ROM 还会缓存名称 24 小时以上。所以我建议测试时不要只看桌面还要去“设置 - 应用管理”里确认新名称是否生效以及让测试机反复重启两次再下结论。2.2 iOS 端修改 CFBundleDisplayNameiOS 的名称分两个字段CFBundleDisplayName显示名称桌面图标下的名字和CFBundleName短名称系统内部使用一般建议不超过 15 个字符。多数情况下你只需要改Info.plist里的CFBundleDisplayName这个是桌面显示名。注意Info.plist里可能没有这个字段需要手动添加一行keyCFBundleDisplayName/key string我的应用/string修改之后 iOS 模拟器上经常不生效原因一会儿在“常见问题”里细说这里只提醒一句真机测试时桌面名称的更新取决于系统对 App 缓存的处理删除 App 重装是最干净的验证方式。CFBundleName一般不推荐乱动因为有些第三方库会拿它做 keychain 存取服务标识改了容易引发数据错乱。2.3 顺着名称修改顺手把启动页名称也查一遍很多人改了桌面名称结果启动页还是旧名字或者 iOS 上多任务切换时显示的标题没变。这不是 bug而是没改全。Android 启动页如果自定义了 Splash Screen通常需要同步修改styles.xml里windowSplashScreenBrandingImage相关配置但最直接的还是依赖android:label。iOS 的启动页如果想显示名称需要用LaunchScreen.storyboard里的 Label 控件这个不会自动跟随CFBundleDisplayName。这部分的“最全”体验就是提醒你不要只盯着一个文件。改完名称后我建议全局搜索项目里的旧名称关键词比如在 Android Studio 里按Shift Shift全局搜把出现在AndroidManifest.xml、Info.plist、启动页配置、甚至README里的旧名字都顺手替换掉。避免以后自己看都分不清哪个是新的。3. 修改包名Android 和 iOS 两套思路一处都别漏3.1 Android 包名applicationId 与 namespace 必须同时协调Android 端包名要分成两条线来看。第一条是applicationId这个字段在android/app/build.gradle中defaultConfig { applicationId com.example.oldname ... }applicationId决定安装包的唯一标识改它不需要动源码目录。但如果你改完之后源码里的MainActivity路径还是com/example/oldname并且你的namespace还指向旧路径编译就会提示找不到 R 类或 BuildConfig。所以我给出的标准步骤是先改build.gradle里的namespace为新的完整包名比如com.mycompany.myapp。同步修改applicationId如果想商店标识与源码结构一致就都改成同一个。在 Android Studio 的 Project 视图中找到android/app/src/main/java/com/example/oldname目录右键 Refactor - Rename把目录结构改成新的包名路径。全局搜索所有import com.example.oldname手动替换为import com.mycompany.myapp尤其是MainActivity.kt和MainApplication.kt里的代码。修改后执行一次flutter clean再flutter run避免旧构建缓存干扰。这里有个关键点不要在纯文本层面全局替换最好用 IDE 的 Refactor 功能这会把注释、字符串里的包名一并处理而且会同步更新目录结构。纯文本全局替换很容易漏掉pubspec.yaml里某些插件自动生成的类文件路径导致编译期玄学报错。3.2 Android 包名修改完毕后立刻验证三件事改完包名不能只看“能编译”还要验证卸载旧包再安装。注意改了包名后应用在系统眼里就是“另一个应用”直接覆盖安装会保留旧数据和旧图标缓存。我一般会手动卸载旧的再flutter run安装新的。第三方服务后台同步更新。很多 Flutter 项目接了推送、统计、地图等 SDK后台白名单里填的还是旧包名。如果只是本地测试还好一旦要上线务必去各个后台如极光、友盟、高德等把包名改成新值。签名配置的包名是否一致。如果你的build.gradle里配置了signingConfigs并且某些渠道要求包名与签名证书绑定那么改完包名后最好用apksigner检查一下 APK 的实际包名防止市场后台校验对不上。如果项目里接了 Firebase那就更要注意google-services.json里的package_name字段必须与新的applicationId一致否则启动直接崩。这个坑我踩过一次改完包名后只要一调 Firebase 方法就报Default FirebaseApp is not initialized排查半天才发现是配置文件里的包名没同步。3.3 iOS 包名Bundle Identifier 与项目配置同步修改iOS 的包名在 Xcode 中主要看Runnertarget 的Bundle Identifier。修改路径是用 Xcode 打开ios/Runner.xcworkspace选中 Runner target在Signing Capabilities面板里改Bundle Identifier。这里强烈建议不要直接手改project.pbxproj虽然网上有些教程教你在文本编辑器里全局替换但稍有不慎就会导致签名配置损坏。Xcode 图形界面操作是官方路径安全可靠。如果你的 Bundle Identifier 从com.example.old改成com.mycompany.newapp那么还要检查Info.plist里的键是否有一处关联。大多数模板里没有显式写 Bundle Identifier它通过PRODUCT_BUNDLE_IDENTIFIER环境变量注入所以你只需要在 Xcode 里改 target 配置即可。但要注意如果项目里某些第三方文件硬编码了旧的 bundle id比如GoogleService-Info.plist也要一并替换。iOS 端改完包名后如果涉及推送和支付还要去 Apple Developer 后台更新 App ID。这里有个容易忽略的操作flutter run时如果是用--dirty或未完全清理的构建很多时候 Xcode 会把旧签名信息带上导致真机安装后包名还是旧的。所以 iOS 端改完包名我建议退出 Xcode删除/ios/Pods和DerivedData缓存再重新pod install并构建这样最干净。3.4 包名命名规范别给自己挖坑说到底包名不是随便起的。Android 的applicationId必须以字母开头包含字母数字和下划线每一段不能以数字开头。iOS 的 Bundle Identifier 只能包含字母、数字、连字符和句点。这里我个人的建议是统一用公司域名的反写形式例如com.company.appname尽量全小写不要用下划线。iOS 的标识里允许下划线但某些第三方库的模块名对下划线支持不好容易在终端编译时出现符号解析异常。另外包名一旦发布到应用商店尤其是 iOS 的 Bundle ID之后再改非常痛苦。因为证书、描述文件、推送配置都和它绑定。所以在前期定义项目包名时一定确认好再动手不要在开发中途频繁更换否则你会在各种第三方后台来回改配置心态都会搞崩。4. 修改应用图标一套图解决双端最实用的工具链实操4.1 图标资源的目录结构与 Android 自适应图标Android 图标目录在android/app/src/main/res/下常见的有mipmap-mdpi、mipmap-hdpi、mipmap-xhdpi、mipmap-xxhdpi、mipmap-xxxhdpi对应不同屏幕密度。每个目录下默认放张ic_launcher.png。如果你只是简单改图标可以准备一张 1024x1024 的源图然后分别缩放到mdpi48x48hdpi72x72xhdpi96x96xxhdpi144x144xxxhdpi192x192但这里必须提另一个坑Android 8.0API 26以上引入了自适应图标系统会要求同时提供前景图层和背景图层。如果你的项目里mipmap-anydpi-v26目录下有ic_launcher.xml它引用了drawable/ic_launcher_foreground和color/ic_launcher_background那你只替换ic_launcher.png是没用的因为系统实际显示的是自适应图层组合出来的效果。要么把前景 PNG 和背景色一并改成自己的设计要么直接删掉mipmap-anydpi-v26下的 XML让系统回退到普通ic_launcher.png。我建议的做法是直接设计一张带有安全边距的前景图中央 66% 区域放你的 logo 主体和背景色然后统一配置。自适应图标在桌面上会被系统裁成圆形或圆角矩形如果你把 logo 顶到边缘成品效果会很差。这也是很多人“明明替换了图标桌面看起来还是旧图”的原因之一。4.2 iOS 图标目录AppIcon.appiconset 全尺寸对照iOS 的图标目录在ios/Runner/Assets.xcassets/AppIcon.appiconset/里面默认有Contents.json和一堆Icon-App-*.png文件。iOS 的图标要求比 Android 严格得多不允许 Alpha 通道不能带透明区域否则上传 App Store 会被拒绝。尺寸对照大概是文件名尺寸Icon-App-20x201x20x20Icon-App-20x202x40x40Icon-App-20x203x60x60Icon-App-29x291x29x29Icon-App-29x292x58x58Icon-App-29x293x87x87Icon-App-40x402x80x80Icon-App-60x602x120x120Icon-App-60x603x180x180Icon-App-76x762x152x152Icon-App-83.5x83.52x167x167Icon-App-1024x10241x1024x1024商店必需手工切这么一堆图很枯燥好在 Flutter 生态里有个现成的工具flutter_launcher_icons可以一条命令给你把两个平台的图全部搞定。我强烈推荐用这个没必要自己一张张用 PS 切效率低还容易切错尺寸。4.3 flutter_launcher_icons 实战一条命令生成全平台图标sflutter_launcher_icons的用法其实很简单。先在项目根目录的pubspec.yaml里添加依赖然后配置好图标源文件和参数dev_dependencies: flutter_launcher_icons: ^0.13.1 flutter_launcher_icons: android: true ios: true image_path: assets/icon/app_icon.png adaptive_icon_background: #FFFFFF adaptive_icon_foreground: assets/icon/app_icon_foreground.png min_sdk_android: 21配置里image_path是主图标源文件建议用 1024x1024 的 PNG。adaptive_icon_background和adaptive_icon_foreground是 Android 自适应图标专用的配置前者填背景色值后者填前景图路径。如果不想单独准备前景图也可以直接不配置这两个字段工具会用image_path自动生成一套兼容方案。然后在终端执行flutter pub get dart run flutter_launcher_icons执行完毕后工具会自动覆盖android/app/src/main/res/下所有ic_launcher.png包括mipmap-anydpi-v26下的 XML 配置如果检测到以及 iOS 的AppIcon.appiconset里的全部图标文件。这个方法是我用过最省事的实测比手动切图快很多但注意生成完必须清理 Flutter 缓存再运行否则 Flutter 引擎可能会复用旧图标。4.4 图标替换后仍显示旧图是为什么你会遇到改了图标但桌面还是旧图的情况不要慌这基本和 Flutter 本身的缓存机制有关。flutter run默认会复用构建缓存而 Android 的桌面图标还会缓存应用图标到系统服务里。我的排查顺序是先执行flutter clean删除build/与.dart_tool/下缓存。再执行flutter pub get重新生成插件注册信息。卸载设备上的旧应用注意是卸载不是覆盖安装。重新flutter run --release或打 debug 包安装。如果依然显示旧图标再检查AndroidManifest.xml里android:icon指向的资源名称确认是mipmap/ic_launcher而不是旧的drawable/xxx。iOS 端则需要删除 App 再重装Xcode 的 DerivedData 缓存也要清掉。4.5 图标避坑透明通道、系统版本差异、圆角裁切做图标设计时最大的坑就是透明通道。Android 的启动图标最好也不要带全透明背景否则在某些 ROM 上会显示黑色或灰色方块。iOS 更是严格App Store 审核时若图标含有透明像素会被直接拒绝。所以我每次处理源图都会先用工具把透明区域压平为纯色背景。另一个坑是系统版本差异。Android 8.0 以下用的是传统方形图标8.0 以上是自适应圆形/圆角矩型裁切iOS 从 iOS 7 开始就一直采用圆角矩形裁切但你的源图本身不要故意画圆角因为系统会再套一层遮罩你画了圆角反而出现一圈透明边。总之提供一张铺满画面的正方形源图就行把边距交给系统处理。5. 常见问题与排查技巧实录改完之后的那些玄学报错5.1 “You are applying Flutters main Gradle plugin imperatively” 是什么情况这个报错在两三年以上的 Flutter 项目升级或重新构建时特别常见。它本质上是说你没有采用新版 Flutter Gradle 插件的声明式配置方式还在用老式的apply plugin写法。出现这个提示时通常项目还能编译但升级依赖后会越来越不稳定。如果你在改包名的过程中顺手升级了 Gradle 或 AGP 版本这个 Warning 大概率会冒出来。处理方法不复杂打开android/settings.gradle确保包含以下格式plugins { id dev.flutter.flutter-plugin-loader version 1.0.0 id com.android.application version 8.1.0 apply false id org.jetbrains.kotlin.android version 1.8.22 apply false }然后在android/app/build.gradle里改成plugins { id com.android.application id kotlin-android id dev.flutter.flutter-gradle-plugin }并且删除掉原来的apply plugin: com.android.application之类的手动应用语句。这里我特别提醒不要复制网上旧帖子的配置直接覆盖要先对比自己项目的 Gradle 版本号AGP 和 Kotlin 插件的版本要与项目兼容否则会出现更蛋疼的依赖冲突问题。5.2 改完包名后 e/flutter (31173) 报错是什么热搜词里有“e/flutter (31173): [ERROR:flutter/runtime/dart_vm_initializer.cc(41)] unhandled”之类的报错。这个报错在 Flutter 里很常见它只是一段异常捕获的输出代表 Dart 层有未处理的错误被抛出真正的错误信息往往在它下面。改包名后如果你遇到这个可以先往下翻找到The following NoSuchMethodError was thrown或者PlatformException那一段。结合我的经验改完包名后最容易出现这类未处理异常的原因有二。一是第三方插件里硬编码了旧包名尤其是和系统通道MethodChannel相关的插件它们在原生层注册时以包名作为一部分标识重新构建时找不到就抛异常。二是MainActivity的onCreate里如果有引用getPackageName()的逻辑返回的新包名和你某个本地资源目录不一致导致状态恢复失败。排查时可以先用全局搜索查一下插件源码里是否有旧包名残留在项目根目录执行grep -r com.example.oldname . --include*.kt --include*.java --include*.swift --include*.m --include*.dart把所有命中结果统一替换为新包名再重新 clean 一次几乎都能解决。如果项目中接入了需要原生回调的 SDK比如支付宝或微信支付这步尤其关键因为这些 SDK 会在 manifest 里校验包名和 AppID 是否匹配。5.3 新建项目后跑不起来多半是 Gradle 下载与 JDK 版本问题“flutter 新建项目后跑不起来”也是热门问题。新建项目本身就带有默认包名与默认图标按说不会出问题但很多情况下因为本机 Gradle 缓存不完整首次构建需要下载大量依赖网络不稳就会卡死。这时终端会停在Running Gradle task assembleDebug长时间没反应或者在flutter pub get阶段报无法连接 pub.dev。我的建议是如果创建项目后第一次运行就失败先别急着改配置。依次检查本机 Java 版本是否符合要求新版 Flutter 通常要求 JDK 17用java -version查看。Android Studio 自带的 JBR 是否被系统 PATH 里的老 JDK 抢占。全局镜像源的配置确保 Flutter SDK 能正常拉取依赖。android/gradle/wrapper/gradle-wrapper.properties里的 distributionUrl 是否能访问必要时手动换到稳定版本再有下载问题可以用其他网络环境多试几次。还有一点尽量保证 Android SDK 平台和 build-tools 已按项目所需提前装好。很多时候“新建项目跑不起来”只是在编译时找不到platforms;android-34之类的资源。在 Android Studio 的 SDK Manager 里把常用版本的 SDK Platform 都装上能省很多事。5.4 修改包名时千万不要全局替换所有 com.example有些教程会让你在 Android Studio 里Edit - Find - Replace in Files把com.example全局替换成新包名这个操作极其危险。因为 Flutter 插件生成的临时代码中也有大量com.example路径引用你全部替换后会导致插件原生代码无法解析。正确的做法是只替换android/app/src/main/java目录下、属于你自己的源码以及android/app/build.gradle里的配置项。如果不得不用Find in Path也要限定在android/app/src/main范围内不要选整个 project。另外还要提醒不要在pubspec.yaml里改name字段作为包名。Flutter 项目根目录的name: your_app是 Dart 包名主要影响 import 路径它与 Android/iOS 的应用包名完全无关。有些人为了“彻底改名”把这个字段也改了结果不仅是所有import语句要跟着改还容易导致插件注册名错乱。老老实实只动原生配置即可pubspec.yaml的包名保持稳定。5.5 iOS 端改名后模拟器依旧显示旧名称这个问题几乎每周都有人问。模拟器上 App 图标的名称更新需要系统重建图标缓存而你改了Info.plist后如果只是重新flutter run模拟器未必会刷新。最可靠的方法flutter clean rm -rf ~/Library/Developer/Xcode/DerivedData flutter run如果还不行就删除模拟器上的 App然后长按图标等它抖动再按删除。有时候系统缓存非常顽固重启模拟器也是有效手段之一。真机上也类似删除 App 后建议关机重启一次手机再装确保LaunchServices刷新。5.6 图标生成后 Android 上出现拉伸或适配问题如果用了flutter_launcher_icons且图标看起来被拉伸了大概率是adaptive_icon_foreground的尺寸不对。自适应图标的前景图层按 108x108 dp 设计但实际安全区域只有中央 66x66 dp也就是 66% 的区域。如果你的前景图填满了整个 108dp桌面上显示时边缘会被裁掉看起来就像“图标被放大了”。解决方式有两种。一是设计前景图时把 logo 元素放在中央 60% 以内四周留出安全边距。二是直接不提供adaptive_icon_foreground只给image_path让工具自动生成适合没有特殊意义的简单图形。iOS 端如果显示出来图标被拉伸多半是源图不是正方形记住一定用 1:1 的源图这是最低要求。6. 修改完成后必须做的事验证清单和收尾细节6.1 验证清单从安装到第三方 SDK 全部过一遍改完名称、包名、图标后别急着发布先按这份清单过一遍桌面名称是否正确显示新名称任务列表是否同步更新。设置中的应用列表里应用名称与包名是否为新值。确认 Android 端applicationId与 iOS 端Bundle Identifier都是新值。安装包中图标在默认桌面和系统设置界面都显示正常。使用第三方登录、支付、推送功能确认回调正常。连接adb shell执行pm list packages | grep 新包名确认包名真实生效。如果发布到应用商店提前在后台把应用描述里的旧包名和旧图标一并替换。第 5 条容易被忽略但它恰恰是我见过翻车最多的地方。很多插件把包名作为文件存储路径的一部分改了包名后共享偏好、数据库路径全部变了用户升级后表现为“登录状态丢失”。所以你测试时最好用一台开发机模拟覆盖安装场景看看升级后数据是否受影响。6.2 清理构建残留clean 命令是万能的吗flutter clean能解决大部分配置残留问题但并非万能。如果你改了AndroidManifest.xml里的android:label后桌面名称一直不变那可能是桌面 Launcher 的应用信息缓存和项目代码无关。此时就算你 clean 十次也没用要删除应用重新安装才能刷新。iOS 的 Xcode 构建还会缓存Info.plist的编译结果建议同时删除~/Library/Developer/Xcode/DerivedData再重建。另外flutter clean会把ios/Pods、.dart_tool等目录删除下次构建要重新下载依赖网络环境不好时非常痛苦。改图标这种纯资源替换有时候不 clean 也能生效只要删除旧的build输出目录里对应资源即可。我一般这样操作改配置类文件时用flutter clean改资源类文件时只执行flutter run --release让它强制全量构建。6.3 一个小技巧把改名的动作做成脚本如果你需要频繁在不同项目间复制这套改名的流程或者团队内经常有新同事要处理这个问题我建议你把核心操作写成一个简单的 Shell 脚本。原理就是批量替换AndroidManifest.xml、build.gradle、Info.plist里的关键字段再调用flutter_launcher_icons生成图标。当然脚本没办法处理源码目录重构那一步但配置层面的重复劳动完全可以自动化。这个脚本我写过一个简化版核心思路是用sed替换字符串但每次运行前都要备份原项目并且限定在android/与ios/目录内搜索替换。写脚本的时候有几个小坑macOS的sed -i语法和 Linux 不一样Info.plist是plist格式用sed改之前要确保CFBundleDisplayName键存在Gradle 文件里对换行敏感不能把整行替换写坏。如果嫌脚本麻烦也至少把步骤写成团队内部文档真的能省不少沟通成本。6.4 上线前的最后一道防线检查产物中的包名与图标flutter build打出来的 APK 或 IPA 一定能反应你改完的配置吗不一定。APK 可以用aapt工具验证命令是aapt dump badging app-release.apk | grep package aapt dump badging app-release.apk | grep application-label这个命令会直接输出 APK 里的包名和默认 label用来验证最直观。IPA 则用 Xcode 的 Organizer 里查。发布前跑一下这些校验可以避免“本地明明改了产物里却是旧信息”的诡异事故。最后再说一个我自己踩过多次的经验千万不要在发布前的最后一天才来做这三件事。名称、包名、图标的修改虽然看起来只是替换资源但因为它牵涉到签名、第三方 SDK、系统缓存所以很容易在细节上出问题。每次改完我至少留出半天时间专门测试第三方登录和推送这部分出问题的概率比你想的高得多。把这些工作当作常规流程的一部分每次建立新项目时一开始就把名称、包名、图标定好后续省下的时间远比多写的这几行配置值钱。