Android lint检查

一、什么是Android lint 检查

Android lint是一个静态代码分析工具,能够对项目中潜在的bug,可优化的代码,安全性,性能,可行性,可访问性,国际化等检查。

二、lint工作流程

针对我们的项目,通过lint.xml配置lint分析的选项,执行检查后,会针对要分析的issue生成报告。

lintTool 把源码和lint.xml配置文件 打包成lint output,并展现出具体的代码哪里有问题。

三、如何配置lint

lint工具提供了对代码的一系列不同方面的问题进行检查的功能。每一个问题(issue)都有它唯一的id。这些issue是分类的。
可以通过为工程配置lint.xml来指定要分析哪些方面的问题:
每个issue一个标签,可以指定它的严重性(severity)为ignore、error、warning等:

<?xml version="1.0" encoding="UTF-8"?>
<lint>
    <!-- 忽略指定的检查 -->
    <issue id="IconMissingDensityFolder" severity="ignore" />

    <!-- 忽略指定文件的指定检查 -->
    <issue id="ObsoleteLayoutParam">
        <ignore path="res/layout/activation.xml" />
        <ignore path="res/layout-xlarge/activation.xml" />
    </issue>

    <!-- 更改检查问题归属的严重性 -->
    <issue id="HardcodedText" severity="error" />
</lint>

LintOptions配置

如果将 abortOnError 设置为true,自定义Lint很容易出现编译失败,此时检查message窗口将不必要的检查进行忽略禁用十分必要,例如 disable 'MissingTranslation' 此配置将忽略没有对strings.xml进行翻译报的错误。其他配置项查看下面各注释?

各个配置含义

android {
lintOptions {
    // 设置为 true时lint将不报告分析的进度
    quiet true
    // 如果为 true,则当lint发现错误时停止 gradle构建
    abortOnError false
    // 如果为 true,则只报告错误
    ignoreWarnings true
    // 如果为 true,则当有错误时会显示文件的全路径或绝对路径 (默认情况下为true)
    //absolutePaths true
    // 如果为 true,则检查所有的问题,包括默认不检查问题
    checkAllWarnings true
    // 如果为 true,则将所有警告视为错误
    warningsAsErrors true
    // 不检查给定的问题id
    disable 'TypographyFractions','TypographyQuotes'
    // 检查给定的问题 id
    enable 'RtlHardcoded','RtlCompat', 'RtlEnabled'
    // * 仅 * 检查给定的问题 id
    check 'NewApi', 'InlinedApi'
    // 如果为true,则在错误报告的输出中不包括源代码行
    noLines true
    // 如果为 true,则对一个错误的问题显示它所在的所有地方,而不会截短列表,等等。
    showAll true
    // 重置 lint 配置(使用默认的严重性等设置)。
    lintConfig file("default-lint.xml")
    // 如果为 true,生成一个问题的纯文本报告(默认为false)
    textReport true
    // 配置写入输出结果的位置;它可以是一个文件或 “stdout”(标准输出)
    textOutput 'stdout'
    // 如果为真,会生成一个XML报告,以给Jenkins之类的使用
    xmlReport false
    // 用于写入报告的文件(如果不指定,默认为lint-results.xml)
    xmlOutput file("lint-report.xml")
    // 如果为真,会生成一个HTML报告(包括问题的解释,存在此问题的源码,等等)
    htmlReport true
    // 写入报告的路径,它是可选的(默认为构建目录下的 lint-results.html )
    htmlOutput file("lint-report.html")
    // 设置为 true, 将使所有release 构建都以issus的严重性级别为fatal(severity=false)的设置来运行lint
    // 并且,如果发现了致命(fatal)的问题,将会中止构建(由上面提到的 abortOnError 控制)
    checkReleaseBuilds true
    // 设置给定问题的严重级别(severity)为fatal (这意味着他们将会
    // 在release构建的期间检查 (即使 lint 要检查的问题没有包含在代码中)
    fatal 'NewApi', 'InlineApi'
    // 设置给定问题的严重级别为error
    error 'Wakelock', 'TextViewEdits'
    // 设置给定问题的严重级别为warning
    warning 'ResourceAsColor'
    // 设置给定问题的严重级别(severity)为ignore (和不检查这个问题一样)
    ignore 'TypographyQuotes'
    }
}

 

四、java代码中和xml布局中使用

有时候会出现不准确的lint检查,如果你认为自己的java代码没有问题可以添加如下注解:

@SuppressLint 来抑制当前代码输出lint检查

xml中

抑制警告使用: tools:ignore.

要禁用多个 issue 时,用逗号把它们的 issue id 分隔开,比如:

tools:ignore="NewApi,StringFormatInvalid"

要在某个 XML 元素中对所有 issue 都禁用 lint 检查,可以使用 all 关键字,比如:

tools:ignore="all"

五、检查示意

#英文示意
"ContentDescription": Image without contentDescription
"AddJavascriptInterface": addJavascriptInterface Called
"ShortAlarm": Short or Frequent Alarm
"AllCaps": Combining textAllCaps and markup
"AllowAllHostnameVerifier": Insecure HostnameVerifier
"AlwaysShowAction": Usage of showAsAction=always
"InvalidUsesTagAttribute": Invalid name attribute for uses element.
"MissingIntentFilterForMediaSearch": Missing intent-filter with action
      android.media.action.MEDIA_PLAY_FROM_SEARCH
"MissingMediaBrowserServiceIntentFilter": Missing intent-filter with action
      android.media.browse.MediaBrowserService.
"MissingOnPlayFromSearch": Missing onPlayFromSearch.
"ImpliedTouchscreenHardware": Hardware feature touchscreen not explicitly
      marked as optional
