This article is simultaneously published on my wechat official account. You can follow it by searching Nanchen on wechat

preface

Although the code of our project has not been developed for a long time and has not gone through many hands, the code standardization is still worrying. At present, there are many relatively free “code specifications”, which is very bad for the maintenance of the project and the code readability is not high enough.

In addition, the r&d mode of the client and the back-end is also completely different. The back-end r&d is basically based on THE SOA idea. Usually, it is enough manpower for three people to maintain a subsystem, and more often, it is the manpower configuration of 1 main force + 1 backup.

But the client side is completely different, everyone’s code is cross-cutting, a module of code may experience dozens of people overrun, so the formation of a consistent development specification is urgent.

Why do you need a consistent code specification?

The core is to reduce communication costs, improve the efficiency of our Code Review, and make our Code easier to maintain. In addition, a consistent code specification causes fewer bugs, which means more time and money saved.

Of course, the norms are agreed, this series of text is all the author over the years to learn from the long, accumulated from, so any different opinions, welcome to comment clap brick.

1. Android tool specifications

To do a good job, he must sharpen his tools.

Since Android is basically developed based on Android Studio, all of the tool specifications are based on Android Studio.

  1. You must use the latest stable version of Android Studio for development;
  2. The encoding format must be UTF-8.
  3. Optimize Imports (Settings -> Keymap -> Optimize Imports) to remove unnecessary Imports and reduce warnings, use the Optimize Imports shortcut to set your preferences.

4. Format.java,. Kt, and. XML files after editing them (the following prerequisites must be set)

Reformat Code is necessary to ensure that the IDE configuration is consistent and as close to the Android Studio default as possible.

Local formatting, not global formatting, is strongly recommended for long old code

  • Each line must contain no more than 160 characters. Set Editor -> Code Style

  • All set to single path references,kotlinx.android.synthetic.mainWith the exception of.

After the above Settings are complete, use the default mode of Android Studio for other Settings, and then click the Reformat Code shortcut key.

2. Android subcontracting specifications

The uniform configuration of the tools was emphasized earlier, but you can use Android Studio’s own capabilities to make code styles consistent. Which brings us to the second part: Android subcontracting specifications.

For subcontracting, we need to reach an agreement that PBF method is adopted and PBL method is not recommended.

PBF (Package By Feature) PBL (Package By Layer)

PBF may not be easy to distinguish between functions, but it is much easier to find than PBL, and PBF has the following advantages compared to PBL:

  • High cohesion within packages, low coupling between packages

Which block needs to add new functions? Only change things under a certain package, while PBL needs to change multiple packages, which is very troublesome.

  • Package has a package-private scope

In principle, public should not be added to a package that does not allow access by other classes.

  • It’s easy to remove features

Statistics show that no one is using the new feature, so that feature has to be removed from this version. If it’s PBL, you have to pull out all the code and classes that can be deleted from the function entry to the entire business process. If it is PBF, it is easy to say, delete the corresponding package first, and then delete the function entry (delete the package after the entry must be wrong), done.

  • Highly abstract

The general way to solve the problem is from abstract to concrete. PBF package names are abstractions of function modules, and classes within packages are implementation details, which correspond to abstract to concrete. PBL is the reverse. PBF starts from AppName, divides package according to function modules, and then considers the specific implementation details of each piece, while PBL should consider whether to have DAO layer, COM layer and so on from the very beginning.

  • Separate logical code only by class

PBL separates both classes and packages, while PBF separates logical code only by class.

  • The size of the package makes sense

The infinite growth of packages in PBL makes sense because more and more functions are added, and a large package in PBF means that the package needs to be refactured.

3. Naming conventions for Android

The naming of the code is strictly forbidden to use pinyin and English mixed way, let alone direct use of Chinese way. Correct English spelling and grammar make it easy for readers to understand and avoid ambiguity.

Note: Even pure pinyin naming should be avoided. However, international common names can be regarded as English, such as Toutiao, Douyin and so on.

3.1 the package name

