通关Android Lint

Lintphp

​ Android Lint 是Android Studio 在ADT(Android Developer Tools)16提供的代码扫描工具,能够帮助咱们发现和更正代码结构质量的问题。系统会报告该工具检测到的每一个问题并提供问题的描述消息和严重级别,以便快速肯定须要优先进行的修改。此外,咱们还能够经过下降问题的严重级别以忽略与项目无关的问题,或者提升严重级别以突出特定问题。html

优势

  • 无需实际执行应用
  • 没必要编写测试用例

Lint工做流

下图显示了 lint 工具如何处理应用源文件。java

lint

  • App Source Files : 应用源文件,包含组成Anroid项目的文件,包括Java,Kotlin和XML文件,图标以及Progurad配置文件。
  • lint.xml : 一个配置文件,可用于指定要排除的任何 lint 检查以及自定义问题严重级别。
  • lint Tool :一个静态代码扫描工具,能够从命令行或在 Android Studio 中对 Android 项目运行该工具。
  • lint Output :lint检查结果,能够在控制台或 Android Studio 的 Inspection Results 窗口中查看 lint 检查结果

手动进行Lint检查

  1. 依次选择 Analyze > Inspect Code,手动运行配置的 lint 及其余 IDE 检查。
image-20200610130611752

在左侧窗格树状视图中,经过展开并选择错误类别、类型和问题来查看检查结果。node

右侧窗格显示选定错误类别、类型或问题的检查报告,并提供错误的名称和位置。在适用状况下,检查报告会显示问题概要等其余信息,以帮助您更正问题。android

allowBackup属性肯定是否能够备份应用程序的数据并恢复。编程

在SDK版本23及更高版本中,您的应用程序数据将在应用程序安装时自动备份和还原。考虑添加属性“ android:fullBackupContent”以指定“ @xml”资源,该资源配置要备份的文件。这个问题属于Security。api

  1. 在Gradle命令行环境下,可直接用./gradlew lint执行Lint检查。

想使用Lint命令须要配置Lint环境变量,或者进入%ANDROID_HOME%\tools\bin目录下数组

会在命令行中输出相应信息:安全

> Task :app:lint
Ran lint on variant debug: 2 issues found
Ran lint on variant release: 2 issues found
Wrote HTML report to file:///D:/Develop/Project/AndroidLint/app/build/reports/lint-results.html
Wrote XML report to file:///D:/Develop/Project/AndroidLint/app/build/reports/lint-results.xml
Lint found no errors or warnings (1 error filtered by baseline lint-baseline.xml)
复制代码

在一个项目目录下运行lint检查一系列文件,使用下面的命令:性能优化

lint [flags] <project directory>
复制代码

例如,你能够执行下面的命令,来扫描项目目录下和它的子目录下的文件。

MissingPrefix问题ID告诉lint只扫描缺乏Android命名空间前缀的XML属性

lint --check MissingPrefix myproject
复制代码
  1. 检查项目中是否存在某个问题

image-20200611180916145

image-20200611180945298

该功能容许咱们输入一个Issue的id来检查项目中是否存在该id对应的Issue的问题

会在Inspection Results窗口显示对应的问题信息

Lint会帮助咱们查找并优化哪些问题

  • 遗漏的翻译(好比国际化未被翻译的字段)
  • 布局性能(解决布局无用或太多,嵌套过多问题)
  • 没有引用的资源文件
  • 不一致的数组大小
  • 国际化的硬编码问题
  • 图标重复问题
  • 可用性问题(文本输入类型没有指定)
  • manifest文件内的错误

Lint 发现的每一个问题都有描述信息和等级,咱们能够很方便地定位问题,同时按照严重程度进行解决。固然这个“严重程度”咱们能够手动调节,有些原则问题不容侵犯,必须提高到 error,而有的个别问题也能够无视。

Lint 问题种类

  • Correctness : 正确性,好比硬编码、使用过期 API 等
  • Performanc : 性能 有影响的编码,好比:静态引用,循环引用等
  • Internationalization : 国际化,直接使用汉字,没有使用资源引用等
  • Security : 安全性,好比在 WebView 中容许使用 JavaScriptInterface 等
  • Usability : 易用性,有更好的替换的 好比排版、图标格式建议.png格式 等
  • Accessibility : 无障碍,好比ImageView的contentDescription每每建议在属性中定义 等
