Android开发代码规范

一. 命名

  1. 基本的命名规则:遵循驼峰命名法(小驼峰或大驼峰),除了需要全部大写或全部小写的情况以外。
  2. 代码中的命名均不能以下划线或美元符号开始,也不能以下划线或美元符号结束。
  3. 代码中的命名严禁使用拼音与英文混合的方式,更不允许直接使用中文的方式,固有名词除外。
  4. 类名必须使用 UpperCamelCase 风格(大驼峰命名法),固有名词除外。Android系统组件的,必须以系统组件名称为结尾,如xxxActivity.java、xxxFragment.java、xxxService.java、xxxAdapter.java,MVP中presenter为xxxPersenter,涉及所有Model、View、Presenter的接口都以I为前缀。
  5. 全局变量按照驼峰规则命名,首字母以m开头(非静态变量)
  6. 方法名、参数名、成员变量、局部变量必须统一使用 lowerCamelCase 风格(小驼峰命名法),固有名词除外。
  7. 局部变量尽量避免单个字符如a, b,i,j, 除非是临时变量循环变量在for循环中使用。
  8. 常量命名全部大写,单词间用下划线隔开,力求语义表达完整清楚,不要嫌名字长。
  9. 抽象类命名使用 Abstract(Abs) 或 Base 开头。
  10. 包名统一使用小写,点分隔符之间有且仅有一个自然语义的英语单词。包名统一使用单数形式,但是类名如果有复数含义,类名可以使用复数形式。包名命名规则为com.公司名.项目名.模块名,如果是以组件化或插件化的方式开发,单个模块或单个插件中的子包划分按照PBL(按层分包Package By Layer)。
  11. 杜绝完全不规范或非公认的缩写,避免望文不知义。
  12. 资源文件的命名以及资源名称统一以小写单词+下划线的方式命名,anim、color、dimen、string等命名格式为:资源类型_模块名_逻辑名称/业务功能
  13. drawable资源的命名,以对应的用途开头,如分割线以divider_开头,图标以ic_开头、背景图以bg_开头,按钮以btn_开头,选择器状态以对应状态结尾,如xxx_normal、xxx_pressed、xxx_selected等。如果UI设计提供多套资源图标则必须按照mdpi :hdpi:xhdpi:xxhdpi:xxxhdpi = 2:3:4:6:8的比例放入对应的drawable文件夹中。
  14. layout布局文件,Activity、Fragment、Dialog等命名格式为activity/frgament/dialog_模块名_逻辑名称/业务功能, include的布局文件统一以layout_开头, layout_模块名_xxx, 列表项的item布局统一以item_开头, item_模块名_xxx
  15. 布局文件中涉及到string、color、dimen等的,需要提取到对应的xml文件中,以@资源的形式引用
  16. 控件id的命名格式为,控件缩写+下划线+逻辑名,常见控件缩写表:
控件缩写
LinearLayoutll
RelativeLayoutrl
FrameLayoutfl
ConstraintLayoutcl
ListViewlv
GridViewgv
ScollViewsv
RecyclerViewrv
TextViewtv
EditTextet
Buttonbtn
ImageViewiv
CheckBoxcb
RadioButtonrb
RadioGrouprg
WebViewwv

其它控件的缩写推荐使用小写字母并用下划线进行分割,例如:ProgressBar 对应的缩写为progress_bar;DatePicker 对应的缩写为date_picker。
17 . 代码中涉及到控件的命名,规则为【控件逻辑名称】+【控件名称】,即跟布局文件中id的命名顺序反过来。其中控件名称可以用缩写,但是不能缩写到两个字母,如xxxImageView可以缩写为xxxImage, 不能缩写为xxxIv, xxxEditText可以缩写为xxxEdit, 不能缩写为xxxEt。

二. 常量

  1. 超过一次以上引用的魔法值(共享值),不允许直接出现在代码中。(即跨方法、跨类、跨页面等用到魔法值必须共享常量)
  2. 在 long 或者 Long 赋值时,数值后使用大写的 L,不能是小写的 l,小写容易跟数字1 混淆,造成误解。
  3. 代码中不允许直接出现中文字符串,使用string资源引用或者常量代替。