Android has the concept of package, so we need to agree on the package name naming convention.

The package name must be lowercase and cannot contain Chinese, uppercase letters, or underscores (_). Name the submodule first and then based on PBF.

3.2 the name of the class

Class names are written in UpperCamelCase style.

A class name is usually a noun or noun phrase, and an interface name may sometimes be an adjective or adjective phrase. There is no specific rule or well-established convention for naming annotation types.

For nouns, use the big hump nomenclature. Avoid abbreviations unless the abbreviations are well-known, such as HTML or URL. If the class name contains abbreviations, capitalize each letter of the abbreviations.

class describe For example,
Activity Module name +Activity The splash screen page classSplashActivity
Fragment Module name +Fragment Home page classHomeFragment
Service Module name +Service Time serviceTimeService
BroadcastReceiver Function name +Receiver Push to receiveJPushReceiver
ContentProvider Function name +Provider ShareProvider
The custom View Function name + View/ViewGroup(Component name) ShapeButton
Dialog Dialog Function name + Dialog ImagePickerDialog
Adapter Module name +Adapter Course Details AdapterLessonDetailAdapter
Parsing class Function name +Parser Home Page parsing classHomePosterParser
Tool method class Function name +UtilsManager Thread pool management class:ThreadPoolManager

Logging tools:LogUtils(LoggerCan also be used)

Printing tools:PrinterUtils
The database class Function name +DBHelper News database:NewsDBHelper
Custom shared base classes Base+ base BaseActivity.BaseFragment
An abstract class Base / AbstractAt the beginning AbstractLogin
Exception class ExceptionAt the end LoginException
interface able / ibleEnd/I beginning Runnable.AccessibleILoginView

The name of a Test class starts with the name of the class it is testing and ends with Test. For example, HashTest or HashIntegrationTest.

Interface: The naming rules are the same as those of classes with the big camel name, and most of them end with able or ible, such as interface Runnable and interface Accessible.

Note: If the project uses MVP, all Model, View, and Presenter interfaces are prefixed with I without suffix, and all other interfaces are named in the same way.

3.3 the method name

Method names are written in lowerCamelCase style.

Method names are usually verbs or phrasal verbs.

methods instructions
initXX() Initialize related methods, such as the initialization layout, with init as the prefixinitView()
isXX().checkXX() If the return value of the method is Boolean, prefix it with is/check
getXX() A method that returns a value, prefixed with get
setXX() Sets a property value
handleXX().processXX() A method of processing data
displayXX().showXX() A dialog box and information are displayed. Use the prefix display/show to identify the information
updateXX() Update the data
saveXX().insertXX() Save or insert data
resetXX() Reset the data
clearXX() Clear data
removeXX().deleteXX() Remove data or views, as inremoveView()
drawXX() Draw data or effects, identified with the draw prefix

3.4 Variable Naming

Variables here are generalized variables, including constants, local variables, global variables, etc. Their basic rules are as follows:

  • The type needs to be a noun/noun phrase;
  • usinglowerCamelCaseStyle;

When naming a specific variable, additional naming rules will be added according to the type of the variable:

type instructions For example,
constant Uppercase and underscore, Kotlin must have const val const val TYPE_NORMAL = 1

static final TYPE_NORMAL = 1
Temporary variable name Integer:i,j,k,m,n, the character type is generally usedc,d,e for(int i = 0; i < len; i++)
Other variables lowerCamelCaseDo not use private variablesmAt the beginning private int tmp;
Kotlin Use of read-only variablesval, variable variable usevar, use it whenever possibleval var tmp = 0

val defaultIndex = 0

3.5 Naming Resources

Android resources include:

The resource files are named all lowercase and underlined.

3.5.1 Animation Resource Files (Anim/and Animator /)

Android mainly includes property animation and view animation, and view animation includes tween animation and frame-by-frame animation. Properties animation files need to be in res/animator/ and view animation files need to be in res/anim/. Naming rule: {module name _} Logical name.

Description: The content in {} is optional. The logical name can consist of multiple words underlined. XML, market_cart_add. XML, and market_cart_remove.xml.