1. Correctness 
    1) DuplicatedIds
    Layout中id应该惟一
    2) NewApi
    代码中使用的某些API高于Manifest中的Min SDK
    3) InconsistentArrays
    字符串国际化中,同一名字的的String-Array对应的item值不相同
    4) Registered
    Activity/Service/ContentProvider没有经过AndroidManifest注册
    5) Deprecated
    使用已经废弃的API
    6) PxUsage
    避免使用px,使用dp
    7) AppCompatCustomView
	Appcompat自定义小部件通常会让你继承自 android.support.v7.widget.AppCompat...
	不要直接扩展android.widget类,而应该扩展android.support.v7.widget.AppCompat中的一个委托类。
 
2. Correctness:Messeges
    1) MissingTranslation
    字符串国际化不彻底
    2) ExtraTranslation
    国际化的字符串,在默认位置(defaultlocale),没有定义
    3) StringFormatInvalid
    若是字符串包含'%'字符,则该字符串多是格式化字符串,将从Java代码传递给String.format以用特定值替换每一个'%'事件。(有可能误报错误)
    4) Typos
    通常为Spelling error,该检查将检查字符串定义,若是发现任何看起来像拼写错误的单词,则会对其进行标记。
    虽然很常见,但通常不作处理。
    
3. Correctness:Assertions
	1) Assert (warning)
	尽可能使用其余方式替代`Assert`,例如
	if (BuildConfig.DEBUG && !(speed > 0)) { throw new AssertionError() }
	替代
	assert maxSize > 0;
	2) UnusedAttribute
	此检查将查找在XML文件中设置的属性,若是文件的引入版本比应用程序所针对的最旧版本(具备minSdkVersion属性)要新就会有提示。
	若是外观/功能影响很大/很重要,应考虑其余实现方式,不然能够忽略。
	3) DuplicateIncludedIds
	两个独立的布局使用相同的ID是能够的。可是,若是布局与include标签结合使用,那么ID在任何状况下都必须是惟一的。
    (例如 Layout_A 经过include引入Layout_B,这两个布局不该该有相同ID的标签)
	

4. Security
    1) SetJavaScriptEnabled
    不肯定你的程序中确实须要JavaScript就不要执行SetJavaScriptEnabled。
    2)ExportedContentProvider/ExportedReceiver/ExportedService/ExportedActivity
    ContentProvider/Receiver/Service/Activity的exported为true时,设置一个Permission,让使用者获取了Permission才能使用。
    3) HardcodedDebugMode
    不要在manifest中设置android:debuggable。
    设置它,编译的任何版本都要采用指定的debug模式。不设置,编译Eng版本采用debug模式;编译User版本采用release模式。

5. Performance
    1) DrawAllocation
    避免在绘制或者解析布局(draw/layout)时分配对象。E.g.,Ondraw()中实例化Paint对象。
    2) ObsoleteLayoutParam
    Layout中无用的参数。
    3) UseCompoundDrawables
    可优化的布局:如包含一个Imageview和一个TextView的线性布局,可被采用CompoundDrawable的TextView代替。
    4) UseSparseArrays
    尽可能用Android的SparseArray代替Hashmap
    5) DisableBaselineAlignment
    若是LinearLayout被用于嵌套的layout空间计算,它的android:baselineAligned属性应该设置成false,以加速layout计算。
    6) FloatMath
    使用FloatMath代替Math。
    7) NestedWeights
    避免嵌套weight,那将拖累执行效率
    8) UnusedResources/UnusedIds
    未使用的资源会让应用程序变大并减慢了构建速度
    9) Overdraw
    若是为RootView指定一个背景Drawable,会先用Theme的背景绘制一遍,而后才用指定的背景,这就是所谓的“Overdraw”。
    能够设置theme的background为null来避免。
    10) UselessLeaf/UselessParent
    没有子级或者背景的layout能够删除(由于它是不可见的)
    具备子级且没有兄弟级,不是滚动视图或根级布局,且没有背景的布局能够删除,并将其子级直接移到父级中。


