Android编码规范

一个项目从开始开发到后期的迭代版本,是多个人共同开发的结果,所以当团队人多起来的时候,编译的规范就显得很重要.这里整理了普遍使用的一些编译规范,希望大家都遵守.

简单说明

Android下的应用程序大部分是基于java语言编写的,所以规范都是按照java的来

Java 样式规则


使用Javadoc 标准注释

每个文件应该在顶部,有版权的声明接着包和导入语句 (由一个空行分隔每个块),最后是类或接口声明.在 Javadoc 注释中,描述类或接口做些什么.

/*
 * Copyright (C) 2016 Globalegrow E-Commerce
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.internal.foo;

import android.os.Blah;
import android.view.Yada;

import java.sql.ResultSet;
import java.sql.SQLException;

/**
 * 做 X 和 Y  还有为 Z 提供一种抽象
 *
 * @author: zhengwu
 * @date: 2016-12-29
 */
public class Foo {
    ...
}

每个类和公共方法,你写必须与至少一个句子描述的类或方法并包含的 Javadoc 注释.这句话应该开始用第三人称描述性动词.

例子:
单行注释

/** 返回一个双精度值的正确圆正平方根. */
static double sqrt(double a) {    ...}

or
多行注释

/**
 * 构造一个新的字符串,通过转换指定的字节数组的
 * 使用平台的默认字符编码. 
*/
public String(byte[] bytes) {    ...}

你不需要为简单的 get 和 set 方法写 Javadoc ,如 setFoo() .如果该方法做更复杂的事物 (如强制约束或有一个重要的副作用),这个时候你必须记录它.

写简短的方法

在可行的情况下,保持方法小而且比较集中.我们认识到,很长的方法有时候是适当的, 所以没有硬性限制放在方法长度.如果一种方法超过 40 行左右,想想是否它可以被分解而不伤害程序的结构。

在标准的地方定义字段

在顶部的文件或紧接使用它们的方法之前定义字段,这里建议在顶部的文件中定义,这样方便查找.

限制变量范围

将局部变量的范围降到最低。通过这样做,您增加可读性和可维护性代码并减少出错的可能性.每个变量应该在包含变量所有使用的最内层块中声明.

局部变量应该在它们首次使用的点上声明。 几乎每个局部变量声明都应该包含一个初始化器。 如果你还没有足够的信息来明智地初始化变量,推迟声明直到你这样做.

异常是try-catch语句. 如果一个变量用一个抛出被检查异常的方法的返回值初始化,它必须在try块中初始化.如果值必须在try块之外使用,那么它必须在try块之前声明,在那里它还不能明智地初始化:

// 初始化类cl, 这个类用来表示某种Set
Set s = null;
try {
    s = (Set) cl.newInstance();
} catch(IllegalAccessException e) {
    throw new IllegalArgumentException(cl + " not accessible");
} catch(InstantiationException e) {
    throw new IllegalArgumentException(cl + " not instantiable");
}
...
// 使用这个 set
s.addAll(Arrays.asList(args));

下面的这种写法是推荐的

Set createSet(Class cl) {
    // 初始化类cl, 这个类用来表示某种Set
    try {
        return (Set) cl.newInstance();
    } catch(IllegalAccessException e) {
        throw new IllegalArgumentException(cl + " not accessible");
    } catch(InstantiationException e) {
        throw new IllegalArgumentException(cl + " not instantiable");
    }
}

...

// 使用这个 set
Set s = createSet(cl);
s.addAll(Arrays.asList(args));

循环变量应该在for语句本身中声明,除非有强制的理由不这样做:

for (int i = 0; i < n; i++) {    
    doSomething(i);
}

and

for (Iterator i = c.iterator(); i.hasNext(); ) {    
    doSomethingElse(i.next());
}
导入语句的顺序

import语句的排序是:

  1. Android imports
  2. Imports from third parties (com, junit, net, org)
  3. java and javax

