java编码规范(Android)

Java语言规范

遵循标准的java编码规范

1.不要忽略Exceptions

有时,完全忽略异常非常诱人,比如:

1. void setServerPort(String value) {  
2.     try {  
3.         serverPort = Integer.parseInt(value);  
4.     } catch (NumberFormatException e) { }  
5. }  

绝对不要这么做,上述忽略异常的代码将会在代码中埋下一颗地雷,说不定哪天它就会炸到某个人了。你必须在代码中以某种规矩来处理所有的异常。

可接受的替代方案包括(按照推荐顺序):
一:向方法的调用这抛出异常

1. void setServerPort(String value) throws 
2. NumberFormatException {  
3.     serverPort = Integer.parseInt(value);  
4. }

二:根据抽象级别抛出新的异常

1. void setServerPort(String value) throws
2. ConfigurationException {  
3.     try {  
4.         serverPort = Integer.parseInt(value);  
5.     } catch (NumberFormatException e) {  
6.         throw new ConfigurationException("Port " + value + " is not valid.");  
7.     }  
8. }  

三:默默的处理错误并在catch{}语句块中替换为合适的值

1. /** Set port. If value is not a valid number, 80 is substituted. */  
2.   
3. void setServerPort(String value) {  
4.     try {  
5.         serverPort = Integer.parseInt(value);  
6.     } catch (NumberFormatException e) {  
7.         serverPort = 80;  // default port for server  
9.     }  
10. }

四:捕获异常并抛出一个新的RuntimeException,这种做法比较危险:只有确信发生该错误时,最合适的方法就是崩溃,才会这么做

1. /** Set port. If value is not a valid number, die. */  
2.   
3. void setServerPort(String value) {  
4.     try {  
5.         serverPort = Integer.parseInt(value);  
6.     } catch (NumberFormatException e) {  
7.         throw new RuntimeException("port " + value " is invalid, ", e);  
9.     }  
10. } 

五:如果确信忽略异常比较合适,那就忽略吧,但必须把理想的原因注释出来:

1. /** If value is not a valid number, original port number is used. */  
2. void setServerPort(String value) {  
3.     try {  
4.         serverPort = Integer.parseInt(value);  
5.     } catch (NumberFormatException e) {  
6.         // Method is documented to just ignore invalid user input.  
7.         // serverPort will just be unchanged.  
8.     }  
9. }  

2.不要捕获顶级的Exception

有时在捕获Exception时偷懒也很是吸引人的,类似如下的处理方式:

1. try {  
2.     someComplicatedIOFunction();        // may throw IOException  
3.     someComplicatedParsingFunction();   // may throw ParsingException  
4.     someComplicatedSecurityFunction();  // may throw SecurityException  
5. } catch (Exception e) {                 // I'll just catch allexceptions  
6.    handleError();                       // with  one generic handler!  
7. } 

不要这么做。绝大部分情况下,捕获顶级的Exception或Throwable都是不合适的,Throwable更不合适,因为它还包含了Error异常。这种捕获非常危险。这意味着本来不必考虑的Exception(包括类似ClassCastException的RuntimeException)被卷入到应用程序级的错误处理中来。这会让代码运行的错误变得模糊不清。这意味着,假如别人在你调用的代码中加入了新的异常,编译器将无法帮助你识别出各种不同的错误类型。绝大部分情况下,无论如何你都不应该用同一种方式来处理各种不同类型的异常。

本规则也有极少数例外情况:期望捕获所有类型错误的特定的测试代码和顶层代码(为了阻止这些错误在用户界面上显示出来,或者保持批量工作的运行)。这种情况下可以捕获顶级的Exception(或Throwable)并进行相应的错误处理。在开始之前,你应该非常仔细地考虑一下,并在注释中解释清楚为什么这么做是安全的。

比捕获顶级Exception更好的方案:

分开捕获每一种异常,在一条try语句后面跟随多个catch 语句块。这样可能会有点别扭,但总比捕获所有Exception要好些。请小心别在catch语句块中重复执行大量的代码。

重新组织一下代码,使用多个try块,使错误处理的粒度更细一些。把IO从解析内容的代码中分离出来,根据各自的情况进行单独的错误处理。

再次抛出异常。很多时候在你这个级别根本就没必要捕获这个异常,只要让方法抛出该异常即可。
请记住:异常是你的朋友!当编译器指出你没有捕获某个异常时,请不要皱眉头。而应该微笑:编译器帮助你找到了代码中的运行时(runtime)问题。