6. Usability:Icons
    1) IconNoDpi
    Icon在nodpi和指定dpi的目录下都出现,应删除一个。
    2) GifUsage
    Image不要用GIF,最好用PNG,能够用JPG。

7. Usability
    1) BackButton
    Android中不要设计有Back的按钮,Android中通常有Back的硬按键。
    2) ButtonCase
    Button的“Ok”/“Cancel”显示大小写必定,不要全大写或全小写。有标准的资源的字符串,不要本身再定义,而要用系统定义的:@android:string/ok和@android:string/cancel

8. Accessibility
    1) ContentDescription
    ImageView和ImageButton应该提供contentDescription

9. Internationalization
    1) HardcodeText
    硬编码的字符串应该在资源里定义
    2) EnforceUTF8
    全部XML资源文件都应该以UTF-8编码

    ...
复制代码
lint --list
Valid issue categories:
    Correctness
    Correctness:Messages
    Correctness:Chrome OS
    Security
    Performance
    Usability:Typography
    Usability:Icons
    Usability
    Accessibility
    Internationalization
    Internationalization:Bidirectional Text

Valid issue id's: "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)
复制代码
查看更详细的信息:
lint --show
或者
访问 http://tools.android.com/tips/lint-checks
复制代码

Lint 问题等级

从高到低:

  • Fatal : 致命的 , 该类型的错误会直接中断 ADT 导出 APK。
  • Error : 错误,明确须要解决的问题,包括Crash、明确的Bug、严重性能问题、不符合代码规范等,必须修复。
  • Warning : 警告,包括代码编写建议、可能存在的Bug、一些性能优化等,适当放松要求。
  • Information
  • Ignore

设置Lint问题检查

​ 默认状况下,运行 lint 扫描时,lint 工具会检查它支持的全部问题。 可是咱们能够限制 lint 要检查的问题,并为这些问题分配严重级别。例如,禁止对与项目无关的特定问题进行 lint 检查,也能够将 lint 配置为以较低的严重级别报告非关键问题。

配置不一样级别的 Lint 检查:

  • 全局(整个项目)
  • 项目模块
  • 生产模块
  • 测试模块
  • 打开的文件
  • 类层次结构
  • 版本控制系统 (VCS) 范围

image-20200610140518620

在 Android Studio 中配置 Lint

在项目根目录建立一个lint.xml文件

image-20200610140705912

lint.xml文件由一个封闭的父标签组成,它包含了一个或者多个子标签。Lint为每个定义了惟一的id属性值,经过设置标识的安全属性,你能够改变一个问题的安全级别,或者这个问题的lint检查,而且能够指定该issue做用于指定的文件仍是当前项目。把lint.xml放在app的目录下(lint.xml须要放在build.gradle同级目录),命令行执行lint时候,lint就会用lint.xml中的规则。另外,执行lint时还能够用参数–config指定一个全局的配置用于全部的项目。当项目中已有lint.xml,则对于某个issue而言,在lint.xml中没有对该issue特别定制的状况下,–config指定的文件中的该issue的定制才起做用。

<?xml version="1.0" encoding="UTF-8"?>
    <lint>
        <!-- 在项目中禁止检测 id为 IconMissingDensityFolder的问题 -->
        <issue id="IconMissingDensityFolder" severity="ignore" />

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

        <!-- 忽略指定文件中的UselessLeaf问题 -->
        <issue id="UselessLeaf">
            <ignore path="res/layout/main.xml" />
        </issue>

        <!-- 将硬编码字符串(HardcodedText)的严重性更改成“错误” -->
        <issue id="HardcodedText" severity="error" />
    </lint>
    
复制代码

image-20200610170959341

配置对Java或Kotlin的检查

要专门对 Android 项目中的某个类或方法停用 lint 检查,能够向该代码添加 @SuppressLint 注释。

@SuppressLint("NewApi")
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
    
复制代码

配置 XML 的 lint 检查

可使用 tools:ignore 属性对 XML 文件的特定部分停用 lint 检查。在 lint.xml 文件中添加如下命名空间值,以便 lint 工具可以识别该属性

namespace xmlns:tools="http://schemas.android.com/tools"
复制代码

对 XML 布局文件的 <ConstraintLayout>元素中的 SmallSp 问题关闭 lint 检查,

