The address of the project: https://github.com/hust201010701/CheckResourceConflict
One, the introduction
In the daily development of Android, we will use a large number of third-party libraries or self-written component libraries. The resources in these dependent libraries plus the resources of the main project itself may have name conflicts, and the phenomenon of mutual resource coverage may occur.
Since resource overrides don’t have any hints and are only exposed when the APP runs into the relevant code, it’s easy to bring the problem online with serious consequences if you don’t test it carefully. In my business development process, there were two practical problems caused by resource coverage:
- The color resource is overwritten
We have two apps called A and B, and they both depend on A dialog component called C. C has A color resource named @color/main_color. B also has A color declared in the main project, but the value of the resource is different from that in component C. Finally, the display effect of the dialog box in B project is abnormal.
- The layout resource is overwritten
If you think that overwriting color resources only affects the display and is not a serious problem, then look at overwriting layout resources, which can cause crashes. We have an existing business, related programs are in the main project, due to some requirements, we need to migrate the business-related code to an independent component library, in the process of migration, some custom View pages followed the migration. However, due to careless reasons, the original resources of the main project were not deleted. When we use the layout resource, the layout file in the main project overlays the layout file in the component. When we use the findViewById to bind the ID in the Java file, the package name of the View in the layout is the old package name, and the package name of the View in the Java file is the new package name. At run time, a crash with a cast failure occurs. For example, A custom control in main engineering package. Dr. Iew. CircleWithBorderView, migrated to A component the package name change, into A package. The b.v iew. CircleWithBorderView, At some layout component A activity_main. XML used in the control, and the corresponding Java file, we are using are package. The b.v iew. CircleWithBorderView package under the custom controls, without any problems. But when meet in the engineering, as a result of the main engineering activity_main. XML was not removed, and the main engineering activity_main. XML using package. Dr. Iew. CircleWithBorderView custom controls, This can cause a crash at run time.
<package.a.view.CircleWithBorderView
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
Copy the code
Knowing that resources with the same name are overwritten can cause problems that are difficult to find in a timely manner, we need to think about how to avoid such problems.
Second, solutions
What if we could find all the conflicting resources at compile time, tell the developer which resources have the same name but different content, and let the developer dispose of these resources before the final release. There are two ways to deal with this:
- If conflicting resources need to be consistent, you can delete one of the resources or keep the content of the resources consistent.
- If the conflicting resources themselves should not be consistent, one of the resources can be prefixed to prevent the resource from being overwritten.
The resource type
Resources can be divided into two types by type. The rule of distinction is that the entire file of a file type occupies one R.ID, while each element of a value type resource occupies one R.ID:
- Value types such as @color/black, @dimen/screen_width correspond to each child element declared in the XML file
- File type: for example, drawable- xxHDpi image resources folder, layout folder layout file
How do I distinguish resource types
How does the program tell whether a file is a value type or a file type?
We can determine whether the directory name of the file is values or starts with values-.
How do I determine resource conflicts
To determine if a resource has a conflict, we need to deal with two things: one is to determine the unique ID of the resource, the other is to determine whether the value of the resource has a conflict.
Resource determines a unique ID
For file resources, for example, res/drawable-xxhdpi/a.png and res/drawable-xxxhdpi/a.png do not conflict even if the files are different. However, res/drawable-xxhdpi/a.png in component A conflicts with res/drawable-xxhdpi/a.png in component B. Therefore, for file resources, the unique ID can be determined as file_@ folder name/file name. If the file @ layout/activity_main. XML.
For resources of value type, for example, res/values/colors. XML will conflict with the main_color declared in res/values/values.xml. Res /values/colors. XML does not conflict with the main_color in res/values-v19/colors. XML. The unique ID can be determined as the name of the upper layer folder/resource name of the file where the alue@ resource resides, such as value@values/main_color.
Whether resource values conflict
For file type resources, we can calculate whether the MD5 of the file pair is equal to determine whether there is a conflict; For a value type resource, we can directly compare whether the contents of the values are the same.
Third, solutions
Our plan is pretty clear. All you need is access to all the resource files at compile time.
Android Gradle Plugin version 3.3 and above provides an API to compile all resource files.
variants.forEach { variant ->
variant as BaseVariantImpl
// Files correspond to all compilation resources
def files = variant.allRawAndroidResources.files
Copy the code
I used this API to develop a plugin that automatically finds all conflicting resources during compilation. The project address is CheckResourceConflict github.com/hust2010107…
CheckResourceConflict plug-in
4.1 Feature Description
- Supports all resources in the RES folder: file type + value type
- Resource conflict output tree in HTML format
- Support to specify HTML file output directory
- Support resource whitelist, even conflicts are not recorded
- After the detection is complete, email notification is supported
4.2 Usage
1. Add a dependency
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com. Orzangleli: checkresourceconflict: 0.0.1'}}Copy the code
2. Use plug-ins
Add the following to your project’s build.gradle file:
apply plugin: 'CheckResourcePrefixPlugin'
3. Run the task
There are two ways to run the resource conflict detection task
- After the plug-in is added, the
check
Several tasks can be produced in the directory, and related tasks can be performed as needed.
- These tasks are also automatically performed during the apK compilation process
4. Configurable items
CheckResourceConfig {// Whether the plug-in is enabledfalse// autoPreviewResult automatically opens the HTML result with the default browser after runningtrue// outputDir"./out"// Resource conflict whiteListFile".. /checkResource/whitelist.lxc"// emailConfig {// Whether to enable needSendEmailtrue// Email Specifies the email host host""// The sender email is fromEmail""ToEmailList (toEmailList)""."") // Email account account""// email authorizationCode of the third-party client authorizationCode""}}Copy the code
5. Whitelist rule
The beginning of # "#" indicates that the behavior is commented
This file declares whitelisted resources that detect resource conflicts
# File resources are in the format of file_@ folder name/file name, such as file@layout/activity_main.xml
# The format of a value type resource is the name of the upper layer folder of the file where the value@ resource resides/resource name, such as value@values/colorPrimary
value@values/error_color_material_dark
file@layout/notification_action_tombstone.xml
Copy the code
value@values/ error_COLOR_material_dark and file@layout/ notification_action_tombston.xml indicate resource IDS. The ID of the resource can be found in the output HTML document.
6. Precautions
- Resource conflict detection in Assets is not supported
- Android Gradle Plugin versions below 3.3 are not supported
7. Effects
Tips:
- Conflicts that have been handled can be marked with checkboxes.
- The HTML preview function cannot be displayed in the mailbox. Download and preview the local file