Android干货整理AndroidAndroid

Android 代码规范大全

2021-01-18  本文已影响0人  nanchen2251

前言

虽然我们项目的代码时间并不长,也没经过太多人手,但代码的规范性依然堪忧,目前存在较多的比较自由的「代码规范」,这非常不利于项目的维护,代码可读性也不够高,

此外,客户端和后端的研发模式也完全不同,后端研发基本都是基于 SOA 思想的,通常一个子系统 3 个人一起维护就已经是很充分的人力了,更多时候就是 1 个主力 + 1 个 backup 的人力配置。

而客户端却完全不同,大家的代码都是相互交叉的,一个模块的代码可能要经历数十人的蹂躏,所以形成一个一致的开发规范迫在眉睫。

为什么需要一致的代码规范?

核心还是减少沟通成本,提升我们的 Code Review 效率,让我们的代码更加易于维护。此外,一个一致的代码规范可以造成更少的 bug,也就意味着更节省时间和金钱。

当然,规范是约定的,本系列文字全是笔者多年来博采众长,积累而成,所以有任何不同意见,欢迎评论拍砖。

1. Android 的工具规范

工欲善其事,必先利其器。

由于 Android 基本都基于 Android Studio 进行开发,所以工具规范全部以 Android Studio 为前提。

  1. 必须使用最新的稳定版本的 Android Studio 进行开发;
  2. 编码格式必须统一为 UTF-8;
  3. 删除多余的 import,减少警告出现,可利用 AS 的 Optimize Imports(Settings -> Keymap -> Optimize Imports)快捷键,设置自己的喜好。


  4. 编辑完 .java、.kt、.xml 等文件后必须格式化(需要在设置好以下几点的前提下)

Reformat Code 的必要性,一定需要保证 IDE 配置一致为前提,尽可能贴切于 Android Studio 默认。
强烈建议对于比较长的老代码局部格式化,不全局格式化

Kotlin Code Style XML Code Style

以上几处设置完毕,其他采用 Android Studio 默认方式,再进行 Reformat Code 快捷键即可。


Reformat Code 快捷键设置

2. Android 的分包规范

前面强调了工具的统一配置,再利用 Android Studio 本身的功能便可把代码风格变得一致。接下来就带来第二部分:Android 的分包规范。

对于分包,我们需要达成一致,我们采用 PBF 方式,不推荐使用 PBL 方式。

PBF(按功能分包 Package By Feature)
PBL(按层分包 Package By Layer)

PBF 可能不是很好区分在哪个功能中,不过也比 PBL 要好找很多,且 PBF 与 PBL 相比较有如下优势:

3. Android 的命名规范

代码中的命名严禁使用拼音与英文混合的方式,更不允许直接使用中文的方式。正确的英文拼写和语法可以让阅读者易于理解,避免歧义。

注意:即使纯拼音命名方式也要避免采用。但国际通用的名称,可视同英文,比如 toutiaodouyin 等。

3.1 包名

Android 里面有 package 的概念,所以需要约定一下包名命名规范。

包名全部小写,不允许出现中文、大写字母或者下划线,前面为子模块命名,再根据 PBF 方式进行命名。

3.2 类名

类名都以 UpperCamelCase 风格编写。

类名通常是名词或名词短语,接口名称有时可能是形容词或形容词短语。现在还没有特定的规则或行之有效的约定来命名注解类型。

名词,采用大驼峰命名法,尽量避免缩写,除非该缩写是众所周知的, 比如 HTML、URL,如果类名称中包含单词缩写,则单词缩写的每个字母均应大写。

描述 例如
Activity 模块名 + Activity 闪屏页类 SplashActivity
Fragment 模块名 + Fragment 主页类 HomeFragment
Service 模块名 + Service 时间服务 TimeService
BroadcastReceiver 功能名 + Receiver 推送接收 JPushReceiver
ContentProvider 功能名 + Provider ShareProvider
自定义 View 功能名 + View/ViewGroup(组件名称) ShapeButton
Dialog对话框 功能名+Dialog ImagePickerDialog
Adapter 模块名 + Adapter 课程详情适配器 LessonDetailAdapter
解析类 功能名 + Parser 首页解析类 HomePosterParser
工具方法类 功能名 + UtilsManager 线程池管理类:ThreadPoolManager
日志工具类:LogUtilsLogger 也可)
打印工具类:PrinterUtils
数据库类 功能名 + DBHelper 新闻数据库:NewsDBHelper
自定义的共享基础类 Base + 基础 BaseActivity, BaseFragment
抽象类 Base / Abstract 开头 AbstractLogin
异常类 Exception 结尾 LoginException
接口 able / ible 结尾 / I 开头 Runnable, AccessibleILoginView