若是某个父元素声明了 ignore 属性,则该元素的子元素会继承此属性。在本例中,也会对 <TextView> 子元素停用 lint 检查。

此时不会提示SmallSp错误

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" ------添加命名空间 xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity" tools:ignore="SmallSp">    <!--不对SmallSp进行检查-->  
    
    <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Hello World!" android:textSize="9sp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>
    
复制代码

禁止 lint 检查 XML 元素中的全部问题:

tools:ignore="all"                              使用关键字all
复制代码

禁止检查多个问题:

tools:ignore="NewApi,StringFormatInvalid"       使用逗号分隔
复制代码

在gradle中配置Lint规则

android {
    // 移除lint检查的error,能够避免因为编译条件太过严格而编译不过的问题
    lintOptions {
        // 若是为 true,则当lint发现错误时中止 gradle构建 (默认为true)
        abortOnError false
        // 若是为 true,则只报告错误
        ignoreWarnings true
        // 不检查给定的问题id InvalidPackage: Package not included in Android
        disable 'InvalidPackage'
        // 不检查给定的问题id 资源类型错误
        disable "ResourceType"
        // 忽略因MissingTranslation致使Build Failed错误 "app_name"
        disable 'MissingTranslation'
        // 检查给定的问题 id 
        enable 'RtlHardcoded','RtlCompat', 'RtlEnabled' 
        // * 仅 * 检查给定的问题 id 
        check 'NewApi', 'InlinedApi'
        // 配置写入输出结果的位置;它能够是一个文件或 “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'
        // 若是为 true,则检查全部的问题,包括默认不检查问题
        checkAllWarnings true
        // 重置 lint 配置(使用默认的严重性等设置)。
        lintConfig file("default-lint.xml")
        // 设置为 true,则当有错误时会显示文件的全路径或绝对路径 (默认状况下为true)
        absolutePaths true
    }

}

复制代码

before:

image-20200610152739018

add this:

image-20200610152422725

after:

image-20200610152509547

好比这个SmallSp,通常状况下是一个黄色的警告,在gradle中配置成fatal以后,此处提示为红色错误,而且在gralde构建时,若是存在SmallSp问题会中止构建。

记录Lint报错信息

能够为项目的当前警告集建立快照,而后将该快照用做未来运行检查的基准,以便只报告新问题。 有了基准快照,您即可开始使用 lint 让构建失败,而没必要先返回并解决全部现有问题。

基准 :当前问题集

建立Lint基准快照

修改项目的 build.gradle 文件

android {
      lintOptions {
        baseline file("lint-baseline.xml")
      }
    }
复制代码

首次添加此代码行时,系统会建立 lint-baseline.xml 文件以创建基准。此后,lint 工具仅读取该文件以肯定基准。

若是要建立新基准,请手动删除该文件并再次运行 lint 以从新建立它。

而后,从 IDE(依次选择 Analyze > Inspect Code)或从命令行运行 lint,以下所示。系统会输出 lint-baseline.xml 文件的位置。

image-20200611182744778

运行 lint 会将全部当前问题记录在 lint-baseline.xml 文件中。当前问题集称为“基准”,若是要与其余人共享 lint-baseline.xml 文件,能够将其加入版本控制。

建立基准后,若是向代码库添加任何新警告,lint 将仅列出新引入的错误。

如上图所示,执行上述操做后,会把全部问题都放入 lint-baseline.xml中,在此以后出现的新问题,会单独展现出来,例如上图中的 activity_main saaa not found

查看和修改检查配置文件

Android Studio 附带了许多 lint 及其余检查配置文件,这些配置文件可经过 Android 更新进行更新。您能够原封不动地使用这些配置文件,也能够修改它们的名称、说明、严重级别和范围。您还能够激活和禁用整组的配置文件或一组配置文件中的个别配置文件。