For general tween animation or attribute animation, use the animation type _ direction naming method.

Such as:

The name of the instructions
fade_in Fade in
fade_out Fade out
push_down_in Push in from the bottom
push_down_out Push from below
push_left To the left
slide_in_from_top Slide in from the head
zoom_enter Deformation into
slide_in Sliding into the
shrink_to_middle In the middle to narrow

3.5.2 Color Resource File (color/)

Color/is a folder dedicated to color-related resources. Naming rules: Type {_ module name}_ Logical name.

Note: The content in {} is optional. For example, sel_btn_font. XML.

Color resources can also be placed in the res/drawable/ directory and referenced using @drawable, but this is not recommended and it is best to keep the two separate.

3.5.3 Image Resource Files (Drawable/and mipmap/)

The res/drawable/ directory places bitmap files (.png,.9.png,.jpg,.gif) or XML files compiled as subtypes of drawable object resources, while the res/mipmap/ directory places different densities of startup ICONS. Therefore, res/mipmap/ should only be used to store the startup icon. The rest of the image resource files should be placed in res/drawable/.

Naming rules: Type {_ module name}_ Logical name, type {_ module name}_ color.

Description: The content in {} is optional; The type can be a drawable object resource type, or a control type. The suffix _small indicates the small graph, and _BIG indicates the large graph.

Such as:

The name of the instructions
btn_main_about.png Home page about buttonsType _ Module name _ Logical name
btn_back.png Returns the buttonType _ Logical name
divider_maket_white.png Mall white division lineType _ Module name _ Color
ic_edit.png The edit iconType _ Logical name
bg_main.png Home page backgroundType _ Logical name
btn_red.png The red buttonType _ Color
btn_red_big.png Big red buttonType _ Color
ic_avatar_small.png Little avatar iconType _ Logical name
bg_input.png Input field backgroundType _ Logical name
divider_white.png White dividing lineType _ Color
bg_main_head.png Home header BackgroundType _ Module name _ Logical name
def_search_cell.png Search page for default unit imagesType _ Module name _ Logical name
ic_more_help.png More Help ICONSType _ Logical name
divider_list_line.png List splitterType _ Logical name
sel_search_ok.xml Search the interface for confirmation selectorType _ Module name _ Logical name
shape_music_ring.xml Music interface ring shapeType _ Module name _ Logical name

If there are multiple forms, such as button selector: sel_btn_xx.xml, use the following name: sel_btn_xx.xml

The name of the instructions
sel_btn_xx Role inbtn_xxOn theselector
btn_xx_normal Default effect
btn_xx_pressed state_pressedClick on the effect of
btn_xx_focused state_focusedFocus on results
btn_xx_disabled state_enabledUnavailable effect
btn_xx_checked state_checkedSelect the effect
btn_xx_selected state_selectedSelect the effect
btn_xx_hovered state_hoveredHover effect
btn_xx_checkable state_checkableOptional effect
btn_xx_activated state_activatedThe activation effect
btn_xx_window_focused state_window_focusedWindow focusing Effect

Note: Using the Android Studio plugin SelectorChapek, you can quickly generate selectors, as long as you name them properly.

3.5.4 Layout Resource File (Layout /)

Naming rules: Type _ module name, {module name _} Type _ logical name. (PBF is also used for easy viewing, especially in large projects)

Note: The content in {} is optional.

Such as:

type The name of the instructions
Activity main_activity.xml Main formModule name _ type
Fragment music_fragment.xml Music clipsModule name _ type
Dialog loading_dialog.xml Load dialogLogical name _ type
PopupWindow info_ppw.xml PopupWindowLogical name _ type
adapterA list of items main_song_item.xml Home song list itemModule name_type_Logical name

3.5.5 Layout Resource ID Naming

Naming rules: {module name _}_ Logical name _VIEW Abbreviation (function), for example, main_search_bTN and back_bTN. In addition, when using Kotlinx to get the layout file directly, the ID name is humped.

