Android 应用项目工程规范

2641次阅读  |  发布于5年以前

本文档的目的是定义项目规范。这些应遵循整个 Android 项目以帮助我们保持整洁和统一的代码库。 翻译自:project_style_guidelines.md

1. 项目规范

1.1 项目结构

一个完整的项目,应保持下列结构:

    src/androidTest
    src/test
    src/commonTest
    src/main

androidTest - 此目录存放功能测试代码。
test - 此目录存放单元测试代码。
commonTest - 此目录存放单元测试与单元测试的代码集合。
main - 此目录存放主应用代码。

当你修改或者增加新的功能时,都应该遵循上述的目录结构。
采用这种目录结构使我们能够保持从任何测试代码与主应用程序代码分离。

1.2 文件命名

1.2.1 Class 命名

任何类名都应该使用 UpperCamelCase (驼峰法)命名, 例如:

    AndroidActivity, NetworkHelper, UserFragment, PerActivity

任何android组件类都应该把组件名放在最末尾。例如:

    UserFragment, SignUpActivity, RateAppDialog, PushNotificationServer, NumberView

我们使用 UpperCamelCase 方法命名,是因为这样有助于我们单独创建名称类时,使得它更容易阅读。将组件名放在末尾,让使用者能够无比清晰,这个类是什么用途。例如,我们要寻找修改一个叫 RegistrationDialog 的文件,那么通过这个命名就能让我们容易地找到这个类。

1.2.1 Resource 命名

当命名的资源文件,我们应该确保使用小写字母来命名他们,并下划线代替空格,例如:

    activity_main, fragment_user, item_post

这样命名使得我们可以很容易地找到自己要找的具体布局文件。在Android Studio中,资源文件是按字母顺序排序,意味着 Activity,Fragment 和其他类型的布局组合如同分组一样,所以我们知道从什么位置开始寻找一个文件。

1.2.2.1 Drawable 命名

图片文件应该使用 ic_ 做前缀与功能、大小、颜色一起命名。例如,在尺寸为24dp、白色的、“accept”、图标命名为:

    ic_accept_24dp_white

而在48dp、黑色的、“取消”图标则命名为:

    ic_cancel_48dp_black

我们使用这个命名约定,这样一个 drawable 文件则可以通过它的名字识别。如果颜色和大小没有出现在名称中,则开发者需要打开 drawable 文件以查找出这个信息。因此这节省了我们一点点时间。 :)

其他 drawable 文件应该使用相应的前缀,例如:

类型 前缀 例如
Selector selector_ selector_button_cancel
Background bg_ bg_rounded_button
Circle circle_ circle_white
Progress progress_ progress_circle_purple
Divider divider_ divider_grey

与上面 Resource 名件一致,在 Android Studio 中 drawable 也会进行类似分组。这也使得它一目了然。举个反例,当我们命名一个资源 button_cancel ,它可能意味着什么呢?这到底是一个选择资源或圆形按钮的背景?正确的命名有助于清除任何可能出现的歧义。

当创建选择不同点击的 selector state 资源文件时,应使用相应的后缀命名:

State 后缀 举例
Normal _normal btn_accept_normal
Pressed _pressed btn_accept_pressed
Focused _focused btn_accept_focused
Disabled _disabled btn_accept_disabled
Selected _selected btn_accept_selected

使用清晰的后缀,如上述有助于使它明显资源用于选择器(selector)的状态。同时也可以让颜色或其他标识符做为前缀。

1.2.2.2 Layout 命名

在layout命名布局文件时,我们要以对应创建的 Android 组件的名称命名。 例如:

组件名 Class Name Layout Name
Activity MainActivity activity_main
Fragment MainFragment fragment_main
Dialog RateDialog dialog_rate
Widget UserProfileView view_user_profile
AdapterView Item N/A item_follower

Note: 如果使用 merge 标签创建布局则应该使用 layout_ 前缀。

1.2.2.3 Menu 命名

菜单文件不需要将 menu_ 做前缀。这是因为它们都已经在menu包的资源目录中,因此它不做要求。

1.2.2.4 Values 命名

所有的 values 文件下的文件名都应该为复数,例如:

    attrs.xml, strings.xml, styles.xml, colors.xml, dimens.xml

2. 代码规范

2.1 Java 语言规范

2.1.1 不要忽略异常

要避免出现捕获异常,但不处理的情况。 例如:

public void setUserId(String id) {
    try {
        mUserId = Integer.parseInt(id);
    } catch (NumberFormatException e) {
    }
}