要访问 Inspections 对话框,请执行如下操做:

  1. 依次选择 Analyze > Inspect Code

  2. Specify Scope 对话框的 Inspection Profile 下,点击 More(省略号)

    此时将显示 Inspections 对话框,其中列出了支持的检查及其说明。

    支持的检查及其说明

  3. 选择 Profile 下拉列表,以在 Default (Android Studio) 与 Project Default(活动的项目)检查之间切换。如需了解详情,请参阅如下 IntelliJ 页面:“Specify Inspection Scope”对话框

  4. 在左侧窗格的 Inspections 对话框中,选择一个顶级配置文件类别,或展开一个组并选择特定的配置文件。选择一个配置文件类别后,您能够将该类别中的全部检查项目看成一个检查项目进行修改。

  5. 选择 Manage 下拉列表,以复制检查项目、对检查项目进行重命名、向检查项目添加说明以及导出/导入检查项目。

  6. 操做完成后,点击 OK

Item Description
Whole project 选择此选项能够对整个项目进行分析。
File 选择此选项可分析当前在“项目”工具窗口中选择或在编辑器中打开的文件。
Selected files 选择此选项可分析“项目”工具窗口中当前选定的文件。
Custom scope 选择此选项以使用自定义范围。从列表中选择一个预约义的范围,或单击"(更多)省略号",而后选择分析的范围
Include test sources 选择此复选框以对test sources执行分析。
Inspection profile 选择一个配置文件以检查指定范围。从列表中选择一个配置文件。若是所需的配置文件不在列表中,请单击省略号按钮,而后在页面上配置所需的配置文件。

自定义Lint

Android Lint内置了不少lint规则,用来检测一些常见的代码问题(例如,正确性问题、安全问题、性能问题,等等)。同时,Android Lint也支持自定义lint规则,以便开发者灵活应用,更好地提高项目代码质量 。利用自定义lint规则,既能够用来在项目中检测代码质量问题,也能够用来保证编码规范的执行。

Detector

Detector 是自定义规则的核心,它的做用是扫描代码,从而获取代码中的各类信息,而后基于这些信息进行提醒和报告。

如下是能够实现的扫描器接口,选择实现哪一种接口取决于你想要的扫描范围。

  • Detector.BinaryResourceScanner 针对二进制资源,例如 res/raw 等目录下的各类 Bitmap
  • Detector.JavaScanner / JavaPsiScanner / UastScanner 针对 Java 代码进行扫描
  • Detector.ClassScanner 相对于 Detector.JavaScanner,更针对于类进行扫描,能够获取类的各类信息
  • Detector.GradleScanner 针对 Gradle 进行扫描
  • Detector.ResourceFolderScanner 针对资源目录进行扫描,只会扫描目录自己
  • Detector.XmlScanner 针对 xml 文件进行扫描
  • Detector.OtherFileScanner 用于除上面6种状况外的其余文件

扫描Java源文件的Scanner前后经历了三个版本。

  1. 最开始使用的是JavaScanner,Lint经过Lombok库将Java源码解析成AST(抽象语法树),而后由JavaScanner扫描。

  2. 在Android Studio 2.2和lint-api 25.2.0版本中,Lint工具将Lombok AST替换为PSI,同时弃用JavaScanner,推荐使用JavaPsiScanner。

    PSI是JetBrains在IDEA中解析Java源码生成语法树后提供的API。相比以前的Lombok AST,能够支持Java 1.八、类型解析等。使用JavaPsiScanner实现的自定义Lint规则,能够被加载到Android Studio 2.2+版本中,在编写Android代码时实时执行。

  3. 在Android Studio 3.0和lint-api 25.4.0版本中,Lint工具将PSI替换为UAST(通用抽象语法树),同时推荐使用新的UastScanner。

    UAST是JetBrains在IDEA新版本中用于替换PSI的API。UAST更加语言无关,除了支持Java,还能够支持Kotlin。

PSI介绍

PSI(Program Structure Interface)是IDEA中用于解析代码的一套API,全称是:程序结构接口 。可将文件的内容表示为特定编程语言中的元素的层级结构。

A PSI (Program Structure Interface) file is the root of a structure representing the contents of a file as a hierarchy of elements in a particular programming language.

每种Psi元素对应一个类,均继承自com.intellij.psi.PsiElement。例如PsiMethodCallExpression表示方法调用语句,PsiNewExpression表示对象实例化语句等。

官方文档

IntelliJ Platform SDK DevGuide www.jetbrains.org/intellij/sd…

UAST

UAST全称是通用抽象语法树,UAST节点本质上是Java和Kotlin所支持的超集。