"MissingTvBanner": TV Missing Banner
"MissingLeanbackLauncher": Missing Leanback Launcher Intent Filter.
"MissingLeanbackSupport": Missing Leanback Support.
"PermissionImpliesUnsupportedHardware": Permission Implies Unsupported
      Hardware
"UnsupportedTvHardware": Unsupported TV Hardware Feature
"SupportAnnotationUsage": Incorrect support annotation usage
"ShiftFlags": Dangerous Flag Constant Declaration
"LocalSuppress": @SuppressLint on invalid element
"SwitchIntDef": Missing @IntDef in Switch
"UniqueConstants": Overlapping Enumeration Constants
"InlinedApi": Using inlined constants on older versions
"Override": Method conflicts with new inherited method
"ObsoleteSdkInt": Obsolete SDK_INT Version Check
"NewApi": Calling new methods on older versions
"UnusedAttribute": Attribute unused on older versions
"AppCompatMethod": Using Wrong AppCompat Method
"AppCompatCustomView": Appcompat Custom Widgets
"AppCompatResource": Menu namespace
"GoogleAppIndexingApiWarning": Missing support for Firebase App Indexing Api
"GoogleAppIndexingWarning": Missing support for Firebase App Indexing
"AppLinksAutoVerifyError": App Links Auto Verification Failure
"AppLinksAutoVerifyWarning": Potential App Links Auto Verification Failure
"AppLinkUrlError": URL not supported by app for Firebase App Indexing
"TestAppLink": Unmatched URLs
"InconsistentArrays": Inconsistencies in array element counts
"Assert": Assertions
"BadHostnameVerifier": Insecure HostnameVerifier
"BatteryLife": Battery Life Issues
"BackButton": Back button
"ButtonCase": Cancel/OK dialog button capitalization
"ButtonOrder": Button order
"ButtonStyle": Button should be borderless
"ByteOrderMark": Byte order mark inside files
"MissingSuperCall": Missing Super Call
"AdapterViewChildren": AdapterViews cannot have children in XML
"ScrollViewCount": ScrollViews can have only one child
"PermissionImpliesUnsupportedChromeOsHardware": Permission Implies Unsupported
      Chrome OS Hardware
"UnsupportedChromeOsHardware": Unsupported Chrome OS Hardware Feature
"GetInstance": Cipher.getInstance with ECB
"CommitTransaction": Missing commit() calls
"Recycle": Missing recycle() calls
"CommitPrefEdits": Missing commit() on SharedPreference editor
"ApplySharedPref": Use apply() on SharedPreferences
"ClickableViewAccessibility": Accessibility in Custom Views
"EasterEgg": Code contains easter egg
"StopShip": Code contains STOPSHIP marker
"MissingConstraints": Missing Constraints in ConstraintLayout
"VulnerableCordovaVersion": Vulnerable Cordova Version
"CustomViewStyleable": Mismatched Styleable/Custom View Name
"CutPasteId": Likely cut & paste mistakes
"SimpleDateFormat": Implied locale in date format
"SetTextI18n": TextView Internationalization
"Deprecated": Using deprecated resources
"MissingPrefix": Missing Android XML namespace
"MangledCRLF": Mangled file line endings
"DuplicateIncludedIds": Duplicate ids across layouts combined with include
      tags
"DuplicateIds": Duplicate ids within a single layout
"DuplicateDefinition": Duplicate definitions of resources
"ReferenceType": Incorrect reference types
"StringEscaping": Invalid string escapes
"UnpackedNativeCode": Missing android:extractNativeLibs=false
"UnsafeDynamicallyLoadedCode": load used to dynamically load code
"UnsafeNativeCodeLocation": Native code outside library directory
"EllipsizeMaxLines": Combining Ellipsize and Maxlines
"ExifInterface": Using android.media.ExifInterface
"ExtraText": Extraneous text in resource files
"FieldGetter": Using getter instead of field
"InvalidAnalyticsName": Invalid Analytics Name
"MissingFirebaseInstanceTokenRefresh": Missing Firebase Instance ID Token
      Refresh
"FontValidationError": Validation of font files
"FontValidationWarning": Validation of font files
"FullBackupContent": Valid Full Backup Content File
"ValidFragment": Fragment not instantiatable
"GetContentDescriptionOverride": Overriding getContentDescription() on a View
"PackageManagerGetSignatures": Potential Multiple Certificate Exploit
"AccidentalOctal": Accidental Octal
"UseOfBundledGooglePlayServices": Use of bundled version of Google Play
      services