还有

  • 每个分组中按字母顺序排列,大写字母前加小写字母(e.g. Z before a)
  • 在每个主要分组(android,com,junit,net,org,java,javax)之间用空行分隔
使用缩进空格

使用4个空格缩进块,而不是制表符
我们使用8个空格缩进进行换行,包括函数调用和赋值,例如,这是正确的:

Instrument i =
        someLongExpression(that, wouldNotFit, on, one, line);

下面这个是不正确的

Instrument i =    
    someLongExpression(that, wouldNotFit, on, one, line);
换行

没有一个精确的公式解释如何换行,并且经常不同的解决方案是有效的.然而,有一些规则可以应用于常见的情况, 这里列举了常见的几种.

int longName = anotherVeryLongVariable + anEvenLongerOne - thisRidiculousLongOne 
        + theFinalOne;
int longName =
        anotherVeryLongVariable + anEvenLongerOne - thisRidiculousLongOne + theFinalOne;
Picasso.with(context)
        .load("http://ribot.co.uk/images/sexyjoe.jpg")
        .into(imageView);
loadPicture(context,
        "http://ribot.co.uk/images/sexyjoe.jpg",
        mImageViewProfilePicture,
        clickListener,
        "Title of the picture");
命名约定

变量的命名

  • 非公共,非静态字段名以m开头
  • 静态字段名称以s开头
  • 其他字段以小写字母开头
  • 公共静态最终字段(常量)所以大写字母并以下画线连接

例子

public class MyClass {
    public static final int SOME_CONSTANT = 42;
    public int publicField;
    private static MyClass sSingleton;
    int mPackagePrivate;
    private int mPrivate;
    protected int mProtected;
}

Android 的许多元素(如SharedPreferences,Bundle或Intent)都使用键值对方法,因此对这个也规范如下:
所有的变量前面都加static final

| 组件 | 命名前缀 |
| ------------- |: -----:|
|SharedPreferences |PREF_
|Bundle |BUNDLE_
|Fragment Arguments |ARGUMENT_
|Intent Extra | EXTRA_
|Intent Action |ACTION_
|BroadCast Action|ACTION_

请注意,Fragment - Fragment.getArguments()的参数也是一个Bundle。 然而,因为这是一个很常见的使用Bundles,我们为它们定义一个不同的前缀。
例子:

// Note the value of the field is the same as the name to avoid duplication issues
static final String PREF_EMAIL = "PREF_EMAIL";
static final String BUNDLE_AGE = "BUNDLE_AGE";
static final String ARGUMENT_USER_ID = "ARGUMENT_USER_ID";

// Intent-related items use full package name as value
static final String EXTRA_SURNAME = "com.myapp.extras.EXTRA_SURNAME";
static final String ACTION_OPEN_USER = "com.myapp.action.ACTION_OPEN_USER";

如果是启动Activity

public static Intent getStartIntent(Context context, User user) {
    Intent intent = new Intent(context, ThisActivity.class);
    intent.putParcelableExtra(EXTRA_USER, user);
    return intent;
}

如果是启动Fragment

public static UserFragment newInstance(User user) {
    UserFragment fragment = new UserFragment;
    Bundle args = new Bundle();
    args.putParcelable(ARGUMENT_USER, user);
    fragment.setArguments(args)
    return fragment;
}

注意1:这些方法应该在onCreate()之前的类的顶部
注意2:如果我们提供上面描述的方法,extras和参数的键应该是私有的,因为它们不需要暴露在类外部。

类的命名

类名使用大写骆驼拼写法来命名,如果类是继承了相应的组件,则要以组件名字结尾来命名,如:SignInActivity, SignInFragment, ImageUploaderService,ChangePasswordDialog