三. 代码格式

  1. Android Studio/Eclispse编写的代码必须进行代码格式化(包括 .java、.xml文件,使用快捷键按照默认风格)。
  2. 大括号的使用约定。非空代码块则:
    1) 左大括号前不换行。
    2) 左大括号后换行。
    3) 右大括号前换行。
    4) 右大括号后还有 else 等代码则不换行;表示终止的右大括号后必须换行。
  3. 左小括号和字符之间不能出现空格;同样,右小括号和字符之间也不能出现空格;而左大括号前需要空格。 if (a == b)
  4. if/for/while/switch/do 等保留字与括号之间都必须加空格。
  5. 任何二目、三目运算符的左右两边都必须需要加一个空格。(包括赋值运算符=、逻辑运算符&&、加减乘除符号等。)
  6. 单行代码过长时,必须换行(在Android Studio中以编辑面板的右侧竖线为换行标志),换行时遵循如下原则:
    1) 第二行相对第一行缩进 4 个空格,从第三行开始,保持与第二行对齐。
    2) 运算符与下文一起换行。
    3) 方法调用的点符号与下文一起换行。
    4) 方法调用中的多个参数需要换行时,在逗号后进行。
    5) 在括号前不要换行。
  7. 方法参数在定义和传入时,多个参数逗号后边必须加空格。
  8. 一行一个语句,每个语句后要换行。
  9. 每次只声明一个变量,不要推荐使用组合声明,比如int a, b;。如非必须,变量在需要的时候才去声明并初始化。
  10. 方法参数不宜过多,如果方法参数太多,函数声明方法名会很长,可以考虑用实体类来封装参数。
  11. 方法中的行数不宜过多,一个方法中代码超过80行时,必须进行方法提取(refactor)

四. OOP规约

  1. 必须用类名来访问此类的静态变量或静态方法,避免通过类的对象变量去引用,无谓增加编译器解析成本。
  2. 所有的覆写方法,必须加@Override 注解。
  3. 只有相同参数类型并且相同业务含义的,才可以使用 Java 的可变参数,可变参数类型避免使用 Object。可变参数必须放置在参数列表的最后。
  4. 外部正在调用或者二方库依赖的接口,不允许修改方法签名,避免对接口调用方产生影响。接口过时必须加@Deprecated 注解,并清晰地说明采用的新接口或者新服务是什么。
  5. 必须使用常量或确定值的对象来调用equals,避免空指针异常。
  6. 所有相同类型的对象之间值相等的比较,全部使用 equals 方法比较,不能用==(除非是判断相同对象引用或基本数据类型除外)。
  7. 构造方法里面禁止加入任何业务逻辑,如果有初始化逻辑,请放在 init 方法中。
  8. 通过用 String.split(xxx); 方法得到的数组,使用前必须做长度检查,否则会有抛 IndexOutOfBoundsException 的风险。
  9. 避免在getter/setter 方法中增加复杂的业务逻辑,增加排查问题的难度。
  10. 循环体内String的拼接,必须使用 StringBuilder 的 append方式。
  11. 不允许被修改的类、对象、方法必须添加final关键字。
  12. 避免出现重复的代码(Don’t Repeat Yourself),即 DRY 原则。(避免不停的复制黏贴代码,使用OO思想提取重复代码)
  13. 方法或构造函数中传递过多的参数时,请使用对象封装。如set(ParamWrap param)
  14. 把一个基本数据类型转为字符串时,一律使用String.valueOf(数据) 的方式,包装类型可使用数据类型.toString(),避免使用数据 + ""的方式