"GradleCompatible": Incompatible Gradle Versions
"GradleDependency": Obsolete Gradle Dependency
"GradleDeprecated": Deprecated Gradle Construct
"DevModeObsolete": Dev Mode Obsolete
"DuplicatePlatformClasses": Duplicate Platform Classes
"GradleGetter": Gradle Implicit Getter Call
"GradlePluginVersion": Incompatible Android Gradle Plugin
"HighAppVersionCode": VersionCode too high
"GradleIdeError": Gradle IDE Support Issues
"GradlePath": Gradle Path Issues
"GradleDynamicVersion": Gradle Dynamic Version
"NotInterpolated": Incorrect Interpolation
"StringShouldBeInt": String should be int
"NewerVersionAvailable": Newer Library Versions Available
"MinSdkTooLow": API Version Too Low
"GridLayout": GridLayout validation
"HandlerLeak": Handler reference leaks
"HardcodedDebugMode": Hardcoded value of android:debuggable in the manifest
"HardcodedText": Hardcoded text
"HardwareIds": Hardware Id Usage
"IconDuplicatesConfig": Identical bitmaps across various configurations
"IconDuplicates": Duplicated icons under different names
"GifUsage": Using .gif format for bitmaps is discouraged
"IconColors": Icon colors do not follow the recommended visual style
"IconDensities": Icon densities validation
"IconDipSize": Icon density-independent size validation
"IconExpectedSize": Icon has incorrect size
"IconExtension": Icon format does not match the file extension
"IconLauncherShape": The launcher icon shape should use a distinct silhouette
"IconLocation": Image defined in density-independent drawable folder
"IconMissingDensityFolder": Missing density folder
"IconMixedNinePatch": Clashing PNG and 9-PNG files
"IconNoDpi": Icon appears in both -nodpi and dpi folders
"IconXmlAndPng": Icon is specified both as .xml file and as a bitmap
"ConvertToWebp": Convert to WebP
"WebpUnsupported": WebP Unsupported
"IncludeLayoutParam": Ignored layout params on include
"DisableBaselineAlignment": Missing baselineAligned attribute
"InefficientWeight": Inefficient layout weight
"NestedWeights": Nested layout weights
"Orientation": Missing explicit orientation
"Suspicious0dp": Suspicious 0dp dimension
"InstantApps": Instant App Issues
"DuplicateDivider": Unnecessary Divider Copy
"TrustAllX509TrustManager": Insecure TLS/SSL trust manager
"InvalidImeActionId": Invalid imeActionId declaration
"InvalidPackage": Package not included in Android
"DrawAllocation": Memory allocations within drawing code
"UseSparseArrays": HashMap can be replaced with SparseArray
"UseValueOf": Should use valueOf instead of new
"JavascriptInterface": Missing @JavascriptInterface on methods
"JobSchedulerService": JobScheduler problems
"KeyboardInaccessibleWidget": Keyboard inaccessible widget
"LabelFor": Missing labelFor attribute
"InconsistentLayout": Inconsistent Layouts
"InflateParams": Layout Inflation without a Parent
"StaticFieldLeak": Static Field Leaks
"DefaultLocale": Implied default locale in case conversion
"LocaleFolder": Wrong locale name
"GetLocales": Locale crash
"InvalidResourceFolder": Invalid Resource Folder
"WrongRegion": Suspicious Language/Region Combination
"UseAlpha2": Using 3-letter Codes
"LogConditional": Unconditional Logging Calls
"LongLogTag": Too Long Log Tags
"LogTagMismatch": Mismatched Log Tags
"AllowBackup": AllowBackup/FullBackupContent Problems
"MissingApplicationIcon": Missing application icon
"DeviceAdmin": Malformed Device Admin
"DuplicateActivity": Activity registered more than once
"DuplicateUsesFeature": Feature declared more than once
"GradleOverrides": Value overridden by Gradle build script
"IllegalResourceRef": Name and version must be integer or string, not
      resource
"MipmapIcons": Use Mipmap Launcher Icons
"MockLocation": Using mock location provider in production
"MultipleUsesSdk": Multiple <uses-sdk> elements in the manifest
"ManifestOrder": Incorrect order of elements in manifest
"MissingVersion": Missing application name/version
"OldTargetApi": Target SDK attribute is not targeting latest version
"UniquePermission": Permission names are not unique
"UsesMinSdkAttributes": Minimum SDK and target SDK attributes not defined
"WearableBindListener": Usage of Android Wear BIND_LISTENER is deprecated
"WrongManifestParent": Wrong manifest parent
"InvalidPermission": Invalid Permission Attribute
"ManifestResource": Manifest Resource References
"ManifestTypo": Typos in manifest tags
"FloatMath": Using FloatMath instead of Math
"MergeMarker": Code contains merge marker
"MergeRootFrame": FrameLayout can be replaced with <merge> tag
"IncompatibleMediaBrowserServiceCompatVersion": Obsolete version of
      MediaBrowserServiceCompat
"InnerclassSeparator": Inner classes should use $ rather than .
"Instantiatable": Registered class is not instantiatable
"MissingRegistered": Missing registered class
"MissingId": Fragments should specify an id or tag
"LibraryCustomView": Custom views in libraries should use res-auto-namespace
"ResAuto": Hardcoded Package in Namespace
"NamespaceTypo": Misspelled namespace declaration
"UnusedNamespace": Unused namespace
"NegativeMargin": Negative Margins
"NestedScrolling": Nested scrolling widgets
"NetworkSecurityConfig": Valid Network Security Config File
"MissingBackupPin": Missing Backup Pin
"PinSetExpiry": Validate <pin-set> expiration attribute
"NfcTechWhitespace": Whitespace in NFC tech lists
"UnlocalizedSms": SMS phone number missing country code
"ObjectAnimatorBinding": Incorrect ObjectAnimator Property
"AnimatorKeep": Missing @Keep for Animated Properties
"ObsoleteLayoutParam": Obsolete layout params
"OnClick": onClick method does not exist
"Overdraw": Overdraw: Painting regions more than once
"DalvikOverride": Method considered overridden by Dalvik
"OverrideAbstract": Not overriding abstract methods on older platforms
"ParcelCreator": Missing Parcelable CREATOR field
"UnusedQuantity": Unused quantity translations
"MissingQuantity": Missing quantity translation
"ImpliedQuantity": Implied Quantities
"ExportedPreferenceActivity": PreferenceActivity should not be exported
"PrivateApi": Using Private APIs
"PackagedPrivateKey": Packaged private key
"PrivateResource": Using private resources
"ProguardSplit": Proguard.cfg file contains generic Android rules
"Proguard": Using obsolete ProGuard configuration
"PropertyEscape": Incorrect property escapes
"UsingHttp": Using HTTP instead of HTTPS
"SpUsage": Using dp instead of sp for text sizes
"InOrMmUsage": Using mm or in dimensions
"PxUsage": Using 'px' dimension
"SmallSp": Text size is too small
"ParcelClassLoader": Default Parcel Class Loader
"PendingBindings": Missing Pending Bindings
"RecyclerView": RecyclerView Problems
"Registered": Class is not registered in the manifest
"RelativeOverlap": Overlapping items in RelativeLayout
"RequiredSize": Missing layout_width or layout_height attributes
"AaptCrash": Potential AAPT crash
"ResourceCycle": Cycle in resource definitions
"ResourceName": Resource with Wrong Prefix
"ValidRestrictions": Invalid Restrictions Descriptor
"RtlCompat": Right-to-left text compatibility issues
"RtlEnabled": Using RTL attributes without enabling RTL support
"RtlSymmetry": Padding and margin symmetry
"RtlHardcoded": Using left/right instead of start/end attributes
"ScrollViewSize": ScrollView size validation
"SdCardPath": Hardcoded reference to /sdcard
"SecureRandom": Using a fixed seed with SecureRandom
"TrulyRandom": Weak RNG
"ExportedContentProvider": Content provider does not require permission
"ExportedReceiver": Receiver does not require permission
"ExportedService": Exported service does not require permission
"SetWorldReadable": File.setReadable() used to make file world-readable
"SetWorldWritable": File.setWritable() used to make file world-writable
"GrantAllUris": Content provider shares everything
"WorldReadableFiles": openFileOutput() or similar call passing
      MODE_WORLD_READABLE