使用UAST编写规则的时候,这个规则会同时适用Java文件和Kotlin文件,无需为同一个对象编写两套规则。

JavaPsiScanner介绍

JavaPsiScanner中包含6组、12个回调方法,以下。

  1. getApplicablePsiTypes返回了须要检查的Psi元素类型列表时,类型匹配的Psi元素(PsiElement)就会被createPsiVisitor返回的JavaElementVisitor检查。
  2. getApplicableMethodNames返回方法名的列表时,名称匹配的方法调用(PsiMethodCallExpression)就会被visitMethod检查。
  3. getApplicableConstructorTypes返回类名的列表时,类名匹配的构造语句(PsiNewExpression)就会被visitConstructor检查。
  4. getApplicableReferenceNames返回引用名的列表时,名称匹配的引用语句(PsiJavaCodeReferenceElement)就会被visitReference检查。
  5. appliesToResourceRefs返回true时,Java代码中的资源引用(例如R.layout.main)就会被visitResourceReference检查。
  6. applicableSuperClasses返回父类名的列表时,父类名匹配的类声明(PsiClass)就会被checkClass检查。

此处用第二种作了示例

关键代码:

MyIssueDetector

public class MyIssueDetector extends Detector implements Detector.UastScanner {

    static final Issue ISSUE_NOT_USE_LOG_UTIL = Issue.create(
            "LOG_UTIL",  				 //id
            "should use log util",        //简介
            "this is explanation",		 //explanation 
            Category.USABILITY,
            10,							//优先级
            Severity.ERROR,
            new Implementation(MyIssueDetector.class, Scope.JAVA_FILE_SCOPE)
    );

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

    /** * @param context lint请求的上下文 * @param node 调用方法的节点 * @param method 被调用的方法 */
    @Override
    public void visitMethodCall(@NotNull JavaContext context, @NotNull UCallExpression node, @NotNull PsiMethod method) {
        super.visitMethodCall(context, node, method);
        if(context.getEvaluator().isMemberInClass(method,"android.util.Log")){
            context.report(ISSUE_NOT_USE_LOG_UTIL, context.getLocation(node), "Do not directly call android.util.Log, you should use the unified tool class");
        }
    }
}
复制代码

当发现有调用v(),d()等方法的时候,咱们都会收到回调visitMethodCall, 由于咱们只是看了方法名,而不知道类,因此须要使用鉴别器(eveluator)进行检查,

确保它是位于android.util.Log中的,若是是的话就上报一个用例。

id : 惟一值,应该能简短描述当前问题。利用Java注解或者XML属性进行屏蔽时,使用的就是这个id。

summary : 简短的总结,一般5-6个字符,描述问题而不是修复措施。

explanation : 完整的问题解释和修复建议。

category : 问题类别。

priority : 优先级。1-10 递增。

severity : 严重级别:Fatal, Error, Warning, Informational, Ignore。

Implementation : 为Issue和Detector提供映射关系,Detector就是当前Detector。声明扫描检测的范围

Scope:用来描述Detector须要分析时须要考虑的文件集,包括:Resource文件或目录、Java文件、Class文件。

getApplicableMethodNames 返回值指定了须要被检查的方法

visitMethodCall 找到与[.getApplicableMethodNames]返回的任何名称匹配的任何方法调用而调用的方法。

context.report的参数:

第一个参数:是咱们定义的Issue; 第二个参数:根据当前节点返回当前的位置信息,便于在报告中显示定位; 第三个参数:字符串用来为警告添加解释。

MyIssueRegistry

建立好的Issue要在IssueRegistry中注册

IssueRegistry 中注册了 Android Lint 自带的 Issue,而自定义的 Issue 则能够经过 getIssues 系列方法传入

public class MyIssueRegistry extends IssueRegistry {

    @NotNull
    @Override
    public List<Issue> getIssues() {
         return Arrays.asList(
                MyIssueDetector.ISSUE_NOT_USE_LOG_UTIL
              // , AttrPrefixDetector.ISSUE_XML_NAME
        );
    }
}
复制代码

build.gradle

添加lint相关依赖,并生成jar包

apply plugin: 'java-library'