测试类的命名以它要测试的类的名称开始,以 Test 结束。例如:HashTestHashIntegrationTest

接口(interface):命名规则与类一样采用大驼峰命名法,多以 able 或 ible 结尾,如 interface Runnableinterface Accessible

注意:如果项目采用 MVP,所有 Model、View、Presenter 的接口都以 I 为前缀,不加后缀,其他的接口采用上述命名规则。

3.3 方法名

方法名都以 lowerCamelCase 风格编写。

方法名通常是动词或动词短语。

方法 说明
initXX() 初始化相关方法,使用 init 为前缀标识,如初始化布局 initView()
isXX(), checkXX() 方法返回值为 boolean 型的请使用 is/check 为前缀标识
getXX() 返回某个值的方法,使用 get 为前缀标识
setXX() 设置某个属性值
handleXX(), processXX() 对数据进行处理的方法
displayXX(), showXX() 弹出提示框和提示信息,使用 display/show 为前缀标识
updateXX() 更新数据
saveXX(), insertXX() 保存或插入数据
resetXX() 重置数据
clearXX() 清除数据
removeXX(), deleteXX() 移除数据或者视图等,如 removeView()
drawXX() 绘制数据或效果相关的,使用 draw 前缀标识

3.4 变量命名

这里的变量为广义的变量,包括了常量、局部变量、全局变量等,它们的基础规则是:

在具体的变量命名时,会根据该变量的类型不同而附加额外的命名规则:

类型 说明 例如
常量 大写 & 下划线隔开,Kotlin 一定要 const val const val TYPE_NORMAL = 1
static final TYPE_NORMAL = 1
临时变量名 整型:ijkmn ,字符型一般用 cde for(int i = 0;i < len; i++)
其他变量 lowerCamelCase 风格即可,私有变量也不要使用 m 开头 private int tmp;
Kotlin 只读变量使用 val,可变变量使用 var,尽可能使用 val var tmp = 0
val defaultIndex = 0

3.5 资源命名

Android 的资源包括:


资源文件命名为全部小写,采用下划线命名法。

3.5.1 动画资源文件(anim/ 和 animator/)

安卓主要包含属性动画和视图动画,其视图动画包括补间动画和逐帧动画。属性动画文件需要放在 res/animator/ 目录下,视图动画文件需放在 res/anim/ 目录下。命名规则:{模块名_}逻辑名称

说明:{} 中的内容为可选,逻辑名称 可由多个单词加下划线组成。例如:refresh_progress.xmlmarket_cart_add.xmlmarket_cart_remove.xml

如果是普通的补间动画或者属性动画,可采用:动画类型_方向 的命名方式。

例如:

名称 说明
fade_in 淡入
fade_out 淡出
push_down_in 从下方推入
push_down_out 从下方推出
push_left 推向左方
slide_in_from_top 从头部滑动进入
zoom_enter 变形进入
slide_in 滑动进入
shrink_to_middle 中间缩小

3.5.2 颜色资源文件(color/)

color/ 是专门用于存放颜色相关资源的文件夹。命名规则:类型{_模块名}_逻辑名称

说明:{} 中的内容为可选。例如:sel_btn_font.xml

颜色资源也可以放于 res/drawable/ 目录,引用时则用 @drawable 来引用,但不推荐这么做,最好还是把两者分开。

3.5.3 图片资源文件(drawable/ 和 mipmap/)

res/drawable/ 目录下放的是位图文件(.png、.9.png、.jpg、.gif)或编译为可绘制对象资源子类型的 XML 文件,而 res/mipmap/ 目录下放的是不同密度的启动图标,所以 res/mipmap/ 只用于存放启动图标,其余图片资源文件都应该放到 res/drawable/ 目录下。

命名规则:类型{_模块名}_逻辑名称类型{_模块名}_颜色

说明:{} 中的内容为可选;类型 可以是可绘制对象资源类型,也可以是控件类型最后可加后缀 _small 表示小图,_big 表示大图。