"WorldWriteableFiles": openFileOutput() or similar call passing
      MODE_WORLD_WRITEABLE
"ServiceCast": Wrong system service casts
"WifiManagerLeak": WifiManager Leak
"WifiManagerPotentialLeak": WifiManager Potential Leak
"SetJavaScriptEnabled": Using setJavaScriptEnabled
"SignatureOrSystemPermissions": signatureOrSystem permissions declared
"SQLiteString": Using STRING instead of TEXT
"SSLCertificateSocketFactoryCreateSocket": Insecure call to
      SSLCertificateSocketFactory.createSocket()
"SSLCertificateSocketFactoryGetInsecure": Call to
      SSLCertificateSocketFactory.getInsecure()
"StateListReachable": Unreachable state in a <selector>
"AuthLeak": Code might contain an auth leak
"StringFormatCount": Formatting argument types incomplete or inconsistent
"StringFormatMatches": String.format string doesn't match the XML format
      string
"StringFormatInvalid": Invalid format string
"PluralsCandidate": Potential Plurals
"UseCheckPermission": Using the result of check permission calls
"CheckResult": Ignoring results
"ResourceAsColor": Should pass resolved color instead of resource id
"MissingPermission": Missing Permissions
"Range": Outside Range
"ResourceType": Wrong Resource Type
"RestrictedApi": Restricted API
"WrongThread": Wrong Thread
"WrongConstant": Incorrect constant
"VisibleForTests": Visible Only For Tests
"ProtectedPermissions": Using system app permission
"TextFields": Missing inputType or hint
"TextViewEdits": TextView should probably be an EditText instead
"SelectableText": Dynamic text should probably be selectable
"MenuTitle": Missing menu title
"ShowToast": Toast created but not shown
"TooDeepLayout": Layout hierarchy is too deep
"TooManyViews": Layout has too many views
"ExtraTranslation": Extra translation
"MissingTranslation": Incomplete translation
"Typos": Spelling error
"TypographyDashes": Hyphen can be replaced with dash
"TypographyEllipsis": Ellipsis string can be replaced with ellipsis character
"TypographyFractions": Fraction string can be replaced with fraction
      character
"TypographyOther": Other typographical problems
"TypographyQuotes": Straight quotes can be replaced with curvy quotes
"UnsafeProtectedBroadcastReceiver": Unsafe Protected BroadcastReceiver
"UnprotectedSMSBroadcastReceiver": Unprotected SMS BroadcastReceiver
"UnusedResources": Unused resources
"UnusedIds": Unused id
"UseCompoundDrawables": Node can be replaced by a TextView with compound
      drawables
"UselessLeaf": Useless leaf layout
"UselessParent": Useless parent layout
"EnforceUTF8": Encoding used in resource files is not UTF-8
"VectorRaster": Vector Image Generation
"VectorDrawableCompat": Using VectorDrawableCompat
"VectorPath": Long vector paths
"InvalidVectorPath": Invalid vector paths
"ViewConstructor": Missing View constructors for XML inflation
"ViewHolder": View Holder Candidates
"ViewTag": Tagged object leaks
"WrongViewCast": Mismatched view type
"FindViewByIdCast": Add Explicit Cast
"Wakelock": Incorrect WakeLock usage
"WakelockTimeout": Using wakeLock without timeout
"InvalidWearFeatureAttribute": Invalid attribute for Wear uses-feature
"WearStandaloneAppFlag": Invalid or missing Wear standalone app flag
"WebViewLayout": WebViews in wrap_content parents
"WrongCall": Using wrong draw/layout method
"WrongCase": Wrong case for view tag
"InvalidId": Invalid ID declaration
"NotSibling": RelativeLayout Invalid Constraints
"UnknownId": Reference to an unknown id
"UnknownIdInLayout": Reference to an id that is not in the current layout
"SuspiciousImport": 'import android.R' statement
"WrongFolder": Resource file in the wrong res folder
"WrongThreadInterprocedural": Wrong Thread (Interprocedural)

#中文示意

1. Correctness 

1) Appcompat Custom Widgets
  Appcompat自定义小部件一般会让你继承自 android.support.v7.widget.AppCompat...
  不要直接扩展android.widget类,而应该扩展android.support.v7.widget.AppCompat中的一个委托类。
2) Attribute unused on older versions
  旧版本未使用的属性  针对具有minSdkVersion
  这不是一个错误; 应用程序将简单地忽略该属性。
  可以选择在layout-vNN文件夹中创建一个布局的副本 将在API NN或更高版本上使用,您可以利用更新的属性。