sourceCompatibility = "7"
targetCompatibility = "7"

configurations {
    lintChecks
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])

    implementation "com.android.tools.lint:lint-api:27.0.0"
    implementation "com.android.tools.lint:lint-checks:27.0.0"

    lintChecks files(jar)
}

jar {
    manifest {
        attributes('Lint-Registry': 'com.example.lint_lib.MyIssueRegistry')
    }
}
复制代码

其中 lint-api 是 Android Lint 的官方接口,基于这些接口能够获取源代码信息,从而进行分析。

lint-checks 是官方已有的检查规则。

Lint-Registry 表示给自定义规则注册,以及打包为 jar。

使用

1.全局使用自定义的Lint

将生成的jar包放入~android/lint文件夹中(若是没有就本身建一个) 我本身的 C:\Users\zhuoy.android\lint

以后使用命令行工具查看是否添加成功:

lint --show issue_id
复制代码

image-20200612122343568

与此同时,lint --show/list 都可以查看到这条Issue

被测试的代码:

image-20200612153924046

image-20200612151926628

2.单独项目使用自定义Lint

Google 官方的方案是把 jar 文件放到 ~/.android/lint/,若是本地没有 lint 目录能够自行建立,这个使用方式较为简单,但也使得 Android Lint 做用于本地全部的项目,不大灵活。 在主项目中新建一个 Module,将jar引入module,这样各个项目能够以 module 的方式自行引入自定义 Lint,比较灵活,项目之间不会形成干扰。

新建一个Android Library, 在build.gradle中添加如下代码:

configurations {
    lintJarImport
}

dependencies {
    // 调用lintjar的lintJarOutput方法,得到jar包
    lintJarImport project(path: ':lint_lib', configuration: 'lintChecks')
}

// 调用lintJarImport获得jar包,拷贝到指定目录
task copyLintJar(type: Copy) {
    from(configurations.lintJarImport) {
        rename {
            String fileName ->
                'lint.jar'
        }
    }
    into 'build/intermediates/lint/'
}

// 当项目执行到prepareLintJar这一步时执行copyLintJar方法(注意:这个时机须要根据项目具体状况改变)
project.afterEvaluate {
    def compileLintTask = project.tasks.find { it.name == 'prepareLintJar' }
    compileLintTask.dependsOn(copyLintJar)
}
复制代码

这里,建立了一个叫“lintJarImport”的Gradle configuration,其引用了模块 “:lint_lib”的Gradle configuration “lintChecks”。

同时,对内置的Gradle task “compileLint”作了修改,让其依赖于咱们定义的一个task “copyLintJar”。在task “copyLintJar”中,把模块 “:lint_lib”输出的jar包拷贝到了build/intermediates/lint/lint.jar。

而后执行 gradlew build 看结果

image-20200612170127649

其余项目只要引入这个lintlibrary便可使用其中定义的Lint规则。

在代码中也能够看到高亮提示。

智能纠错

有时IDE给出提示的同时,会有一个ALT+ENTER的快捷键来让咱们直接使用它的建议修改代码,方便快捷,咱们也能够如此:

下图是我修改了一下检查的内容:发现使用Log.wtf()的时候,给出提示,而且给一个建议使用Log.e来代替Log.wtf

wtf不是what the fuck,而是 What a terrible failure

image-20200612181637845

实现方式:

private void reportUsage(@NotNull JavaContext context, @NotNull UCallExpression node, @NotNull PsiMethod method) {
        LintFix lintFix = LintFix.create()
                .name("Use Log.(e)")
                .replace()
                .text(method.getName())
                .with("e")
                .robot(true)
                .independent(true)
                .build();
        context.report(ISSUE_NOT_USE_LOG_UTIL,
                context.getLocation(node),
                " reportUsage Do not directly call android.util.Log, you should use the unified tool class",
                lintFix);
    }
复制代码

相较于以前的上报信息,咱们在调用report方法时,后面加了一个lintFix的参数。

LintFix是Lint的一个快速修复的方式。

independent :此修补程序是否独立于要应用的其余修补程序。

robot:当在 fix-mode下运行lint时,能够自动应用这些类型的修复程序,在该模式下,它将应用全部建议的(合格)修复程序。

相关文章
相关标签/搜索