例如:

名称 说明
btn_main_about.png 主页关于按键 类型_模块名_逻辑名称
btn_back.png 返回按键 类型_逻辑名称
divider_maket_white.png 商城白色分割线 类型_模块名_颜色
ic_edit.png 编辑图标 类型_逻辑名称
bg_main.png 主页背景 类型_逻辑名称
btn_red.png 红色按键 类型_颜色
btn_red_big.png 红色大按键 类型_颜色
ic_avatar_small.png 小头像图标 类型_逻辑名称
bg_input.png 输入框背景 类型_逻辑名称
divider_white.png 白色分割线 类型_颜色
bg_main_head.png 主页头部背景 类型_模块名_逻辑名称
def_search_cell.png 搜索页面默认单元图片 类型_模块名_逻辑名称
ic_more_help.png 更多帮助图标 类型_逻辑名称
divider_list_line.png 列表分割线 类型_逻辑名称
sel_search_ok.xml 搜索界面确认选择器 类型_模块名_逻辑名称
shape_music_ring.xml 音乐界面环形形状 类型_模块名_逻辑名称

如果有多种形态,如按钮选择器:sel_btn_xx.xml,采用如下命名:

名称 说明
sel_btn_xx 作用在 btn_xx 上的 selector
btn_xx_normal 默认状态效果
btn_xx_pressed state_pressed 点击效果
btn_xx_focused state_focused 聚焦效果
btn_xx_disabled state_enabled 不可用效果
btn_xx_checked state_checked 选中效果
btn_xx_selected state_selected 选中效果
btn_xx_hovered state_hovered 悬停效果
btn_xx_checkable state_checkable 可选效果
btn_xx_activated state_activated 激活效果
btn_xx_window_focused state_window_focused 窗口聚焦效果

注意:使用 Android Studio 的插件 SelectorChapek 可以快速生成 selector,前提是命名要规范。

3.5.4 布局资源文件(layout/)

命名规则:类型_模块名{模块名_}类型_逻辑名称。(也采用 PBF,方便查看,尤其在大项目中)

说明:{} 中的内容为可选。

例如:

类型 名称 说明
Activity main_activity.xml 主窗体 模块名_类型
Fragment music_fragment.xml 音乐片段 模块名_类型
Dialog loading_dialog.xml 加载对话框 逻辑名称_类型
PopupWindow info_ppw.xml 信息弹窗(PopupWindow) 逻辑名称_类型
adapter 的列表项 main_song_item.xml 主页歌曲列表项 模块名_类型_逻辑名称

3.5.5 布局资源 id 命名

命名规则:{模块名_}_逻辑名_view 缩写(功能),例如: main_search_btnback_btn。此外,采用 Kotlinx 直接获取布局文件的时候,id 命名采用驼峰样式。

说明:{} 中的内容为可选。参考 GoogleSamples Demo:https://github.com/android/architecture-samples

例如:

类型 规范 命名示例
TextView xxx_text user_login_text
EditText xxx_edit user_login_edit
ImageView xxx_iv user_login_iv
Button xxx_btn user_login_btn
CheckBox xxx_cb user_login_cb
GridView xxx_gv user_login_gv
ListView xxx_lv user_login_lv
RecyclerView xxx_rv user_login_rv
RadioButton xxx_rb user_login_rb
LinearLayout xxx_ll user_login_ll
RelativeLayout xxx_rl user_login_rl
FrameLayout xxx_fl user_login_fl
GridLayout xxx_gl user_login_gl
ConstraintLayout xxx_cl user_login_cl

3.5.6 菜单资源文件(menu/)

菜单相关的资源文件应放在该目录下。命名规则:{模块名_}逻辑名称

说明:{} 中的内容为可选。例如:main_drawer.xmlnavigation.xml

3.5.7 colors.xml

<color>name 命名使用下划线命名法,在你的 colors.xml 文件中应该只是映射颜色的名称一个 ARGB 值,而没有其它的。不要使用它为不同的按钮来定义 ARGB 值。

例如,不要像下面这样做:

  <resources>
      <color name="button_foreground">#FFFFFF</color>
      <color name="button_background">#2A91BD</color>
      <color name="comment_background_inactive">#5F5F5F</color>
      <color name="comment_background_active">#939393</color>
      <color name="comment_foreground">#FFFFFF</color>
      <color name="comment_foreground_important">#FF9D2F</color>
      ...
      <color name="comment_shadow">#323232</color>

