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.
-
You must use the latest stable version of Android Studio for development;
-
The encoding format must be UTF-8.
-
Optimize Imports (Settings -> Keymap -> Optimize Imports) to remove unnecessary Imports and reduce warnings, use the Optimize Imports shortcut to set your preferences.
-
After editing.java,.kt, and.xml files, you must format them (after setting the following points)
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 reference, kotlinx. Android. Synthetic. Except for the main.
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:
-
There is high cohesion within package and low coupling between packages, which part needs to add new functions, only to change things under a certain package, while PBL needs to change multiple packages, which is very troublesome.
-
In principle, a package that is not accessible to other classes should not be made public.
-
It’s easy to remove features and statistics show that new features are not being used, so in this version that feature has to be removed. 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.
-
The general way to solve a problem with high abstraction is from abstract to concrete. PBF package names are abstractions of functional modules, and classes within packages are implementation details that conform to abstract to concrete. PBL does the opposite. 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.
-
Separating logical code by class only PBL separates both classes and packages, while PBF separates logical code by class only.
-
The size of the package makes sense because it makes sense for the package size to grow indefinitely in PBL, because more and more functions are added, whereas in PBF, too large a package (too many classes in the package) means that the package needs to be reconfigured.
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 +Utils 或 Manager |
Thread pool management class:ThreadPoolManager |
Logging tools:LogUtils (Logger Can 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 / Abstract At the beginning |
AbstractLogin |
Exception class | Exception At the end |
LoginException |
interface | able / ible End/I beginning |
Runnable .Accessible ,ILoginView |
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;
- using
lowerCamelCase
Style;
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 | lowerCamelCase Do not use private variablesm At 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_xx On theselector |
btn_xx_normal |
Default effect |
btn_xx_pressed |
state_pressed Click on the effect of |
btn_xx_focused |
state_focused Focus on results |
btn_xx_disabled |
state_enabled Unavailable effect |
btn_xx_checked |
state_checked Select the effect |
btn_xx_selected |
state_selected Select the effect |
btn_xx_hovered |
state_hovered Hover effect |
btn_xx_checkable |
state_checkable Optional effect |
btn_xx_activated |
state_activated The activation effect |
btn_xx_window_focused |
state_window_focused Window 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 |
adapter A 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
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.
/** * <pre> * author: nanchen * E-mail: xxx@xx * time: 2021/01/18 * desc: XXXX description * version: 1.0 * </pre> */ 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, if you want to rewrap them if necessary, use /*… * /.
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.
// FIXME: 17/3/14 needs to be fixed, even if the code is wrong, does not work, needs to be fixedCopy 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() { 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:
- Constants (Kotlin’s associated object at the beginning)
- field
- The constructor
- Override functions and callbacks
- The public function
- Private functions
- 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() {
...
}
public void setTitle(String title) {
mTitle = title;
}
private void setUpView() {
...
}
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() {}
@Override
public void onResume() {}
@Override
public void onPause() {}
@Override
public void onDestroy() {}
}
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:
Static final String PREF_EMAIL = "PREF_EMAIL"; static final String BUNDLE_AGE = "BUNDLE_AGE"; static final String ARGUMENT_USER_ID = "ARGUMENT_USER_ID"; Static final String EXTRA_SURNAME = "com.myapp.extras.extra_surname "; 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() { 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 instanceof RetrofitError; }}); }Copy the code
The original link: www.jianshu.com/p/e3afd461e…
At the end of the article
Your likes collection is the biggest encouragement to me! Welcome to follow me, share Android dry goods, exchange Android technology. If you have any comments or technical questions about this article, please leave a comment in the comments section.