3) Class is not registered in the manifest
  类未在清单中注册
  Activities, services and content providers should be registered in the AndroidManifest.xml file
4) Combining Ellipsize and Maxlines
  Ellipsize和Maxlines相结合
  结合ellipsize和maxLines = 1可能导致某些设备崩溃。 早期版本的lint建议用maxLines = 1替换singleLine = true,但在使用ellipsize时不应该这样做
5) Extraneous text in resource files
  资源文件中的无关文本
6) Hardcoded reference to /sdcard
  硬编码参考/ SD卡
  代码不应该直接引用/ sdcard路径; 而是使用:Environment.getExternalStorageDirectory().getPath()
  不要直接引用/ data / data /路径; 它可以在多用户场景中有所不同。
7) Implied default locale in case conversion
  在转换的情况下默认的默认语言环境
8) Implied locale in date format
  隐含的日期格式的区域设置
  调用者都应该使用getDateInstance(),getDateTimeInstance()或getTimeInstance()来获得适合用户语言环境的SimpleDateFormat的现成实例。
9) Likely cut & paste mistakes
  可能剪切和粘贴错误
  剪切和粘贴调用findViewById但忘记更新R.id字段的情况。 有可能你的代码只是(冗余)重复查找字段
10) Mismatched Styleable/Custom View Name
  不匹配的样式/自定义视图名称
  自定义视图的惯例是使用名称与自定义视图类名称相匹配的声明样式。
11) Missing Permissions
  缺少权限
12) Nested scrolling widgets
  嵌套的滚动小部件
  A scrolling widget such as a ScrollView should not contain any nested scrolling widgets since this has various usability issues
13) Obsolete Gradle Dependency    
  已过时的Gradle依赖关系
14) Target SDK attribute is not targeting latest version    
  目标SDK属性未定位到最新版本
15) Using 'px' dimension    
  使用“px”维度
16) Using android.media.ExifInterface    
  使用android.media.ExifInterface旧版的有一些漏洞,使用支持库中的
17) Using dp instead of sp for text sizes    
  使用dp代替文本大小的sp
18) Using Private APIs    
  使用私有API
19) Using private resources    
  使用私人资源

2. Internationalization

1) Hardcoded text  
  硬编码文本
  直接在布局文件中对文本属性进行硬编码是有缺陷的
  should use @string resource
2) Overlapping items in RelativeLayout  
  在RelativeLayout中重叠项目
  如果相对布局的文本或按钮项左右对齐,则由于本地化的文本扩展,它们可以相互重叠,除非它们具有toEndOf / toStartOf之类的相互约束。
3) Padding and margin symmetry   
  填充和边缘对称
  如果您在布局的左侧指定填充或边距,则应该也可以在右侧指定填充(反之亦然),以便从右到左布局对称。
4) TextView Internationalization   
  TextView国际化
  永远不要调用Number#toString()来格式化数字; 它不会正确处理分数分隔符和区域特定的数字
  使用具有适当格式规范(%d或%f)的String#格式
  不要传递字符串(例如“Hello”)来显示文本。 硬编码文本无法正确翻译成其他语言,考虑使用Android资源字符串
  不要通过连接文本块来构建消息。 这样的消息不能被正确翻译。
5) Using left/right instead of start/end attributes   
  使用左/右而不是开始/结束属性

3. Performance

1) Handler reference leaks
  handler导致的泄漏
  由于该Handler被声明为内部类,所以可以防止外部类被垃圾收集。 如果处理程序对主线程以外的线程使用Looper或MessageQueue,则不存在问题。 如果处理程序正在使用主线程的Looper或MessageQueue,则需要修复Handler声明,
  解决:将Handler声明为静态类; 在外部类中,实例化WeakReference到外部类,并在实例化Handler时将此对象传递给Handler; 使用WeakReference对象来引用外部类的所有成员。
2) HashMap can be replaced with SparseArray
  HashMap可以用SparseArray替换
  对于键类型为integer的映射,使用Android SparseArray API通常效率更高。
3) Inefficient layout weight
  低效的布局权重
  当LinearLayout中只有一个控件定义了一个权重时,为它指定一个0dp的宽度/高度会更有效率,因为它将吸收所有的剩余空间。 如果声明的宽度/高度为0dp,则不必首先测量其自己的大小。
4) Layout has too many views
  布局有太多的意见
  在单个布局中使用太多的视图对性能不利。 考虑使用复合绘图或其他技巧来减少此布局中的视图数量。 最大视图数量默认为80,但可以使用环境变量ANDROID_LINT_MAX_VIEW_COUNT进行配置。
5) Layout hierarchy is too deep
  布局层次太深
  嵌套太多的布局对性能不利。 考虑使用更平坦的布局(比如RelativeLayout或GridLayout)。默认的最大深度是10,但可以使用环境变量ANDROID_LINT_MAX_DEPTH进行配置。
6) Memory allocations within drawing code
  内存分配在绘图代码
  应该避免在绘图或布局操作中分配对象。 这些被频繁地调用,所以平滑的UI可以被对象分配造成的垃圾收集暂停中断。 通常处理的方式是预先分配所需的对象,并为每个绘图操作重新使用它们。 有些方法代表您分配内存(如Bitmap.create),并且应该以相同的方式处理这些内存。
7) Missing @Keep for Animated Properties
  属性动画缺少@Keep
  当你使用属性动画师时,属性可以通过反射来访问。 这些方法应该使用@Keep注释,以确保在发布构建期间,这些方法不会被视为未被使用和删除,或者被视为内部的,并被重新命名为更短。 这个检查还会标记出其他可能遇到的反射问题,比如缺少属性,错误的参数类型等等。