| 类 | 命名格式 | 示例 |
| ------------- |: -------------:|: -----:|
| Activity | 描述+Activity |HomeActivity,MainActivity |
|Fragment |描述+Fragment | 如购物车,CartFragment|
| Service | 描述+Service | PushMessageService |
| BroadcastReceiver | 描述+Receiver | OnlineReceiver |
|ContentProvider | 描述+Receiver| 如联系人的内容提供者,ContactsProvider|
|Dialog|描述+Dialog|如普通的选择提示对话框,ChoiceDialog|
|Adapter|描述+Adapter|如联系人列表,ContactsListAdapter|
|基础功能类|Base+父类名| 如BaseActivity,BaseFragment|
|工具类|描述+Utils|如处理字符串的工具类,StringUtils|
|管理类|描述+Manager|如管理联系人的类,ContactsManager|

方法的命名

| 命名风格 | 含义 |
| ------------- |: -----:|
|initXX()|初始化,如初始化所有控件initView()|
|isXX()|是否满足某种要求,如是否为注册用户isRegister()|
|displayXX()|显示提示信息,如displayXXDialog,displayToast,displayXXPopupWindow|
|saveXX()| 保存XX数据|
|resetXX()| 重置XX数据|

资源的命名

资源的名字是用小写的、下划线连接

图片资源的命名,没有在下面列出来的图片可以参考: 图片的用途_用到的地方 来命名,如bg_sign_in, start_splash

drawables.png

用到的一些图标的命名

icon.png
select.png

Layout 下资源的命名

| 组件 | 类名字 | 布局文件名 |
| ------------- |: -------------:|: -----:|
|Activity| UserProfileActivity| activity_user_profile.xml|
|Fragment| SignUpFragment | fragment_sign_up.xml|
|Dialog| ChangePasswordDialog| dialog_change_password.xml|
|AdapterView Item| -- | item_person.xml|
|抽取出来复用的xml布局(include)| --|include_bottom_tabs|

当XML元素没有任何内容时,您必须使用自动关闭标记。

正确的

<TextView
    android:id="@+id/text_view_profile"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content" />

不对的

<TextView
    android:id="@+id/text_view_profile"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content" >
</TextView>

Menu 下资源的命名

与布局文件类似,菜单文件应与组件的名称匹配。 例如,如果我们定义将要在UserActivity中使用的菜单文件,则文件的名称应为activity_user.xml.

注意这里不要加menu,因为已经在menu目录下了

Values 下的文件命名

values文件夹中的资源文件应为复数,如:strings.xml,styles.xml,colors.xml,dimens.xml,attrs.xml

colors.xml: 定义颜色值, 如果是通用的颜色,用具体的颜色名称来表示.如果是只有单独的界面用到的或者有 代表性的,则用如goods_price,progressbar_bg,main_window_background
themes.xml: 定义主题,所有的主题名字以Theme结尾
styles.xml: 定义使用的样式,所有的样式名字以Style结尾
strings.xml: 定义所有使用的字符串, 字符串名称以标识其所属性的前缀开头,看下面表格:

| 前缀 | 描述 |
| ------------- |: -----:|
|error_ |An error message
|msg_ |A regular information message
|title_ |A title, i.e. a dialog title|
|action_ |An action such as "Save" or "Create"|

id 的命名

都是用小写字母、下划线链接

| 组件 | 描述 |
| ------------- |: -----:|
|TextView|_text|
|ImageView|_image|
|Button|_button|
|CheckBox|_check|
|ProgressBar|_bar或者_view|
|ScrollView|_view|
|自定义view|_view|
|LinearLayout|_layout或者_container|
|RelativeLayout|_layout或者_container|
|Fragment|fragment|
|Menu|menu
|

<ImageView
    android:id="@+id/profile_image"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content" />
<menu>
    <item
        android:id="@+id/menu_done"
        android:title="Done" />
</menu>
使用标准括号样式

大括号不自己另起一行; 他们和他们之前的代码在同一行:

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

我们需要在条件语句周围添加括号. 特例:如果整个条件(条件和正文)符合一行,您可以(但不是必须)将它全部放在一行上,例如,这是可以接受的:

if (condition) {
    body();
}

下面这样也可以

if (condition) body();

但是这样就不可以
if (condition)
body(); // 不好的!容易造成歧义

限制代码每一行的长度

代码中的每行文字长度应试最多为100个字符,也有特例:

  • 如果注释行包含示例命令或长度超过100个字符的文字URL,那么该行可能长于100个字符,以便于剪切和粘贴.
  • import行可以超过限制,因为人们很少看到它们(这也简化了工具写入)
正确使用首字母缩略词

将缩写词作为命名变量,方法和类中的单词,以使名称更易读:

canvas.png
使用TODO注释

对临时代码使用TODO注释,短期解决方案,或者足够好但不完美的代码。 TODO应在所有大写字母中包含字符串TODO,后跟冒号:

Java 语言规则


不要忽略异常的处理

可能很容易编写完全忽略异常的代码,例如:

void setServerPort(String value) {
    try {
        serverPort = Integer.parseInt(value);
    } catch (NumberFormatException e) { }
}

不要这样做.虽然你可能认为你的代码永远不会遇到这个错误条件或者它不重要的处理它,忽略异常如上所示在你的代码中为别人触发一天.你必须以原则的方式处理你的代码中的每一个异常; 具体处理根据情况而变化.

因该使用下面的下发来替换(按优先顺序):

  • 将异常抛出给方法的调用者
void setServerPort(String value) throws NumberFormatException {
    serverPort = Integer.parseInt(value);
}
  • 抛出一个适合你的抽象层次的新异常
void setServerPort(String value) throws ConfigurationException {
    try {
        serverPort = Integer.parseInt(value);
    } catch (NumberFormatException e) {
        throw new ConfigurationException("Port " + value + " is not valid.");
    }
}
  • 处理错误并在catch {}块中替换一个适当的值
void setServerPort(String value) {
    try {
        serverPort = Integer.parseInt(value);
    } catch (NumberFormatException e) {
        serverPort = 80;  // default port for server
    }
}
不捕获泛型异常
try {
    someComplicatedIOFunction();        // may throw IOException
    someComplicatedParsingFunction();   // may throw ParsingException
    someComplicatedSecurityFunction();  // may throw SecurityException
    // phew, made it all the way
} catch (Exception e) {                 // I'll just catch all exceptions
    handleError();                      // with one generic handler!
}
导入具体的包路径

当你想使用包foo中的类Bar时,有两种可能的方法来导入它:

  • import foo.*;
  • import foo.Bar; //使用这个方式,代码可读性更强.

其他还需要注意的地方


  • 缩放保证不失真的,图片可以做成点9图
  • strings.xml中使用%1$s实现字符串的通配
  • 代码中不出现中文,最多注释中可以出现中文

Android 官方规范
android-guidelines
简书

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,772评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,458评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,610评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,640评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,657评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,590评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,962评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,631评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,870评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,611评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,704评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,386评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,969评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,944评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,179评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,742评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,440评论 2 342

推荐阅读更多精彩内容

  • Android 编码规范 1. 前言 这份文档是 Google Java Code Style 的译文,并稍有添加...
    人失忆阅读 442评论 0 3
  • 作者:李旺成 时间:2016年4月3日 1. 前言 这份文档参考了 Google Java 编程风格规范和 Goo...
    diygreen阅读 39,852评论 19 224
  • androidstudio集成checkstyle提交前校验方法,将pre-commit文件copy到工程目录.g...
    Chris锅阅读 885评论 0 0
  • Android编码规范 源文件基础 文件名 源文件以其最顶层的类名来命名,大小写敏感,文件扩展名为.java。 文...
    呼呼哥阅读 930评论 0 0
  • 介绍 为什么需要编码规范? 编码规范对于程序员而言尤为重要,有以下几个原因:  一个软件的生命周期中,80%...
    lucas777阅读 448评论 0 0