使用这种格式,会非常容易重复定义 ARGB 值,而且如果应用要改变基色的话会非常困难。同时,这些定义是跟一些环境关联起来的,如 button 或者 comment,应该放到一个按钮风格中,而不是在 colors.xml 文件中。

相反,应该这样做:

  <resources>

      <!-- grayscale -->
      <color name="white"     >#FFFFFF</color>
      <color name="gray_light">#DBDBDB</color>
      <color name="gray"      >#939393</color>
      <color name="gray_dark" >#5F5F5F</color>
      <color name="black"     >#323232</color>

      <!-- basic colors -->
      <color name="green">#27D34D</color>
      <color name="blue">#2A91BD</color>
      <color name="orange">#FF9D2F</color>
      <color name="red">#FF432F</color>

  </resources>

向应用设计者那里要这个调色板,名称不需要跟 "green""blue" 等等相同。"brand_primary""brand_secondary""brand_negative" 这样的名字也是完全可以接受的。像这样规范的颜色很容易修改或重构,会使应用一共使用了多少种不同的颜色变得非常清晰。通常一个具有审美价值的 UI 来说,减少使用颜色的种类是非常重要的。

注意:如果某些颜色和主题有关,那就单独写一个 colors_theme.xml

3.5.8 strings.xml

<string>name 命名使用下划线命名法,采用以下规则:{模块名_}逻辑名称,这样方便同一个界面的所有 string 都放到一起,方便查找。

名称 说明
main_menu_about 主菜单按键文字
friend_title 好友模块标题栏
friend_dialog_del 好友删除提示
login_check_email 登录验证
dialog_title 弹出框标题
button_ok 确认键
loading 加载文字

3.5.9 styles.xml

<style>name 命名使用大驼峰命名法,几乎每个项目都需要适当的使用 styles.xml 文件,因为对于一个视图来说,有一个重复的外观是很常见的,将所有的外观细节属性(colorspaddingfont)放在 styles.xml 文件中。 在应用中对于大多数文本内容,最起码你应该有一个通用的 styles.xml 文件,例如:

<style name="ContentText">
    <item name="android:textSize">@dimen/font_normal</item>
    <item name="android:textColor">@color/basic_black</item>
</style>

应用到 TextView 中:

<TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@string/price"
    style="@style/ContentText"/>

或许你需要为按钮控件做同样的事情,不要停止在那里,将一组相关的和重复 android:xxxx 的属性放到一个通用的 <style> 中。

4. Android 的注释规范

4.1 类注释

每个类完成后应该有作者姓名和联系方式的注释,对自己的代码负责。

/**
 * <pre>
 *     author : nanchen
 *     e-mail : xxx@xx
 *     time   : 2021/01/18
 *     desc   : xxxx 描述
 *     version: 1.0
 * </pre>
 */
public class WelcomeActivity {
    ...
}

具体可以在 AS 中自己配制,进入 Settings -> Editor -> File and Code Templates -> Includes -> File Header,输入

/**
 * <pre>
 *     author : ${USER}
 *     e-mail : xxx@xx
 *     time   : ${YEAR}/${MONTH}/${DAY}
 *     desc   :
 *     version: 1.0
 * </pre>
 */

这样便可在每次新建类的时候自动加上该头注释。

4.2 方法注释

每一个成员方法(包括自定义成员方法、覆盖方法、属性方法)的方法头都必须做方法头注释,在方法前一行输入 /** + 回车 或者设置 Fix doc comment(Settings -> Keymap -> Fix doc comment)快捷键,AS 便会帮你生成模板,我们只需要补全参数即可,如下所示。@param , @return , @throws , @deprecated 这 4 种标记出现的时候,描述都不能为空。当描述无法在一行中容纳,连续行至少需要再缩进 4 个空格。

    /**
     * Report an accessibility action to this view's parents for delegated processing.
     *
     * <p>Implementations of {@link #performAccessibilityAction(int, Bundle)} may internally
     * call this method to delegate an accessibility action to a supporting parent. If the parent
     * returns true from its
     * {@link ViewParent#onNestedPrePerformAccessibilityAction(View, int, android.os.Bundle)}
     * method this method will return true to signify that the action was consumed.</p>
     *
     * <p>This method is useful for implementing nested scrolling child views. If
     * {@link #isNestedScrollingEnabled()} returns true and the action is a scrolling action
     * a custom view implementation may invoke this method to allow a parent to consume the
     * scroll first. If this method returns true the custom view should skip its own scrolling
     * behavior.</p>
     *
     * @param action Accessibility action to delegate
     * @param arguments Optional action arguments
     * @return true if the action was consumed by a parent
     */
    public boolean dispatchNestedPrePerformAccessibilityAction(int action, Bundle arguments) {
        for (ViewParent p = getParent(); p != null; p = p.getParent()) {
            if (p.onNestedPrePerformAccessibilityAction(this, action, arguments)) {
                return true;
            }
        }
        return false;
    }

