Moment For Technology

Java code specification for Android development

Posted on Sept. 23, 2022, 8:49 p.m. by Patricia Randolph
Category: android Tag: java android

AOSP Java Code Style guide for contributors

Java language Rules

Android follows standard Java coding specifications and other rules described below.

Do not Ignore exceptions

Developers may be tempted to write code that ignores exceptions entirely, such as:

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

Don't do that. While you may think that your code will never encounter such an error, or that you don't need to worry about handling it, ignoring exceptions as in the above example leaves a trap in your code that someone else will someday trigger. You must handle every exception in your code in a principled way; The specific treatment varies from case to case.

"You should be on your guard whenever you encounter an empty catch clause. Of course, there are times when an empty catch statement is fine, but at least you have to think about it. In Java, you can never be too careful. - James Gosling

Acceptable alternatives (in order of priority) include:

  • Throw the exception to the method caller.

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

  • Throw a new exception that is appropriate for your level of abstraction.

    setServerPort(String value) throws ConfigurationException { try { serverPort = Integer.parseInt(value); } catch (NumberFormatException e) { throw new ConfigurationException("Port " + value + " is not valid."); }}

  • Handle the error properly and replace the corresponding value in the catch {} block.

    /** Set port. If value is not a valid number, 80 is substituted. */

    void setServerPort(String value) { try { serverPort = Integer.parseInt(value); } catch (NumberFormatException e) { serverPort = 80; // default port for server } }

  • Catch the exception and throw a new RuntimeException. This is risky, so use this option only when you are sure that the most appropriate thing to do if this error occurs is to crash the application.

    /** Set port. If value is not a valid number, die. */

    void setServerPort(String value) { try { serverPort = Integer.parseInt(value); } catch (NumberFormatException e) { throw new RuntimeException("port " + value " is invalid, ", e); }}

    Note: The original exception is passed to the constructor of RuntimeException. If your code must be compiled in Java 1.3, you must ignore the exception indicating the cause.

  • One last option: If you are sure that ignoring the exception is the appropriate way to handle it, you can ignore the exception, but you must add a note to make the case:

    /** If value is not a valid number, original port number is used. */

    void setServerPort(String value) { try { serverPort = Integer.parseInt(value); } catch (NumberFormatException e) { // Method is documented to just ignore invalid user input. // serverPort will just be unchanged. } }

Do not catch normal exceptions

When catching exceptions, developers may be tempted to do the following in order to be lazy:

  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!
  }
Copy the code

Don't do that. Catching normal exceptions or Throwable (preferably not because it contains Error exceptions) is not a good idea in almost all cases. Doing so is dangerous because it means that the system catches exceptions (including RuntimeExceptions like ClasscastExceptions) that you never expected during application level errors. It masks the fault handling nature of your code, which means that if someone adds a new type of exception to the code you call, the compiler doesn't help you realize that you need to handle the error differently. In most cases, you should not handle different types of exceptions in the same way.

The exception to this rule is that in both test code and top-level code, you want to catch all types of errors (in case they show up in the interface or so that batch jobs are always running). In these cases, you can catch regular exceptions (or Throwable) and handle errors appropriately. But be sure to think twice before doing so, and then add a note explaining why it's safe to do this here.

Alternatives to catching regular exceptions:

  • Catch each exception separately as multiple catch blocks, for example:

    try { ... } catch (ClassNotFoundException | NoSuchMethodException e) { ... }

  • Refactoring your code with multiple try blocks refines error handling. Separate the IO from the parse, and then deal with the error in each case separately.

  • Rethrow the exception. Many times, you don't need to catch an exception at this level; you can simply have the corresponding method throw an exception.

Remember: Exceptions are your friend! Don't mope when the compiler complains that you didn't catch an exception! You should smile: The compiler makes it easier to catch runtime errors in your code.

Do not use finalizers

Finalizers can execute a piece of code when an object is collected by the garbage collector. While finalizers are great for cleaning up resources (especially external resources), there is no guarantee that they will be called (or even at all).

Android does not use finalizers. In most cases, you can implement finalizer functionality with a good exception handling process. If you do need a finalizer, define a close() method (or something similar) and note the exact time you need to call it (see InputStream for an example). In this case, you can (but do not have to) output short log messages in the finalizer, provided you do not output a large number of log messages.

Fully qualified import

When you want to use the Bar class in the foo package, you can import it in one of two ways:

  • import foo.*;The number of import statements may be reduced.
  • import foo.Bar;It clearly indicates which classes are actually used, and the code is easier for maintainers to read.

Using the import foo Bar; Import all Android code. For the Java standard libraries (java.util., [java.io](https://link.zhihu.com/?target=http%3A//java.io/).*, etc.) and unit test code (junit.framework.*), A clear exception has been established.

Java library rules

Java libraries and tools using Android must comply with the specifications. In some cases, there have been major changes to the specification, and older code may be using deprecated patterns or libraries. When using this type of code, you can continue to follow the existing style. However, do not use deprecated libraries when creating new components.

Java style Rules

Use the Javadoc standard remarks

Each file should have a copyright notice at the top, followed by package and import statements (blocks separated by empty lines), and finally a class or interface declaration. Specify the role of a class or interface in Javadoc remarks.

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 { ... }Copy the code

Every class and important public method that you write must include a Javadoc note that describes the purpose of the class or method in at least one sentence. The sentence pattern should begin with a third person descriptive verb.

The sample

/** Returns the correctly rounded positive square root of a double value. */

static double sqrt(double a) {
    ...
}
Copy the code

or

/**
 * Constructs a new String by converting the specified array of
 * bytes using the platform's default character encoding.
 */
public String(byte[] bytes) {
    ...
}
Copy the code

For normal get and set methods (such as setFoo()), you don't need to write Javadoc remarks, just "sets Foo". If the method performs more complex operations, such as enforcing constraints or having significant side effects, then you must add remarks. If the meaning of the attribute "Foo" is unclear, you should also add a comment.

Every method you write, public or otherwise, will benefit from Javadoc remarks. Public methods are part of the API, so you need to add Javadoc remarks. Android does not currently mandate a particular style for writing Javadoc notes, but you are advised to follow the instructions in ** How to Write documentation notes for the Javadoc tool **.

Write short methods

Whenever possible, write short, concise methods. We understand that in some cases longer methods are appropriate, so there is no hard limit on the code length of a method. If a method has more than 40 lines of code, consider whether you can disassemble it without breaking the program structure.

Define fields in standard locations

Define fields at the top of the file or before using their methods.

Limits the scope of variables

Minimize the scope of local variables. Doing so helps make your code more readable and maintainable, and reduces the likelihood of errors. Each variable should be declared in the innermost block that contains all the uses for the variable.

Local variables should be declared the first time they are used. Almost every local variable declaration should contain an initializer. If you don't have enough information to properly initialize a variable, postpone declaring it until you do.

Try-catch statements are an exception. If a variable is initialized by the return value of a method that throws a checked exception, it must be initialized in the try block. If this value must be used outside the try block, you must declare it before the try block, because it cannot be properly initialized inside the try block:

// 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));
Copy the code

However, even this can be avoided by wrapping a try-catch block in a method:

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));Copy the code

Loop variables should be declared in the for statement itself, unless there is a compelling reason not to:

for (int i = 0; i  n; i++) {
    doSomething(i);
}
Copy the code

and

for (Iterator i = c.iterator(); i.hasNext(); ) {
    doSomethingElse(i.next());
}
Copy the code

Sort import statements

The order of import statements is:

  1. Import the Android package
  2. Importing third-party packages (com,junit,net,org)
  3. javajavax

To fully conform to the IDE setup, the import order should be:

  • Each group is sorted alphabetically, where statements beginning with a capital letter precede statements beginning with a lowercase letter (for example, Z precedes a).
  • Each major subgroup (android,com,junit,net,org,java,javax) separated by a blank line.

Initially there was no style requirement for the order of statements, which meant that the IDE often changed the order, or IDE developers had to disable automatic import management and manually maintain import statements. This is rather inconvenient. When it comes to Java styles, there's a wide variety of styles that developers like, but ultimately it boils down to "choosing a compatible and consistent sorting method" for Android. So we chose a style, updated the style guide, and made the IDE follow it. We want IDE users to write code in such a way that the system imports all packages into this pattern without additional engineering.

This style was chosen according to the following principles:

  • Imports that users want to see first tend to be at the top (android).
  • The imports that users least want to see tend to be at the bottom (java).
  • Styles that users can easily follow.
  • The styles that the IDE can follow.

Place static imports on top of all other imports (in the same order as regular imports).

Indent with space

We use four (4) Spaces to indent blocks, never tabs. If you have doubts, be consistent with the surrounding code.

We use eight (8) Spaces to indent the wrap line, including function calls and assignments.

recommended

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

Is not recommended

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

Follow field naming conventions

  • Non-public and non-static field names start with m.
  • Static field names start with s.
  • Other fields begin with a lowercase letter.
  • Public static final fields (constants) are all capitalized and underlined (ALL_CAPS_WITH_UNDERSCORES).

Such as:

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;
}
Copy the code

Use the standard curly brace style

The open curly brace does not occupy a single line, but is on the same line as the code that precedes it:

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

We need to add braces around the conditional statement. Exception: If the entire conditional statement (condition and body) fits on the same line, then you can (but not must) put it all on one line. For example, we accept the following styles:

if (condition) {
    body();
}
Copy the code

The following styles are also accepted:

if (condition) body();
Copy the code

The following styles are not accepted:

if (condition)
    body();  // bad!
Copy the code

Limit code line length

No line of text in your code should be more than 100 characters long. While there was a lot of debate about this rule, the final decision was to limit it to 100 characters, with the following exceptions:

  • If the comment line contains sample commands or text urls that are longer than 100 characters, it can be longer than 100 characters for cutting and pasting purposes.
  • Importing statement lines can exceed this limit because they are rarely seen by the user (which also simplifies the tool writing process).

Use standard Java annotations

Comments should precede other modifiers of the same language element. Simple tag comments, such as @override, can be listed on the same line as language elements. If there are multiple comments or parameterized comments, they should be on one line and arranged alphabetically.

The Android standard for the three predefined annotations in Java is as follows:

  • @deprecated: The @deprecated annotation must be used when annotating elements is not recommended. If you use the @deprecated annotation, you must also add the @Deprecated Javadoc tag to it, and that tag should specify an alternative implementation. Also note that the @deprecated method should still be available. If you see old code with the @deprecated Javadoc tag, add the @deprecated annotation.

  • @Override: The @Override annotation must be used when a method replaces a declaration or implementation in a superclass. For example, if you use the @Inheritdocs Javadoc tag and derive from a class (rather than an interface), you must add the @Override annotation to the method to indicate that it replaces the method of the parent class.

    @SuppressWarnings: The @SuppressWarnings annotation should only be used if the warning cannot be eliminated. If a warning passes the "Unextinguishable" test, you must use the @SuppressWarnings annotation to ensure that all warnings reflect an actual problem in your code. When the @SuppressWarnings annotation is required, you must precede it with a TODO note for the "Cannot be eliminated" situation. This usually identifies which offending class is using the bad interface. // TODO: The third-party class com.third.useful.Utility.rotate() needs generics @SuppressWarnings("generic-cast") List blix = Utility.rotate(blax);

    When you need the @SuppressWarnings annotation, you should refactor the code to isolate the software elements that need it.

Treat acronyms as words

When naming variables, methods, and classes, treat acronyms and abbreviations as words to make the names more readable:

good

poor

class Html

class HTML

getCustomerId

getCustomerID

long id

long ID

String url

String URL

XmlHttpRequest

XMLHTTPRequest

Because the JDK and Android code base are so inconsistent on acronyms, it's almost impossible to keep up with the surrounding code. Therefore, be sure to treat acronyms as words.

Note using TODO

Using TODO notes for your code is a short-term temporary solution, or good enough but not perfect. TODO remarks should contain the string TODO in all uppercase, followed by a colon.

// TODO: Remove this code after the UrlTable2 has been checked in.
Copy the code

and

// TODO: Change this to use a flag instead of a constant.
Copy the code

If your TODO takes the form of "do something at a future date," be sure to include either a very specific date (" Fix by November 2005 ") or a very specific event (" Remove this code after V7 protocol is handled by synthesizers in all production environments ").

Use logging sparingly

While logging is essential, it has a significant negative impact on performance, and can quickly lose its usefulness if it does not maintain a level of simplicity. The logging tool provides the following five different levels of logging:

  • ERROR: To be used in extremely serious situations. For example, some events have user-visible consequences that cannot be recovered without explicitly deleting some data, uninstalling applications, erasing data partitions, or overwriting the entire device (or worse). The system records logs at this level all the time. In general, it is best to report problems to the statistics collection server that illustrate some of the logging conditions at the ERROR level.
  • WARNING: Used in serious and unexpected situations. For example, some events can have consequences visible to the user, but can be recovered without loss of data by performing certain explicit actions, from waiting or restarting the application all the way to re-downloading a new version of the application or restarting the device. The system records logs at this level all the time. Consider reporting issues to the statistics collection server that illustrate some of the logging at the WARNING level.
  • INFORMATIVE:Used to record information of interest to most people. For example, when a condition is detected that causes widespread effects, the system records it, though not necessarily an error. This should only be documented by a module that is considered the most authoritative in the field (to avoid duplication by non-authoritative components). The system records logs at this level all the time.
  • DEBUG: Used to further document situations that occur on equipment that may be relevant to the investigation and debugging of unexpected behavior. You should only record what you need to gather enough information about the component. If your debug log is the primary log, you should probably use VERBOSE logging. This level of logging is logged by the system (even in a release) and should be aroundif (LOCAL_LOG)if LOCAL_LOGD)Block,LOCAL_LOG[D]Defined in your class or child component. This makes it possible for the system to disable all such logging. As a result,if (LOCAL_LOG)Block must not contain valid logic. All strings compiled for logging also need to be placedif (LOCAL_LOG)Block. If the logging call causes the string to compile inif (LOCAL_LOG)Occurs outside the block and should not be refactored as a method call.

    Some of the code is still in useif (localLOGV). Although the name is not canonical, it is acceptable.
  • VERBOSE: Records all other information. The system logs this level only for the debug version, and must be surrounded byif (LOCAL_LOGV)Block (or its kind) so that it can compile by default. All string compilations are removed from the release and are required inif (LOCAL_LOGV)Block.

Note:

  • In a given module, an error should only be reported once (if possible), except at the VERBOSE level. In a single function call chain within a module, only the innermost function should return an error, and callers in the same module can only add some logging that obviously helps isolate the problem.
  • In a chain of modules, with the exception of the VERBOSE level, when a lower level module detects invalid data from a higher level module, the lower level module should only record the situation in the DEBUG log, and only if the information provided by the log is not available to the caller. Specifically, there is no need to log when an exception is thrown (which should contain all relevant information) or when all the information logged is contained in the error code. This is especially important in interactions between frameworks and applications, and situations caused by third-party applications should not trigger logging higher than DEBUG if properly handled by the framework. The only condition that INFORMATIVE level or higher logging should trigger is if the module or application detects an error at its own level or lower.
  • When it turns out that some logging may occur more than once, it is a good idea to implement a frequency-limiting mechanism to prevent the occurrence of a large number of duplicate log copies with the same (or very similar) information.
  • The loss of Internet connection is a perfectly predictable and common occurrence that doesn't need to be documented. If a loss of network connection results in some kind of consequence within the application, it should be logged at the DEBUG or VERBOSE level (depending on whether the consequence is serious enough and unexpected enough to be documented in the release).
  • If you have a full file system on top of a file system accessible to or on behalf of a third party application, none above the INFORMATIVE level should be logged.
  • Invalid data from any untrusted source (including any file in shared storage space or data obtained over any network connection) is considered as expected, and no logging above DEBUG level should be triggered when an invalid is detected (or even limited if possible).
  • Note that this is used for String+Operator, it implicitly creates a buffer with a default size of 16 charactersStringBuilderOther temporary Strings may also be created. For example, creating A StringBuilder explicitly is not more expensive (and may actually be more efficient) than relying on the default "+" operator. Note that even if no log information is read, the callLog.v()Code is also compiled and executed in the release, including compiled strings.
  • Any logging that is available for others to read and provided in a release should be concise, reasonably understandable. This includes all logging up to the DEBUG level.
  • Keep the log on a single line whenever it makes sense. A line length of 80 to 100 characters is perfectly acceptable, and more than 130 or 160 characters (including the length of the tag) should be avoided whenever possible.
  • Success events should never be reported using logging higher than VERBOSE.
  • Temporary logging to diagnose hard-to-reproduce problems should be at the DEBUG or VERBOSE level and should be wrapped in an if block so that it can be completely disabled during compilation.
  • Be careful not to leak security information in logs. Personal information should be avoided, and information about protected content must be avoided. This is especially important when writing framework code, because it is not easy to know in advance what is personal information or protected content and what is not.
  • Do not useSystem.out.println()(or for native codeprintf()). System.out and system. err redirect to /dev/null, so your print statement will not be visible. However, any strings compiled for these calls will still be executed.
  • The golden rule of logging is that your log doesn't have to push other logs out of the buffer, just as other logs don't do that to your log.

consistent

In short: Be consistent. If you are modifying code, take a few minutes to look around and determine its style. If the code uses Spaces around if statements, you should do the same. If the code remarks are surrounded by small boxes with asterisks, you should also place the remarks in such small boxes.

The goal of a style specification is to put together a common coding vocabulary so that people can focus on what you say, not how you say it. We present the overall style rules here so that users are aware of the vocabulary, but local styles are also important. If the code you add to the file looks markedly different from the existing code around it, it will throw readers off their stride when they get to this point. Please try to avoid this situation.

Javatests style rules

Follow the naming conventions for test methods and use underscores to distinguish what is being tested from what is being tested. This style makes it easier to see what you're testing. Such as:

testMethod_specificCase1 testMethod_specificCase2

void testIsDistinguishable_protanopia() {
    ColorMatcher colorMatcher = new ColorMatcher(PROTANOPIA)
    assertFalse(colorMatcher.isDistinguishable(Color.RED, Color.BLACK))
    assertTrue(colorMatcher.isDistinguishable(Color.X, Color.Y))
}
Copy the code
Search
About
mo4tech.com (Moment For Technology) is a global community with thousands techies from across the global hang out!Passionate technologists, be it gadget freaks, tech enthusiasts, coders, technopreneurs, or CIOs, you would find them all here.