五. 集合处理

  1. 关于 hashCode 和 equals 的处理,遵循如下规则:
    1) 只要重写 equals,就必须重写 hashCode。
    2) 因为 Set 存储的是不重复的对象,依据 hashCode 和 equals 进行判断,所以 Set 存储的对象必须重写这两个方法。
  2. 使用集合转数组的方法,必须使用集合的 toArray(T[] array),传入的参数必须是类型完全一样的数组(否则强转数组类型时将出现 ClassCastException),并且大小就是 list.size()。
  3. 使用 Arrays.asList()把数组转换成集合时,不能对集合使用其修改的相关方法, 调用add/remove/clear 等方法会抛出 UnsupportedOperationException 异常。
  4. 泛型通配符<? extends T>来接收返回的数据,此写法的泛型集合不能使用 add 方法,而<? super T>不能使用 get 方法,作为接口调用赋值时易出错。(第一、频繁往外读取内容的,适合用<? extends T>。第二、经常往里插入的,适合用<? super T>)
  5. 不能在 foreach 循环里进行元素的 remove/add 操作。remove 元素请使用 Iterator方式,如果并发操作,需要对 Iterator 对象加锁。
  6. 在 JDK7 版本及以上,Comparator 实现类要满足如下三个条件,不然 Arrays.sort,Collections.sort 会报 IllegalArgumentException 异常。
    说明:三个条件如下
    1) x,y 的比较结果和 y,x 的比较结果相反。
    2) x>y,y>z,则 x>z。
    3) x=y,则 x,z 比较结果和 y,z 比较结果相同。
  7. 可以对集合进行快速去重操作时,避免使用 List 的 contains 方法进行遍历、对比、去重操作,可以利用 Set 元素唯一的特性。
  8. 必须高度注意 Map 类集合 K/V 能不能存储 null 值的情况,如下表格:
集合类KeyValueSuper说明
Hashtable不允许为null不允许为nullDictionary线程安全
ConcurrentHashMap不允许为null不允许为nullAbstractMap锁分段技术(JDK8:CAS)
TreeMap不允许为null允许为nullAbstractMap线程不安全
HashMap允许为null允许为nullAbstractMap线程不安全

由于 HashMap 的干扰,很多人认为 ConcurrentHashMap 是可以置入 null 值,而事实上,存储 null 值时会抛出 NPE 异常。

六. 控制语句

  1. 在一个 switch 块内,每个 case 要么通过 break/return 等来终止,要么注释说明程序将继续执行到哪一个 case 为止;在一个 switch 块内,都必须包含一个 default 语句并且放在最后,即使空代码。
  2. 在 if/else/for/while/do 语句中必须使用大括号。即使只有一行代码,避免采用单行的编码方式:if (condition) statements;
  3. 如果非得使用if()…else if()…else…方式表达逻辑,避免超过 3 层, 以至于后续代码维护困难。(可以使用卫语句、策略模式、状态模式等来实现)
  4. 不要在if/while等条件判断中执行其它复杂的语句,将复杂逻辑判断的结果赋值给一个有意义的布尔变量名,以提高可读性。
  5. 不要在for循环体中执行 try-catch 操作, 如有需要放在外层。
  6. 避免采用取反逻辑运算符。如 if (!isOk)
  7. 在for循环之前, 必须对集合变量进行判空和lengh检查,避免出现NPE或IndexOutBounds异常。

七. 注释规约

  1. 类、类属性、类方法的注释必须使用 Javadoc 规范,必须使用/*内容/格式,不得使用// xxx方式。
  2. 方法内部单行注释,必须在被注释语句上方另起一行,使用//注释。避免使用行尾注释。方法内部多行注释使用/* */注释,注意与代码对齐。
  3. 所有的抽象方法(包括接口中的方法)必须要用 Javadoc 注释说进行说明含义。
  4. 所有的类都必须添加创建者和创建日期,类的说明。(可以用AS快捷键或模板生成)
  5. 无用的、过时的注释必须删除,避免维护代码时造成阅读歧义和困惑。
  6. 反常规、反常识的代码以及核心业务逻辑,必须添加代码注释。
  7. 注释掉的代码,可以直接删除,如果保留,则需要注释说明是干什么的。
  8. 方法的代码修改时,如果该方法之前有注释,则也要对注释进行相应的修改,尤其是参数、返回值、异常、核心逻辑等的修改。
  9. 如果是临时处理或者打算留待将来处理的,必须添加TODO标记备忘,并且在上线提测之前检查工程中的TODO是否已经全部完成(十分重要)。如果是多人协作开发的项目,TODO后面必须添加作者。如果是错误,不能工作的代码,或者目前难以修复的bug,必须用FIXME来注释。