8) Missing baselineAligned attribute    
  缺少baselineAligned属性
  当使用LinearLayout在嵌套布局之间按比例分配空间时,应关闭基线对齐属性以使布局计算速度更快。
9) Node can be replaced by a TextView with compound drawables
  节点可以用复合可绘制的TextView替换
  包含ImageView和TextView的LinearLayout可以更有效地处理为复合可绘制(单个TextView,使用drawableTop,drawableLeft,drawableRight和/或drawableBottom属性在文本旁边绘制一个或多个图像)。 如果这两个小部件彼此之间有空白,则可以用drawablePadding属性替换。
10) Obsolete layout params
  过时的布局参数
11) Obsolete SDK_INT Version Check
  已过时的SDK_INT版本检查
  此检查标志版本检查不是必需的,因为minSdkVersion(或周围已知的API级别)已经至少与检查的版本一样高。
  它还会在-vNN文件夹中查找资源,如版本限定符小于或等于minSdkVersion的values-v14,其中内容应合并到最佳文件夹中。
12) Static Field Leaks
  静态常量--持有fragment及activity的引用
  非静态内部类对其外部类具有隐式引用。
  如果该外部类是例如fragment或activity,如果长时间运行的处理程序/加载程序/任务将持有对该activity的引用,长时间没有被回收掉。
13) Useless parent layout
  无用的父母布局
  有没有兄弟的孩子的布局不是滚动视图或根布局,并且没有背景,可以被移除并且其子节点直接移动到父节点以获得更平坦和更高效的布局分层结构。
14) View Holder Candidates
  查看持有人候选人
  Should use View Holder pattern

4. Security

1) Cipher.getInstance with ECB
  Cipher.getInstance与ECB
  不应使用ECB作为cipher mode或不设置cipher mode来调用Cipher#getInstance,因为android上的默认模式是ECB,这是不安全的。(加解密)
2) Content provider does not require permission
  内容提供者不需要权限
  内容提供程序默认导出,系统上的任何应用程序都可能使用它们来读取和写入数据。 如果内容提供者提供对敏感数据的访问,则应该通过在清单中指定export = false来保护它,或者通过可以授予其他应用程序的权限来保护它。
3) Exported service does not require permission
  导出的服务不需要权限
  导出的服务(设置了exported = true或者包含intent-filter并且不指定exported = false的服务)应该定义一个实体为了启动服务或绑定到服务而必须拥有的权限。 没有这个,任何应用程序都可以使用此服务。
4) Hardware Id Usage
  硬件ID使用情况
  不建议使用这些设备标识符,除了高价值欺诈预防和高级电话使用情况。
  getLine1Number获取手机号,getDeviceId设备IMEI,getMacAddressMAC地址
5) Incorrect constant
  不正确的常量
6) Insecure TLS/SSL trust manager
  不安全的TLS / SSL信任管理器
7) Missing @JavascriptInterface on methods
  缺少@JavascriptInterface方法
8) openFileOutput() or similar call passing MODE_WORLD_READABLE
  openFileOutput()或类似的调用传递MODE_WORLD_READABLE
9) openFileOutput() or similar call passing MODE_WORLD_WRITEABLE
  openFileOutput()或类似的调用传递MODE_WORLD_WRITEABLE
  在某些情况下,应用程序可以编写世界可写文件,但应仔细检查这些文件以确保它们不包含私人数据,并且如果文件被恶意应用程序修改,则不会欺骗或破坏应用程序。
10) Receiver does not require permission
  接收者不需要许可
11) Using setJavaScriptEnabled  使用setJavaScriptEnabled
  如果您不确定您的应用程序确实需要JavaScript支持,那么您的代码不应该调用setJavaScriptEnabled。

5. Usability

1) Button should be borderless
  按钮应该是无边界的
  两个 Buttons 放在一个布局里会被判断为按钮栏,需要添加样式取消它的边框
  在 Buttons 上添加属性 style="?android:attr/buttonBarButtonStyle" 。系统提示也可以在按钮的父布局上添加 style="? android:attr/buttonBarStyle" 属性
2) Ellipsis string can be replaced with ellipsis character
  省略号字符串可以用省略号字符替换
  Replace "..." with ellipsis character (…, …) ?
3) Hyphen can be replaced with dash
  连字符可以用短划线代替
  Replace "-" with an "en dash" character (–, –) ?
4) Missing View constructors for XML inflation
  缺少XML通货膨胀的视图构造函数
5) Text size is too small
  文字太小
  避免使用小于12sp的尺寸。小于12sp的字体会太小导致用户看不清

二. 其他类型
  Class structure 类结构
  Code maturity issues 代码成熟度问题
  Code style issues 代码样式问题
  Compiler issues 编译器问题
  Control flow issues 控制流量问题
  Data flow issues 数据流问题
  Declaration redundancy 声明冗余
  Error handling 错误处理
  General 一般
  Imports 进口
  J2ME issues J2ME问题
  Java 5 Java 5
  Java 7 Java 7
  Java language level migration aids Java语言级别的迁移辅助
  Javadoc issues Javadoc问题
  Naming conventions 命名约定
  Numeric issues 数字问题
  Performance issues 性能问题
  Probable bugs 可能的错误
  Properties Files 属性文件
  Spelling 拼字
  Style 样式
  Verbose or redundant code constructs 详细或冗余的代码结构
  XML XML
1. Class structure
  Field can be local字段可以是本地的
  Parameter can be local参数可以是本地的
  'private' method declared 'final'
  'static' method declared 'final'
2. Code maturity issues 代码成熟度问题
  Deprecated API usage不推荐使用API
  Deprecated member is still used不推荐使用的成员仍在使用