4.3 块注释

块注释与其周围的代码在同一缩进级别。它们可以是 /* ... */ 风格,也可以是 // ... 风格(// 后最好带一个空格)。对于多行的 /* ... */ 注释,后续行必须从 * 开始, 并且与前一行的 * 对齐。以下示例注释都是 OK 的。

/*
 * This is okay.
 */

// And so
// is this.

/* Or you can
* even do this. */

注释不要封闭在由星号或其它字符绘制的框架里。

Tip:在写多行注释时,如果你希望在必要时能重新换行(即注释像段落风格一样),那么使用
/* ... */

比如:


4.4 全局变量的注释

全局变量的注释样式如下(注意注释之间有空格):

/**
 * The next available accessibility id.
 */
private static int nextAccessibilityViewId;

/**
 * The animation currently associated with this view.
 */
protected Animation currentAnimation = null;

4.5 其他一些注释

AS 已帮你集成了一些注释模板,我们只需要直接使用即可,在代码中输入 TODOFIXME 等这些注释模板,回车后便会出现如下注释。

// TODO: 17/3/14 需要实现,但目前还未实现的功能的说明
// FIXME: 17/3/14 需要修正,甚至代码是错误的,不能工作,需要修复的说明

4.5 注释必须遵守的规范

4.5.1 不言自明的方法不要加注释。

比如 Item getItem(int index) 是一段自说明的代码,我们可以直接从方法的命名就能知道它是干嘛的,所以不需要增加注释。

4.5.2 提测的代码不应该有 TODO 这样的注释

5. 代码样式规范

5.1 使用标准大括号样式

左大括号不单独占一行,与其前面的代码位于同一行:

class MyClass {
    int func() {
        if (something) {
            // ...
        } else if (somethingElse) {
            // ...
        } else {
            // ...
        }
    }
}

我们需要在条件语句周围添加大括号。例外情况:如果整个条件语句(条件和主体)适合放在同一行,那么您可以(但不是必须)将其全部放在一行上。例如,我们接受以下样式:

if (condition) {
    body();
}

同样也接受以下样式:

if (condition) body();

但不接受以下样式:

if (condition)
    body();  // bad!

5.2 编写简短方法

在可行的情况下,尽量编写短小精炼的方法。我们了解,有些情况下较长的方法是恰当的,因此对方法的代码长度没有做出硬性限制。如果某个方法的代码超出 40 行,请考虑是否可以在不破坏程序结构的前提下对其拆解。

5.3 类成员的顺序

这并没有唯一的正确解决方案,但如果都使用一致的顺序将会提高代码的可读性,推荐使用如下排序:

  1. 常量(Kotlin 伴生对象放在开头)
  2. 字段
  3. 构造函数
  4. 重写函数和回调
  5. 公有函数
  6. 私有函数
  7. 内部类或接口

例如:

public class MainActivity extends Activity {

    private static final String TAG = MainActivity.class.getSimpleName();

    private String mTitle;
    private TextView mTextViewTitle;

    @Override
    public void onCreate() {
        ...
    }

    public void setTitle(String title) {
        mTitle = title;
    }

    private void setUpView() {
        ...
    }

    static class AnInnerClass {

    }
}

如果类继承于 Android 组件(例如 ActivityFragment),那么把重写函数按照他们的生命周期进行排序是一个非常好的习惯,例如,Activity 实现了 onCreate()onDestroy()onPause()onResume(),它的正确排序如下所示:

public class MainActivity extends Activity {
    //Order matches Activity lifecycle
    @Override
    public void onCreate() {}

    @Override
    public void onResume() {}

    @Override
    public void onPause() {}