八. 异常处理

  1. 注意防止 NPE,是程序员的基本修养。注意 NPE 产生的场景:
    1)使用全局对象变量和方法传递进来的对象变量之前,必须进行判空操作。
    2)返回类型为基本数据类型,return 包装数据类型的对象时,自动拆箱有可能产生 NPE。 反例:public int f() { return Integer 对象}, 如果为 null,自动解箱抛 NPE。
    3) 数据库的查询结果可能为 null。
    4) 即使集合是 isNotEmpty,集合里取出的数据元素也可能为 null。
    5) 调用一个其他方法返回的对象时,一律要求进行空指针判断,防止 NPE。
    6) 级联调用 obj.getA().getB().getC();一连串调用,易产生 NPE。
    7) 注意可控与不可控场景,如当前方法内new出来的对象内容是可控的,而接口返回的数据解析生成的对象是不可控,将NPE的重点放在不可控的对象上。
  2. 方法的返回值为 null的,必须添加@Nullable注释,方法参数同样需要添加。
  3. catch 时请分清稳定代码和非稳定代码,稳定代码指的是无论如何不会出错的代码。对于非稳定代码的 catch 尽可能进行区分异常类型,再做对应的异常处理。对大段代码进行 try-catch,使程序无法根据不同的异常做出正确的应激反应,也不利于定位问题,这是一种不负责任的表现。
  4. 捕获异常是为了处理它,不要捕获了却什么都不处理而抛弃之(至少要添加Log输出)。如果不想处理它,请将该异常抛给它的调用者。最外层的业务使用者,必须处理异常,将其转化为用户可以理解的内容。
  5. 有 try 块放到了事务代码中,catch 异常后,如果需要回滚事务,一定要注意手动回滚事务。
  6. finally 块必须对资源对象、流对象进行关闭,有异常也要做 try-catch。(如果 JDK7 及以上,可以使用 try-with-resources 方式。)
  7. 不要在 finally 块中使用 return。
  8. 捕获异常与抛异常,必须是完全匹配,或者捕获异常是抛异常的父类。
  9. 传递资源id的方法,必须用Android注释类,标明参数id类型,如setText(@ResId id), 否则有异常风险。
  10. log输出必须添加TAG, TAG不能为空,方便根据tag定位问题。

九. Android基本组件

  1. Activity 间的数据通信,对于数据量比较大的,避免使用Intent + Parcelable的方式,以免造成TransactionTooLargeException,可以考虑其他替代方案。
  2. Activity 间通过隐式Intent 的跳转,在发出Intent 之前必须通过resolveActivity检查,避免找不到合适的调用组件,造成ActivityNotFoundException 的异常。
  3. 避免在Service#onStartCommand()/onBind()方法中执行耗时操作,如果确实有需求,应改用IntentService 或采用其他异步机制完成。
  4. 避免在BroadcastReceiver#onReceive() 中执行耗时操作,如果有耗时工作,应该创建 IntentService 完成,而不应该在BroadcastReceiver 内创建子线程去做。
  5. 避免使用隐式Intent 广播敏感信息,信息可能被其他注册了对应BroadcastReceiver 的App 接收。
  6. 如非必须,避免使用嵌套的Fragment。
  7. Service 需要以多线程来并发处理多个启动请求,建议使用IntentService,可避免各种复杂的设置。
  8. 对于只用于应用内的广播,优先使用LocalBroadcastManager 来进行注册和发送,LocalBroadcastManager 安全性更好,同时拥有更高的运行效率。
  9. 当前Activity 的onPause 方法执行结束后才会创建(onCreate)或恢复(onRestart)别的Activity,所以在onPause 方法中不适合做耗时较长的工作,这会影响到页面之间的跳转效率。
  10. Activity 或者Fragment 中动态注册BroadCastReceiver 时,registerReceiver() 和 unregisterReceiver() 要成对出现。一般为onCreate/onDestroy或onResume/onPause。
  11. Android 基础组件如果使用隐式调用,应在 AndroidManifest.xml 中使用 或在代码中使用 IntentFilter 增加过滤。