Note: The content in {} is optional. Refer to GoogleSamples Demo: github.com/android/arc…

Such as:

type specification After the sample
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 Resource File (Menu /)

Menu-related resource files should be placed in this directory. Naming rule: {module name _} Logical name

Note: The content in {} is optional. For example, main_drawer. XML and navigation.xml.

3.5.7 colors. XML

name is named using the underscore naming method. In your colors.xml file, it should be just the name of the mapped color, an ARGB value, and nothing else. Do not use it to define ARGB values for different buttons.

For example, don’t do something like this:

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

Using this format, it is very easy to redefine ARGB values, and it is very difficult to change the base color if the application needs to do so. Also, these definitions are associated with some environment, such as button or comment, and should be placed in a button style, not in the colors.xml file.

Instead, do this:

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

Ask the app designer for this palette. It doesn’t have to be named “green”, “blue”, etc. Names like brand_primary”, “brand_secondary”, and “brand_negative” are also perfectly acceptable. Standard colors like this are easy to modify or refactor, making it very clear how many different colors are used in the application. It is often important to reduce the number of colors used for a UI with aesthetic value.

Note: If some color is related to a theme, write a separate colors_theme.xml.

3.5.8 strings. XML

The name of

uses the underscore naming method, using the following rules: {module name _} logical name, so that all strings in the same interface can be placed together for easy searching.

The name of the instructions
main_menu_about Main menu key text
friend_title Friends module title bar
friend_dialog_del Friend Deletion Prompt
login_check_email Login authentication
dialog_title Popup title
button_ok Identify key
loading Loaded words

3.5.9 styles. The XML

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

Apply to TextView:

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

Maybe you need to do the same for button controls, don’t stop there, put a set of related and duplicate Android: XXXX properties into a common

4. Comment specification for Android

4.1 class annotation

Each class should be responsible for its own code, annotated with the author’s name and contact information.

/** * 
 * author: nanchen * E-mail: xxx@xx * time: 2021/01/18 * desc: XXXX Description * version: 1.0 * 

*/
public class WelcomeActivity {... }Copy the code

You can configure it yourself in AS. Go to 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 > * /
Copy the code

This will automatically add the header annotation each time a new class is created.

4.2 Method Notes

The method headers of every member method (including custom member methods, override methods, and attribute methods) must be annotated. Type /** + Enter or set Fix Doc comment (Settings -> Keymap -> Fix Doc comment) on the first line of the method, and AS will generate the template for you, AS shown below. The description of @param, @return, @throws, and @deprecated cannot be empty. When the description cannot fit on a single line, consecutive lines should be indented by at least four more Spaces.

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

4.3 block comment

Block comments are indent at the same level as the surrounding code. They can be /*… */ style, also can be //… Style (// preferably followed by a space). For multiple lines /*… */ comment, subsequent lines must start with * and align with the * on the previous line. The following sample comments are OK.

/* * This is okay. */

// And so
// is this.

/* Or you can
* even do this. */
Copy the code

Comments should not be enclosed in frames drawn by asterisks or other characters.

Tip: When writing multi-line comments, use this if you want to rewrap them if necessary

/ *… * /.

Such as:

4.4 Comments on global variables

Comments for global variables look like this (note the Spaces between comments) :

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

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

4.5 Other comments

AS has already integrated some comment templates for you. Just use them directly. Type TODO, FIXME, etc.

// TODO:17/3/14 Description of functionality that needs to be implemented, but is not yet implemented
// FIXME:17/3/14 Needs to be fixed, even if the code is wrong, does not work, needs to be fixed
Copy the code

4.5 Annotate the specifications that must be followed

4.5.1 Do not comment methods that are self-evident.

For example, Item getItem(int index) is a self-explanatory piece of code, and we can tell what it does from the name of the method, so we don’t need to add comments.

4.5.2 Proposed code should not have comments like TODO

5. Code style specification