3. Code style issues 代码样式问题
  Unnecessary enum modifier不必要的枚举修饰符
  Unnecessary interface modifier不必要的界面修饰符
  Unnecessary semicolon不必要的分号
  private  public
4. Compiler issues 编译器问题
  Unchecked warning未经检查的警告
5. Control flow issues 控制流问题
  Double negation 双重否定
  Pointless boolean expression 无意义的布尔表达式
  Redundant 'if' statement 冗余“if”语句
  Redundant conditional expression 冗余的条件表达式
  Simplifiable boolean expression 简化布尔表达式
  Simplifiable conditional expression 简化条件表达式
  Unnecessary 'return' statement 不必要的“return”声明
  return;
6. Data flow issues 数据流问题
  Boolean method is always inverted布尔方法总是倒置的
  Redundant local variable冗余局部变量
7. Declaration redundancy 声明冗余
  Access static member via instance reference通过实例引用访问静态成员
  this.minsize = this.maxsize;
  Actual method parameter is the same constant实际的方法参数是相同的常量
  Actual value of parameter ''register'' is always ''true''
  Declaration access can be weaker声明访问权限可以再弱
  Can be private
  Declaration can have final modifier宣言可以有最终的修改
  Duplicate throws重复抛出
  Empty method空方法
  Method can be void方法可以是无效的
  Method returns the same value方法返回相同的值
  All implementations of this method always return '3'
  Redundant throws clause冗余抛出子句
  The declared exception 'UnsupportedEncodingException' is never thrown
  Unnecessary module dependency不必要的模块依赖
  Unused declaration未使用的声明(方法,变量)
8. Error handling 错误处理
  Caught exception is immediately rethrown捕获到的异常立即被重新抛出
  Empty 'catch' block空'catch'块
  'return' inside 'finally' block在'finally'块中'返回'
  'throw' inside 'finally' block在“finally”块内“抛出”
9. General
  Annotator注解者
  Default File Template Usage默认文件模板的用法
10. Imports 导入
  Unused import没有用到的导入
11. J2ME issues J2ME问题
  'if'语句可以用&&或||代替 表达
12. Java 5 Java 5
  'for' loop replaceable with 'foreach''for'循环可替换为'foreach'
  'indexOf()' expression is replaceable with 'contains()''indexOf()'表达式可以用'contains()'来替换
  'StringBuffer' may be 'StringBuilder''StringBuffer'可能是'StringBuilder'
  Unnecessary boxing不必要的装箱
  Unnecessary unboxing不必要的拆箱
  'while' loop replaceable with 'foreach''while'循环可以替换'foreach'
13. Java 7 Java 7
  Explicit type can be replaced with <>显式类型可以用<>来替换
  '试试最后'用资源替换'试用'
14. Java language level migration aids Java语言级别的迁移辅助
  'if' replaceable with 'switch'
15. Javadoc issues Javadoc问题
  Dangling Javadoc comment 摇摇晃晃的Javadoc评论
  Declaration has Javadoc problems 宣言有Javadoc问题
  Declaration has problems in Javadoc  声明在Javadoc引用中有问题
16. Naming conventions 命名约定
17. Numeric issues 数字问题
  数字溢出 Numeric overflow
  八进制整数 Octal integer
  无意义的算术表达式 Pointless arithmetic expression
18. Performance issues 性能问题
  Redundant 'String.toString()'  冗余'String.toString()'
  Redundant 'substring(0)' call  冗余'substring(0)'调用
  Redundant call to 'String.format()'  冗余调用'String.format()'
  String concatenation as argument to 'StringBuffer.append()' call  字符串连接  作为“StringBuffer.append()”调用的参数
  String concatenation in loop  循环中的字符串连接
  'StringBuffer' can be replaced with 'String'  'StringBuffer'可以替换为'String'
19. Probable bugs 可能的错误
  Collection added to self   Collection添加到自我
  Constant conditions & exceptions 不变的条件和例外
  Mismatched query and update of collection 不匹配的查询和集合更新
  Mismatched query and update of StringBuilder 不匹配的查询和更新的StringBuilder
  @NotNull/@Nullable problems @NotNull / @可空问题
  Result of method call ignored 方法调用的结果被忽略
  Statement with empty body 声明与空的实现
  String comparison using '==', instead of 'equals()' 使用'=='进行字符串比较,而不是'equals()'
  Suspicious collections method calls 可疑collections方法调用
  Suspicious variable/parameter name combination 可疑变量/参数名称组合
  Unused assignment 没用的赋值操作
20. Properties Files 属性文件
  Unused Property未使用的属性
21. Spelling 拼字
22. Style 样式
  Unnecessary semicolon没必要的分号
23. Verbose or redundant code constructs 详细或冗余的代码结构
  Redundant array creation创建冗余阵列
  Redundant type cast冗余类型转换
24. XML XML
  Deprecated API usage in XML  在XML中不推荐使用API
  Unbound XML namespace prefix  未绑定的XML名称空间前缀
  Unused XML schema declaration  未使用的XML模式声明
  XML highlighting  XML突出显示
  XML tag empty body  XML标签为空的正文

五、在Android Studio中使用

lint提供了命令行接口,所以可以作为单独工具被使用,或者集成到ide的构建流程中去。
在Android Studio中,通过菜单或者在Project视图以及代码视图中的右键菜单中选择Analyze > Inspect Code就可以打开lint检查的对话框Specify Inspection Scope,在这里设置好要进行代码分析的范围,确定后工具就开始对代码进行检查了,完成后会自动打开Inspection工具窗口。

六、自定义lint