十. UI 与布局

  1. 布局中不得不使用ViewGroup 多重嵌套时,不要使用LinearLayout 嵌套,改用RelativeLayout,可以有效降低嵌套数。
  2. 源文件统一采用UTF-8 的形式进行编码。
  3. 禁止在非UI 线程进行View 相关操作。
  4. TextView等文本大小使用sp,View 宽高间距大小使用单位dp。
  5. 禁止在设计布局时多次为子View 和父View 设置同样背景进而造成页面过度绘制,推荐将不需要显示的布局进行及时隐藏。
  6. 尽量不要使用AnimationDrawable展示过多的图片资源(如几十个),它在初始化的时候就将所有图片加载到内存中,特别占内存,并且还不能释放,释放之后下次进入再次加载时会报错。
  7. 不能使用ScrollView 包裹 ListView/GridView/ExpandableListVIew; 因为这样会把ListView 的所有Item 都加载到内存中,要消耗巨大的内存和cpu 去绘制图面。
  8. 使用Adapter 的时候,如果你使用了ViewHolder 做缓存,在 getView() 的方法中无论这项convertView 的每个子控件是否需要设置属性(比如某个TextView 设置的文本可能为null,某个按钮的背景色为透明,某控件的颜色为透明等),都需要为其显式设置属性(Textview 的文本为空也需要设置 setText(“”),背景透明也需要设置),否则在滑动的过程中,因为adapter item 复用的原因,会出现内容的显示错乱。
  9. 灵活使用布局,推荐merge、ViewStub 来优化布局,尽可能多的减少UI布局层级,推荐使用FrameLayout,LinearLayout、RelativeLayout 次之。

十一. 进程、线程与消息通信

  1. 获取单例对象需要保证线程安全,其中的方法也要保证线程安全。
  2. 不允许在应用中自行显式创建线程(如new Thread().start()), 必须通过线程池提供(AsyncTask 或者ThreadPoolExecutor 或者其他形式自定义的线程池工具类),并且要限制最大线程数,减少系统资源的消耗。创建线程池避免使用Executors 去创建,而是通过 ThreadPoolExecutor 的方式。
  3. 对多个资源、数据库表、对象同时加锁时,需要保持一致的加锁顺序,否则可能会造成死锁。(线程一需要对表 A、B、C 依次全部加锁后才可以进行更新操作,那么线程二的加锁顺序也必须是 A、B、C,否则可能出现死锁。)
  4. 并发环境下,单例模式避免使用双重检查锁(double-checked locking)实现延迟初始化的方式, 可以采用静态内部类的方式实现。
  5. 在高并发场景中,避免使用”等于”判断作为中断或退出的条件。
  6. 不要通过Intent 在Android 基础组件之间传递大数据(binder transaction 缓存为1MB),可能导致 OOM。
  7. 在Application 的业务初始化代码加入进程判断,确保只在自己需要的进程初始化。特别是后台进程减少不必要的业务初始化。
  8. 子线程中不能更新界面,更新界面必须在主线程中进行,网络操作不能在主线程中调用。
  9. 谨慎使用Android 的多进程,多进程虽然能够降低主进程的内存压力,但会遇到如下问题:
    1)首次进入新启动进程的页面时会有延时的现象(有可能黑屏、白屏几秒,是白屏还是黑屏和新Activity 的主题有关);
    2)应用内多进程时,Application 实例化多次,需要考虑各个模块是否都需要在所有进程中初始化。

十二. 文件与数据库

  1. 任何时候不要硬编码文件路径,请使用Android 文件系统API 访问。
  2. 当使用外部存储时,必须检查外部存储的可用性。
  3. 应用间共享文件时,不要通过放宽文件系统权限的方式去实现,而应使用FileProvider。
  4. SharedPreference 中只能存储简单数据类型(int、boolean、String 等),复杂数据类型建议使用文件、数据库等其他方式存储。
  5. 数据库Cursor 必须确保使用完后关闭,以免内存泄漏。可以使用JDK1.7推荐的try-with-resource语法简化操作。
  6. 多线程操作写入数据库时,需要使用事务,以免出现同步问题。
  7. 执行SQL 语句时,应使用SQLiteDatabase#insert()、update()、delete(),不要使用SQLiteDatabase#execSQL(),以免SQL 注入风险。
  8. 如果ContentProvider 管理的数据存储在 SQL 数据库中,应该避免将不受信任的外部数据直接拼接在原始 SQL 语句中。推荐使用selectionWhere和selectionArgs搭配。