5.1 Use standard curly brace styles

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(a) {
        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

5.2 Write a short method

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.

5.3 Order of class members

There is no single correct solution to this, but it will improve the readability of your code if you all use the same order. The following order is recommended:

  1. Constants (Kotlin’s associated object at the beginning)
  2. field
  3. The constructor
  4. Override functions and callbacks
  5. The public function
  6. Private functions
  7. Inner class or interface

Such as:

public class MainActivity extends Activity {

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

    private String mTitle;
    private TextView mTextViewTitle;

    @Override
    public void onCreate(a) {... }public void setTitle(String title) {
    	mTitle = title;
    }

    private void setUpView(a) {... }static class AnInnerClass {}}Copy the code

If a class inherits from an Android component (such as an Activity or Fragment), it’s a good practice to sort overridden functions by their lifecycle, for example, The Activity implements onCreate(), onDestroy(), onPause(), and onResume() in the correct order as follows:

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

    @Override
    public void onResume(a) {}

    @Override
    public void onPause(a) {}

    @Override
    public void onDestroy(a) {}}Copy the code

5.4 Sorting function parameters

Context is very common in function arguments during Android development, and it is best to use Context as its first argument.

Instead, the callback interface should be its last argument.

Such as:

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

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

5.5 Names and values of string constants

Many classes in the Android SDK use key-value pair functions, such as SharedPreferences, bundles, and Intents, so even small applications end up having to write a lot of string constants.

When we used these classes, we had to prefix their keys with static final fields and follow these instructions.

class Field name prefix
SharedPreferences PREF_
Bundle BUNDLE_
Fragment Arguments ARGUMENT_
Intent Extra EXTRA_
Intent Action ACTION_

Note: Although fragment.getarguments () gives you bundles, this is how bundles are used all the time, so I define a different prefix for this purpose.

Such as:

// Note: The value of the field is the same as the name to avoid duplicate problems
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 prefix their values with the full package name
static final String EXTRA_SURNAME = "com.myapp.extras.EXTRA_SURNAME";
static final String ACTION_OPEN_USER = "com.myapp.action.ACTION_OPEN_USER";
Copy the code

5.6 Line Length Restrictions

Each line of text in your code should be no longer than 160 characters. While there was a lot of debate about this rule, the final decision was to limit the line length to 160 characters. If the line length exceeds 160 (the vertical bar to the right of the AS window is the end of the set line width), there are usually two ways to reduce the line length.

  • Extract a local variable or method (preferably).
  • Replace one line with multiple lines using a newline character.

However, there are the following exceptions:

  • If the comment line contains a sample command or text url that is longer than 160 characters, the line can be longer than 160 characters for cut and paste purposes.
  • Importing statement lines can exceed this limit because they are rarely seen by the user (which also simplifies the tool writing process).

5.6.1 Line break Policy

There is no one exact solution for deciding how to wrap a line, and often different solutions are valid, but there are some rules that can be applied to common situations.

5.6.1.1 Line breaks for the operator

In addition to the assignment operator, we place the newline character before the operator, for example:

int longName = anotherVeryLongVariable + anEvenLongerOne - thisRidiculousLongOne
        + theFinalOne;
Copy the code

We place the newline after the assignment operator, for example:

int longName =
        anotherVeryLongVariable + anEvenLongerOne - thisRidiculousLongOne + theFinalOne;
Copy the code

5.6.1.2 Line breaks for function chains

When more than one function is called on the same line (such as when using a builder), the call to each function should be on a new line. Before.

Such as:

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

We should use the following rules:

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

5.6.1.3 Multi-parameter Line feed

When a method has many arguments or long arguments, we should wrap each of them.

Such as:

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

We should use the following rules:

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

5.6.1.4 RxJava chain newline

Each RxJava operator needs a newline, and the newline is inserted in. Before.

Such as:

public Observable<Location> syncLocations(a) {
    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 instanceofRetrofitError; }}); }Copy the code

Reference: source.android.com/source/code… Developer.android.com/kotlin/styl… Github.com/Blankj/Andr…

This article is simultaneously published on my wechat official account. You can follow it by searching Nanchen on wechat