3.不要使用Finalizer

Finalizer提供了一个机会,可以让对象被垃圾回收器回收时执行一些代码。

优点:便于执行清理工作,特别是针对外部资源。

缺点:调用finalizer的时机并不确定,甚至根本就不会调用。

结论:我们不要使用finalizers。大多数情况下,可以用优秀的异常处理代码来执行那些要放入finalizer的工作。如果确实是需要使用finalizer,那就定义一个close()方法(或类似的方法),并且在文档中准确地记录下需要调用该方法的时机。相关例程可以参见InputStream。这种情况下还是适合使用finalizer的,但不需要在finalizer中输出日志信息,因为日志不能因为这个而被撑爆。

4.使用完全限定Import

当需要使用foo包中的Bar类时,存在两种可能的import方式:
import foo.*;
优点:可能会减少import语句。
import foo.Bar;
优点:实际用到的类一清二楚。代码的可读性更好,便于维护。

结论:用后一种写法来import所有的Android代码。不过导入java标准库(java.util.、*java.io.**等) 和单元测试代码 (junit.framework.*)时可以例外。

Java类库规范

使用Android Java类库和工具存在一些惯例。有时这些惯例会作出重大变化,可之前的代码也许会用到过时的模板或类库。如果用到这部分过时的代码,沿用已有的风格就是了(参阅Consistency)。创建新的组件时就不要再使用过时的类库了。

Java编程规范

1.使用Javadoc标准准注释

每个文件的开头都应该有一句版权说明。然后下面应该是package包语句和import语句,每个语句块之间用空行分隔。然后是类或接口的定义。在Javadoc注释中,应描述类或接口的用途。


​/* 
 * Copyright (C) 2010 The Android Open Source Project 
 * 
 * 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;  

/** 
* Does X and Y and provides an abstraction for Z. 
*/  

public class Foo {  
    ...  
} 

每个类和自建的public方法必须包含Javadoc注释,注释至少要包含描述该类或方法用途的语句。并且该语句应该用第三人称的动词形式来开头。

例如:
/** Returns the correctly rounded positive square root of a double value. */  
static double sqrt(double a) {  
    ...  
}  
或
/** 
 * Constructs a new String by converting the specified array of 
 * bytes using the platform's default character encoding. 
 */  
public String(byte[] bytes) {  
    ...  
} 

如果所有的Javadoc都会写成“setsFoo”,对于那些无关紧要的类似setFoo()的get和set语句是不必撰写Javadoc的。如果方法执行了比较复杂的操作(比如执行强制约束或者产生很重要的副作用),那就必须进行注释。如果“Foo”属性的意义不容易理解,也应该进行注释。

无论是public的还是其它类型的,所有自建的方法都将受益于Javadoc。public的方法是API的组成部分,因此更需要Javadoc。

Android目前还没有规定自己的Javadoc注释撰写规范,但是应该遵守Sun Javadoc约定。

2.编写简短的方法

为了把规模控制在合理范围内,方法应该保持简短和重点突出。不过,有时较长的方法也是合适的,所以对方法的代码长度并没有硬性的限制。如果方法代码超过了40行,就该考虑是否可以在不损害程序结构的前提下进行分拆。

3.在标准的位置定义字段

字段应该定义在文件开头,或者紧挨着使用这些字段的方法之前。

4.限制变量的作用范围

局部变量的作用范围应该是限制为最小的(Effective Java第29条)。使用局部变量,可以增加代码的可读性和可维护性,并且降低发生错误的可能性。每个变量都应该在最小范围的代码块中进行声明,该代码块的大小只要能够包含所有对该变量的使用即可。

应该在第一次用到局部变量的地方对其进行声明。几乎所有局部变量声明都应该进行初始化。如果还缺少足够的信息来正确地初始化变量,那就应该推迟声明,直至可以初始化为止。

本规则存在一个例外,就是涉及try-catch语句的情况。如果变量是用方法的返回值来初始化的,而该方法可能会抛出一个checked异常,那么必须在try块中进行变量声明。如果需在try块之外使用该变量,那它就必须在try块之前就进行声明了,这时它是不可能进行正确的初始化的。

// Instantiate class cl, which represents some sort of 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");  
}  
// Exercise the set  
s.addAll(Arrays.asList(args));  

但即便是这种情况也是可以避免的,把try-catch 块封装在一个方法内即可:

Set createSet(Class cl) {  
    // Instantiate class cl, which represents some sort 
of Set  
    try {  
        return (Set) cl.newInstance();  
    } catch(IllegalAccessException e) {  
        throw new IllegalArgumentException(cl + " not 
accessible");  
    } catch(InstantiationException e) {  
        throw new IllegalArgumentException(cl + " not 
instantiable");  
    }  
}  
...  

// Exercise the set  
Set s = createSet(cl);  
s.addAll(Arrays.asList(args)); 

除非理由十分充分,否则循环变量都应该在for语句内进行声明

for (int i = 0; i n; i++) {  
    doSomething(i);  
}for (Iterator i = c.iterator(); i.hasNext(); ) {  
    doSomethingElse(i.next());  
}  

5.对Import语句的排序

import语句的次序应该如下:
1. Android imports
2. 第三方库(com、junit、net、org)
3. java和javax
为了精确匹配IDE的配置,import顺序应该是:
· 在每组内部按字母排序,大写字母排在小写字母的前面。
· 每个大组之间应该空一行(android、com、junit、net、org、java、javax)。

原先次序是不作为规范性要求的。这意味着要么允许IDE改变顺序,要么使用IDE的开发者不得不禁用import自动管理功能并且人工维护import。这看起来比较糟糕。每当说起java规范,推荐的规范到处都是。符合我们要求的差不多就是“选择一个次序并坚持下去。”于是,我们就选择一个规范,更新规范手册,并让IDE去遵守它。我们期望:不必耗费更多的精力,用IDE编码的用户就按照这种规则去import所有的package。

基于以下原因,选定了本项规则:
· 导入人员期望最先看到的放在最开始位置(android)
· 导入人员期望最后才看到的放在最后(java)
· 风格让人容易遵守
· IDE可以遵守

静态import的使用和位置已经成为略带争议的话题。有些人愿意让静态import和其它import混在一起,另一些人则期望让它们位于其它import之上或者之下。另外,我们还未提到让所有IDE都遵守同一个次序的方法。

因为大多数人都认为这部分内容并不要紧,只要遵守你的决定并坚持下去即可。

6.使用空格进行缩进

我们用8个空格作为换行后的缩进,包括函数调用和赋值。例如这是正确的:
Instrument i =  
        someLongExpression(that, wouldNotFit, on, one, 
line);  
而这是错误的:
Instrument i =  
    someLongExpression(that, wouldNotFit, on, one, line);  

7.遵循字段命名惯例

驼峰(Camel)命名法:又称小驼峰命名法,除首单词外,其余所有单词的第一个字母大写。
· 非public的、非static的字段名称以m开头。
· static字段名称以s开头。
· 其它字段以小写字母开头。
· public static final字段(常量)全部字母大写并用下划线分隔。
包名:
包(packages): 采用反域名命名规则,全部使用小写字母。一级包名为com,二级包名为xx(可以是公司或则个人的随便),三级包名根据应用进行命名,四级包名为模块名或层级名
例如:com.l99.bed
类名,接口(interface):
采用大驼峰命名法,尽量避免缩写,除非该缩写是众所周知的, 比如HTML,URL,如果类名称中包含单词缩写,则单词缩写的首子母大写。
根据项目的不同添加大写缩写前缀
例如:CSLoginActivity
方法(methods):小驼峰
变量(variables)(id下划线,变量名将下划线换成驼峰,但是必须保持一致):小驼峰
常量(Constants)
全部大写,采用下划线命名法.例如:MIN_WIDTH
资源文件(图片drawable文件夹下):
全部小写,采用下划线命名法
资源布局文件
全部小写,采用下划线命名法
例如:

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;  
}  

8.使用标准的大括号分格

大括号不单独占用一行;它们紧接着上一行书写。就像这样:
class MyClass {  
    int func() {  
        if (something) {  
            // ...  
        } else if (somethingElse) {  
            // ...  
        } else {  
            // ...  
        }  
    }  
} 
我们需要用大括号来包裹条件语句块。不过也有例外,如果整个条件语句块(条件和语句本身)都能容纳在一行内,也可以(但不是必须)把它们放入同一行中。也就是说,这是合法的:
if (condition) {  
    body();  
} 
这也是合法的:
if (condition) body();  
但这是非法的:
if (condition)  
    body();  // bad! 
9.简称等同于单词
好                           差
XmlHttpRequest          XMLHTTPRequest
getCustomerId           getCustomerID
class Html              class HTML
String url              String URL
long id                 long ID
如何对待简称,JDK和Android底层代码存在很大的差异。因此,你几乎不大可能与其它代码取得一致。别无选择,把简称当作完整的单词看待吧。

10.使用TODO注释

TODO注释应该包含全部大写的TODO,后跟一个冒号:
// TODO: Remove this code after the UrlTable2 has been 
checked in.  
和
// TODO: Change this to use a flag instead of a constant.

如果TODO注释是“将来要做某事”的格式,则请确保包含一个很明确的日期(“在2005年11月会修正”),或是一个很明确的事件(“在所有代码整合人员理解了V7协议之后删除本段代码”)。

4.其他注意事项

java代码中不出现中文,最多注释中可以出现中文,中文统一写在strings.xml中;

遵循只做一件事原则,一个方法,一个类,只做一件事,降低藕合度,降低逻辑复杂度

styles.xml:将layout中不断重现的style提炼出通用的style通用组件,放到styles.xml中;

图片尽量分拆成多个可重用的图片

图片要.9.png处理(看情况)

使用静态变量方式实现界面间共享要慎重

不要重用父类的handler,对应一个类的handler也不应该让其子类用到,否则会导致message.what冲突

strings.xml中使用%1$s实现字符串的通配

如果多个Activity中包含共同的UI处理,那么可以提炼一个CommonActivity,把通用部分叫由它来处理,其他activity只要继承它即可

数据一定要效验,例如字符型转数字型,如果转换失败一定要有缺省值;服务端响应数据是否有效判断

在getItemView中,判断convertView是否为空,如果不为空,可复用。如果couvertview中的view需要添加listerner,代码一定要在if(convertView==null){}之外。

如果BaseAdapter的实体类有属性非常消耗内存,可以将保存到文件;为提高性能,可以进行缓存,并限制缓存大小。

使用线程池,分为核心线程池和普通线程池,下载图片等耗时任务放置在普通线程池,避免耗时任务阻塞线程池后,导致所有异步任务都必须等待

异步任务,分为核心任务和普通任务,只有核心任务中出现的系统级错误才会报错,异步任务的ui操作需要判断原activity是否处于激活状态,fragment是否isAdded();

尽量避免static成员变量引用资源耗费过多的实例,比如Context

.使用WeakReference代替强引用,弱引用可以让您保持对对象的引用,同时允许GC在必要时释放对象,回收内存。对于那些创建便宜但耗费大量内存的对象,即希望保持该对象,又要在应用程序需要时使用,同时希望GC必要时回收时,可以考虑使用弱引用。

超级大胖子Bitmap及时的销毁(Activity的onDestroy时将bitmap回收,在被UI组件使用后马上进行回收会抛 RuntimeException: Canvas: trying to use a recycled bitmap android.graphics.Bitmap)

设置一定的采样率(有开发者提供的图片无需进行采样,对于有用户上传或第三方的大小不可控图片,可进行采样减少图片所占的内存),从服务端返回图片,建议同时反馈图片的size

巧妙的运用软引用

drawable对应resid的资源,bitmap对应其他资源

任何类型的图片,如果获取不到(例如文件不存在,或者读取文件时跑OutOfMemory异常),应该有对应的默认图片(默认图片放在在apk中,通过resid获取);

保证Cursor 占用的内存被及时的释放掉,而不是等待GC来处理。并且 Android明显是倾向于编 程者手动的将Cursor close掉

线程也是造成内存泄露的一个重要的源头。线程产生内存泄露的主要原因在于线程 生命周期的不可控

应用开发中自定义View的时候,交互部分,千万不要写成线程不断刷新界面显示,而是根据TouchListener事件主动触发界面的更新

ui组件需要用到的图片是apk包自带的,那么一律用setImageResource或者setBackgroundResource,而不要根据resourceid

注意:get(getResources(), R.drawable.btn_achievement_normal)该方法通过resid转换为drawable,需要考虑回收的问题,如果drawable是对象私有对象,在对象销毁前是肯定不会释放内存的。

复用、回收Activity对象,临时的activity及时finish,主界面设置为singleTask,一般界面设置为singleTop

新增:

1:观察者模式的时候,注册的时候在onCreate中调用,反注册在onDestroy中调用

2:Handler的使用必须使用static内部类,然后用弱应用引用外部类

3:不建议使用压制警告

4:警告应该尽量避免

5:不要自己创建线程,会发生线程安全问题,应该使用统一的线程池管理类

猜你喜欢

转载自blog.csdn.net/zengyongsun/article/details/52833886