    @Override
    public void onDestroy() {}
}

5.4 函数参数的排序

在 Android 开发过程中,Context 在函数参数中是再常见不过的了,我们最好把 Context 作为其第一个参数。

正相反,我们把回调接口应该作为其最后一个参数。

例如:

// Context always goes first
public User loadUser(Context context, int userId);

// Callbacks always go last
public void loadUserAsync(Context context, int userId, UserCallback callback);

5.5 字符串常量的命名和值

Android SDK 中的很多类都用到了键值对函数,比如 SharedPreferencesBundleIntent,所以,即便是一个小应用,我们最终也不得不编写大量的字符串常量。

当时用到这些类的时候,我们 必须 将它们的键定义为 static final 字段,并遵循以下指示作为前缀。

字段名前缀
SharedPreferences PREF_
Bundle BUNDLE_
Fragment Arguments ARGUMENT_
Intent Extra EXTRA_
Intent Action ACTION_

说明:虽然 Fragment.getArguments() 得到的也是 Bundle ,但因为这是 Bundle 的常用用法,所以特意为此定义一个不同的前缀。

例如:

// 注意:字段的值与名称相同以避免重复问题
static final String PREF_EMAIL = "PREF_EMAIL";
static final String BUNDLE_AGE = "BUNDLE_AGE";
static final String ARGUMENT_USER_ID = "ARGUMENT_USER_ID";

// 与意图相关的项使用完整的包名作为值的前缀
static final String EXTRA_SURNAME = "com.myapp.extras.EXTRA_SURNAME";
static final String ACTION_OPEN_USER = "com.myapp.action.ACTION_OPEN_USER";

5.6 行长限制

代码中每一行文本的长度都应该不超过 160 个字符。虽然关于此规则存在很多争论,但最终决定仍是以 160 个字符为上限,如果行长超过了 160(AS 窗口右侧的竖线就是设置的行宽末尾 ),我们通常有两种方法来缩减行长。

不过存在以下例外情况:

5.6.1 换行策略

这没有一个准确的解决方案来决定如何换行,通常不同的解决方案都是有效的,但是有一些规则可以应用于常见的情况。

5.6.1.1 操作符的换行

除赋值操作符之外,我们把换行符放在操作符之前,例如:

int longName = anotherVeryLongVariable + anEvenLongerOne - thisRidiculousLongOne
        + theFinalOne;

赋值操作符的换行我们放在其后,例如:

int longName =
        anotherVeryLongVariable + anEvenLongerOne - thisRidiculousLongOne + theFinalOne;

5.6.1.2 函数链的换行

当同一行中调用多个函数时(比如使用构建器时),对每个函数的调用应该在新的一行中,我们把换行符插入在 . 之前。

例如:

Picasso.with(context).load("https://blankj.com/images/avatar.jpg").into(ivAvatar);

我们应该使用如下规则:

Picasso.with(context)
        .load("https://blankj.com/images/avatar.jpg")
        .into(ivAvatar);

5.6.1.3 多参数的换行

当一个方法有很多参数或者参数很长的时候,我们应该在每个 , 后面进行换行。

比如:

loadPicture(context, "https://blankj.com/images/avatar.jpg", ivAvatar, "Avatar of the user", clickListener);

我们应该使用如下规则:

loadPicture(context,
        "https://blankj.com/images/avatar.jpg",
        ivAvatar,
        "Avatar of the user",
        clickListener);

5.6.1.4 RxJava 链式的换行

RxJava 的每个操作符都需要换新行,并且把换行符插入在 . 之前。

例如:

public Observable<Location> syncLocations() {
    return mDatabaseHelper.getAllLocations()
            .concatMap(new Func1<Location, Observable<? extends Location>>() {
                @Override
                 public Observable<? extends Location> call(Location location) {
                     return mRetrofitService.getLocation(location.id);
                 }
            })
            .retry(new Func2<Integer, Throwable, Boolean>() {
                 @Override
                 public Boolean call(Integer numRetries, Throwable throwable) {
                     return throwable instanceof RetrofitError;
                 }
            });
}

参考文档:
https://source.android.com/source/code-style?hl=zh-cn
https://developer.android.com/kotlin/style-guide?hl=zh-cn
https://github.com/Blankj/AndroidStandardDevelop

上一篇下一篇

猜你喜欢

热点阅读