当发生异常时,这段代码里没有任何信息提供给开发者与用户,因此这样不易调试,同时也会让用户感到迷惑。我们应当捕获异常,但同时也应该让错误打印到控制台用于调试,如有必要还需给用户提示。例如:

    public void setCount(String count) {
        try {
            count = Integer.parseInt(id);
        } catch (NumberFormatException e) {
            count = 0;
            Log.e(TAG, "There was an error parsing the count " + e);
            DialogFactory.showErrorMessage(R.string.error_message_parsing_count);
        }
    }

在这里我们做了一些适当的处理:

2.1.2 不要轻易使用 Exception 捕获异常

不要轻易使用 Exception 捕获异常 :

    public void openCustomTab(Context context, Uri uri) {
        Intent intent = buildIntent(context, uri);
        try {
            context.startActivity(intent);
        } catch (Exception e) {
            Log.e(TAG, "There was an error opening the custom tab " + e);
        }
    }

为什么?
使用 Exception 捕获异常非常不正确的,因为意味着你从来没有想到(包括如 RuntimeExceptions 中的 ClassCastException 异常)异常会被夹在应用程序级别的错误进行处理。它掩盖了我们处理异常代码的效率,也意味着如果有人添加了一个新的类型,我们在调用代码异常时,IDE 不会帮提示我们需要对这个错误进行不同的处理。而在大多数情况下,我们应该处理这样不同类型的异常。

因此当捕获我们可预知的异常时,我们应该采取相应的措施:

    public void openCustomTab(Context context, Uri uri) {
        Intent intent = buildIntent(context, uri);
        try {
            context.startActivity(intent);
        } catch (ActivityNotFoundException e) {
            Log.e(TAG, "There was an error opening the custom tab " + e);
        }
    }

2.1.3 多组异常

一段代码中,可能会出现多种异常。为了可读性和避免重复代码进行分组。例如,

你可以这么做:

    public void openCustomTab(Context context, @Nullable Uri uri) {
        Intent intent = buildIntent(context, uri);
        try {
            context.startActivity(intent);
        } catch (ActivityNotFoundException e) {
            Log.e(TAG, "There was an error opening the custom tab " + e);
        } catch (NullPointerException e) {
            Log.e(TAG, "There was an error opening the custom tab " + e);
        } catch (SomeOtherException e) {
            // Show some dialog
        }
    }

同样也可以这么做:

    public void openCustomTab(Context context, @Nullable Uri uri) {
        Intent intent = buildIntent(context, uri);
        try {
            context.startActivity(intent);
        } catch (ActivityNotFoundException e | NullPointerException e) {
            Log.e(TAG, "There was an error opening the custom tab " + e);
        } catch (SomeOtherException e) {
            // Show some dialog
        }
    }

2.1.4 全包声明

当我们声明引用时,应用使用全包声明。例如:

错误的用法:

    import android.support.v7.widget.*;

正确的用法:

    import android.support.v7.widget.RecyclerView;

2.1.5 不要保留未使用的引用

有时,我们会删除一些代码,可能意味着一些引用已不再需要。如果是这种情况,那么相应的引用代码也应该被删除。

2.2 java 的 Style 规范

2.2.1 fields 声明 & 命名

所有字段应该在文件的顶部声明,并遵循以下规则:

    private static final int PAGE_COUNT = 0;

不应使用那些没有意义的字段名。例如,

    int e; //列表中的元素数量

为什么不把给该字段有意义的名字摆在首位,而不是加上注释!

    int numberOfElements;

这样就很好啊!

2.2.1.2 View 命名

当我们给视图命名,view 应该放在字段名最后,例如:

View Name
TextView usernameView
Button acceptLoginView
ImageView profileAvatarView
RelativeLayout profileLayout

2.2.2 避免用集合类型命名

我们对集合创建变量时,避免用容器类型名称。例如,假如我们有一个包含用户 id 列表的 ArrayList 集合:

正确的做法:

    List<String> userIds = new ArrayList<>();

错误的做法:

    List<String> userIdList = new ArrayList<>();

因为集合名称很容易变化,这些命名往往被人遗忘 ,它不是完全必要的。

2.2.3 避免类似的命名

当方法或者类的名称太过类似时,会影响其他开发者在阅读你的代码时产生一些混淆。

    hasUserSelectedSingleProfilePreviously

    hasUserSelectedSignedProfilePreviously

这样的代码在乍看之下可能很难理解,不容易区别。使用一个更清晰的方式命名这些,可以使开发人员更容易阅读你的代码。

2.2.4 多个同类型参数的命名

当使用 Android Studio 自动生成代码的我们,很容易留下一些不规则的东西——无实意的命名参数!例如:

    public void doSomething(String s1, String s2, String s3)

阅读代码时我们很难理解这些参数的含义。那么正确的做法是:

    public void doSomething(String userName, String userEmail, String userId)

一目了解。

Copyright© 2013-2019

京ICP备2023019179号-2