原生Lint无法满足特有的需求,例如:编码规范。

  • id : 唯一值,应该能简短描述当前问题。利用Java注解或者XML属性进行屏蔽时,使用的就是这个id。
  • summary : 简短的总结,通常5-6个字符,描述问题而不是修复措施。
  • explanation : 完整的问题解释和修复建议。
  • category : 问题类别。详见下文详述部分。
  • priority : 优先级。1-10的数字,10为最重要/最严重。
  • severity : 严重级别:Fatal, Error, Warning, Informational, Ignore。
  • Implementation : 为Issue和Detector提供映射关系,Detector就是当前Detector。声明扫描检测的范围Scope,Scope用来描述Detector需要分析时需要考虑的文件集,包括:Resource文件或目录、Java文件、Class文件。

自定义Detector可以实现一个或多个Scanner接口,选择实现哪种接口取决于你想要的扫描范围
- Detector.XmlScanner - Detector.JavaScanner - Detector.ClassScanner - Detector.BinaryResourceScanner - Detector.ResourceFolderScanner - Detector.GradleScanner - Detector.OtherFileScanner

这里因为我们是要针对Java代码扫描,所以选择使用JavaScanner

代码中getApplicableNodeTypes方法决定了什么样的类型能够被检测到。这里我们想看Log以及println的方法调用,选取MethodInvocation。对应的,我们在createJavaVisitor创建一个ForwardingAstVisitor通过visitMethodInvocation方法来接收被检测到的Node。
可以看到getApplicableNodeTypes返回值是一个List,也就是说可以同时检测多种类型的节点来帮助精确定位到代码,对应的ForwardingAstVisitor接受返回值进行逻辑判断就可以了。

可以看到JavaScanner中还有其他很多方法,getApplicableMethodNames(指定方法名)、visitMethod(接收检测到的方法),这种对于直接找寻方法名的场景会更方便。当然这种场景我们用最基础的方式也可以完成,只是比较繁琐。

例子:

public class ULogUsageDetector extends Detector implements Detector.JavaScanner {

    private static final Class<? extends Detector> DETECTOR_CLASS = ULogUsageDetector.class;
    private static final EnumSet<Scope> DETECTOR_SCOPE = Scope.JAVA_FILE_SCOPE;
    private static final Implementation IMPLEMENTATION = new Implementation(
            DETECTOR_CLASS,
            DETECTOR_SCOPE
    );

    private static final String ISSUE_ID = "ULogNotUsed";
    private static final String ISSUE_DESCRIPTION = "错误:你应该使用自定义的Log打印工具类{com.xiaohe.www.lib.tools.log.ULog}";
    private static final String ISSUE_EXPLANATION = "为了能够控制Log并统一输出,你不能直接使用{android.util.Log}或者{System.out.println}直接打印日志,你应该使用自定义的Log打印工具类{com.xiaohe.www.lib.tools.log.ULog}";

    private static final Category ISSUE_CATEGORY = Category.CORRECTNESS;
    private static final int ISSUE_PRIORITY = 9;
    private static final Severity ISSUE_SEVERITY = Severity.ERROR;

    public static final Issue ISSUE = Issue.create(
            ISSUE_ID,
            ISSUE_DESCRIPTION,
            ISSUE_EXPLANATION,
            ISSUE_CATEGORY,
            ISSUE_PRIORITY,
            ISSUE_SEVERITY,
            IMPLEMENTATION
    );
    private static final String SYSTEM_OUT_PRINT = "System.out.print";
    private static final String SYSTEM_OUT_PRINTLN = " System.out.println";
    private static final String SYSTEM_ERR_PRINT = "System.err.print";
    private static final String SYSTEM_ERR_PRINTLN = " System.err.println";

    private static final String CHECK_PACKAGE = "android.util.Log";

    /**
     * JavaScanner
     * @return
     */
    @Override
    public List<String> getApplicableMethodNames() {
        return Arrays.asList("v", "d", "i", "w", "e", "wtf");
    }
    /**
     * ClassScanner
     * @return
     */
//    public List getApplicableCallNames() {
//        return Arrays.asList("v", "d", "i", "w", "e", "wtf");
//    }

    @Override
    public List<Class<? extends Node>> getApplicableNodeTypes() {
        return Collections.<Class<? extends Node>>singletonList(MethodInvocation.class);
    }

    @Override
    public AstVisitor createJavaVisitor(final JavaContext context) {
        return new ForwardingAstVisitor() {
            @Override
            public boolean visitMethodInvocation(MethodInvocation node) {
                String nodeString = node.toString();
                if (nodeString.startsWith(SYSTEM_OUT_PRINT)
                        || nodeString.startsWith(SYSTEM_OUT_PRINTLN)
                        || nodeString.startsWith(SYSTEM_ERR_PRINT)
                        || nodeString.startsWith(SYSTEM_ERR_PRINTLN)) {
                    context.report(ISSUE, node, context.getLocation(node),
                            ISSUE_DESCRIPTION+",请使用ULog,不要使用System.out.println等");
                    return true;
                }

                JavaParser.ResolvedNode resolve = context.resolve(node);
                if (resolve instanceof JavaParser.ResolvedMethod) {
                    JavaParser.ResolvedMethod method = (JavaParser.ResolvedMethod) resolve;
                    // 方法所在的类校验
                    JavaParser.ResolvedClass containingClass = method.getContainingClass();
                    if (resolve.getName().equals("v")
                            ||resolve.getName().equals("d")
                            ||resolve.getName().equals("i")
                            ||resolve.getName().equals("w")
                            ||resolve.getName().equals("e")
                            ||resolve.getName().equals("wtf")){
                        if (containingClass.matches(CHECK_PACKAGE)) {
                            context.report(ISSUE, node, context.getLocation(node),
                                    ISSUE_DESCRIPTION+",请使用ULog,避免使用Log");
                            return true;
                        }
                    }
                }
                return super.visitMethodInvocation(node);
            }
        };
    }


}

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

二蛋和他的大花

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值