十三. Bitmap、Drawable 与动画

  1. 当你不得不手动去加载一张图片到内存中去展示的时候,一定要注意图片的宽高尺寸,不管来源是sd卡、网络还是应用内置的背景图片,因为图片的宽高和格式决定了它的内存占用大小,避免OOM,推荐使用Glide等第三方图片加载库。
  2. 展示大图之前,必须进行尺寸压缩,应根据实际展示需要,压缩图片尺寸,而不是直接显示原图。手机屏幕比较小,直接显示原图,并不会增加视觉上的收益,但是却会耗费大量内存并易导致OOM。
  3. 网络传输图片之前必须进行质量/体积压缩,以减少流量,加快上传速度,推荐使用三方库压缩处理。
  4. 加载大图片或者一次性加载多张图片,应该在异步线程中进行。图片的加载,涉及到 IO 操作,以及CPU 密集操作,很可能引起卡顿。
  5. 在ListView,ViewPager,RecyclerView,GirdView 等组件中使用图片时,应做好图片的缓存,避免始终持有图片导致内存溢出,也避免重复创建图片,引起性能问题。建议使用Fresco、Glide等图片库。
  6. png 图片使用TinyPNG 或者类似工具压缩处理,减少包体积。(尤其针对UI设计师提供过来的图标资源)
  7. 使用 RGB_565 代替 RGB_888,在不怎么降低视觉效果的前提下,减少内存占用。
  8. 谨慎使用 gif 图片,注意限制每个页面允许同时播放的 gif 图片,以及单个 gif 图片的大小。
  9. 在 Activity#onPause() 或 Activity#onStop() 回调中,关闭当前 activity 正在执行的的动画。
  10. 在动画或者其他异步任务结束时,应该考虑回调时刻的环境是否还支持业务处理。例如Activity 的 onStop() 函数已经执行,且在该函数中主动释放了资源,此时回调中如果不做判断就会空指针崩溃。
  11. 在有强依赖 onAnimationEnd 回调的交互时,如动画播放完毕才能操作页面, onAnimationEnd 可能会因各种异常没被回调建议加上超时保护或通过 postDelay 替代 onAnimationEnd。
  12. 当View Animation 执行结束时,调用 View.clearAnimation() 释放相关资源。

十四. 安全

  1. 将 android:allowbackup 属性必须设置为false,阻止应用数据被导出。
  2. 确保应用发布版本的 android:debuggable 属性设置为 false。
  3. 不要把敏感信息打印到 log 中,发布版必须关闭开发模式的log输出,log工具类必须设置开关。
  4. 在SDK 支持的情况下,Android 应用必须使用 V2 签名,这将对 APK 文件的修改做更多的保护。
  5. 所有的 Android 基本组件(Activity、Service、BroadcastReceiver、ContentProvider 等)都不应在没有严格权限控制的情况下,将 android:exported 设置为 true。
  6. WebView 应设置 WebView#getSettings()#setAllowFileAccess(false)、
    WebView#getSettings()#setAllowFileAccessFromFileURLs(false)、
    WebView#getSettings()#setAllowUniversalAccessFromFileURLs(false),阻止 filescheme URL 的访问。
  7. 本地加密秘钥不能硬编码在代码中,更不能使用 SharedPreferences 等本地持久化机制存储。应选择 Android 自身的秘钥库(KeyStore)机制或者其他安全性更高的安全解决方案保存。
  8. 在 Android 4.2(API Level 17) 及以上,对安全性要求较高的应用可在Activity中,对 Activity 所关联的 Window 应用WindowManager.LayoutParams.FLAG_SECURE,防止被截屏、录屏。但要注意的是,一个 Activity 关联的 Window 可能不止一个,如果使用了 Dialog / DialogFragment 等控件弹出对话框,它们本身也会创建一个新的 Window,也一样需要保护。
  9. zip 中不要包含 …/…/file 这样的路径,可能被篡改目录结构,造成攻击。
  10. 使用Android 的 AES/DES/DESede 加密算法时,不要使用 ECB 加密模式,应使用 CBC 或 CFB 加密模式。
  11. 应用开启混淆必须十分小心,注意必须keep的类,尤其第三方的库,否则将导致release版本调用异常。
  12. 应用中涉及到so库时,必须注意保证每一个armeabi文件夹中so的数量一致(尤其是多module大量依赖三方的时候),否则将导致应用加载so异常崩溃。如非必要,只保留一个armeabi文件夹。

十五. AS配置

  1. 组件化插件化的方式下,多个module依赖相同的依赖库,依赖库版本必须集中控制,如建立公共的gradle文件来管理。同样compileSdkVersion、minSdkVersion、targetSdkVersion也需要集中控制。
  2. 签名文件、IP Host、一些key或控制开关等尽量使用配置文件,通过gradle去配置,在变更时避免修改代码。高效使用BuildConfig文件可以避免很多麻烦。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值