This article will be updated from time to time, and the project under Watch is recommended. Please call star if you like it, submit issue if you think there are flaws, and submit pull Request if you have better ideas. The purpose of this article is to share the author’s practical solutions for speeding up the packaging of debug and release packages.
This article fixed link: github.com/tianzhijiex…
demand
Make packing faster, faster!
implementation
Remove unnecessary modules
AS has a completely different code structure from Eclipse, which provides developers with a single-project, multi-module format. But creating one more module requires maintaining one more module. Therefore, if it is not advisable to create a module just for the convenience of writing code, I strongly recommend that you do a good job of sorting out the project structure before you consider whether you need to create a module. The following is a multi-module APP structure diagram:
The preview tool in AS can also help us comb through modules:
There are many modules in this project, so gradle will check the dependency chain of modules during compilation. Gradle will help us to sort out the relationship between modules and avoid problems caused by references between modules. All this combing and merging of modules will lead to build time. If your build is very slow, I strongly recommend that you sort out the relationships between modules and merge some modules. Speed up builds by packaging stable underlying modules as AArs and uploading them to your company’s Maven repository.
Delete unnecessary files from module
As creates the test directory with module by default:
If you have no intention of writing tests at all, you can always delete the test directory. Of course, if your Module is pure code and does not use resource files at all, please remove the res directory as well.
Delete unnecessary resource files from the main project
There will always be some useless code and resources left in the development of the project, and the more resources there are, the longer it takes to package and merge the resources. However, deleting useless code has little effect on improving the packaging speed. We can use obfuscation to remove useless code at one time when making release package. For resource files, AS provides automatic detection and deletion of invalid files, which is definitely worth checking out.
In the dialog box that pops up, I strongly recommend not checking delete useless ids, because databinding uses some ids that are not represented in the code, so AS will consider them useless. If you delete these ids, expect the compilation to fail. Don’t ask me how I know T_T. By the way, commit every time you do this so you can diff.
Use no-op to speed up debugging
If your project has a lot of company-owned Module dependencies, you can use a technique like this one to do no-op for private modules (see this example for examples). Generally, private modules will be relatively stable and have few exposed methods, or even developed by other project teams. Therefore, if the module itself is used for monitoring and statistics, which does not affect the function, it is suggested to discuss with the developers to provide the no-OP version. Examples from the article:
debugCompile(project(':share-lib-no-op')) {}
releaseCompile(project(':share-lib')) {}
debugCompile(project(':zxing-no-op')) {}
releaseCompile(project(':zxing')) {}Copy the code
The advantage of using the no-op version is that you can only use the interface and not use the implementation, so that all the implemented code is removed. If you do it well, you can even debug without multidex. However, the downside is the need for collaborative communication. If the module’s external interface changes, the impact on the no-op version should be considered.
Reduce the number of methods and do not use multidex
About what multidex is, and how to use it, please refer to this article: blog.csdn.net/t12x3456/ar…
It was a last resort, and I often found some weird bugs on particular models, but just a lot of bugs. In terms of build time, Multidex has a serious impact on build speed because of the process of subcontracting and compression. After analyzing our project through the dexcount plug-in, I found that some libraries in the project were no longer used or had better substitutes, so I streamlined the third-party libraries and opened the confusion of support package, finally making the number of methods in the release package of our project reach a reasonable level.
In order to control variables, I made an empty project to compare the support package before and after the confusion. Let’s look at the data:
When a third-party SDK says don’t mess with the support package, don’t mess with my SDK code, I strongly suggest you think about the number of methods. One effect of obfuscation is to optimize code and shorten method and field names; Another function is to delete variables and methods that are not used. Third party SDKS have a large number of methods, which, if not confused, can lead to a large number of methods, which needs to be very careful. Obfuscation is a very useful tool, but it is also a source of many errors, so I suggest you use it with caution and caution!
Optimize third-party libraries
As mentioned above, optimizing third-party libraries will reduce the number of methods. Here are some general optimization strategies:
DebugCompile is a library that depends on debug. DebugCompile is a library that depends on debug. DebugCompile is a library that depends on debug.
2. Using smaller libraries to replace existing libraries depends on the experience and knowledge of the developers. Although it is nonsense, if you can really do it, the results are very obvious.
Use exclude to exclude some unnecessary dependencies. Take Rn for example. Rn is a large library that will depend on many other libraries once it is introduced:
In our project, I used a network request module I wrote to make network requests, so I wanted to exclude okHTTP introduced by RN. I also found that it introduced a support package, and I’m sure I have a support package in my project, so I wanted to exclude it too. Gradle will only contain the latest version of the library, I’m just giving you an example.
compile ('com.facebook.react:react-native:+'){
exclude group: 'com.squareup.okhttp3', module: 'okhttp'
exclude group: 'com.android.support', module: 'support-v4'
exclude group: 'com.android.support', module: 'support-v7'
}Copy the code
After a rebuild, you will find that okhttp has been removed:
Local modules can also be handled like this:
compile(project(':react-native-custom-module')) {
exclude group: 'com.facebook.react', module: 'react-native'
}Copy the code
Use the company’s warehouse as a cache
I recommend that all of the project’s dependencies (private or third party) be retrieved through the company’s repository, which will find the jCenter repository itself, download the required dependencies, and cache them. The advantage of this is that when one colleague introduces a new library or updates the library version, other colleagues can directly access the cache during build, greatly reducing the time to download dependencies. This is a minor optimization, but it’s important for newcomers and team work.
Debug skips some tasks
Our project uses many Gradle plugins, some of which run their own tasks at build time:
Tiny is used to compress images, buildTime is used to detect build times, and dexcount is used to analyze method counts. These plugins were a huge help to our development, but also increased build time.
Let me share how I did it:
- Enable Tiny before each release, build it directly once, and close it after compressing the image.
- Enable BuildTime when detecting and diagnosing buildtime, not for general debug.
- Open dexcount in the Release package and combine it with Jenkins. This will not affect the debug package, and the number of methods can be continuously monitored.
For information on how Dexcount was combined with Jenkins and how the chart below was produced, please refer to: www.th7.cn/Program/And…
Abandon lambda expressions and use AspectJ sparingly
Lambda is not currently supported on Android, so many people have introduced Retrolambda. Once you introduce the library, you must face the problem of slow builds caused by bytecode conversion. The more you use it, the simpler the code looks, but the longer it takes to build. So, I wouldn’t recommend using it at this stage, but wait and see what Google Jack does.
AspectJ is a great tool for AOP, but because you need to insert code at build time, build time can increase significantly with AspectJ, depending on how much you use it. The pros and cons of AspectJ are obvious, and I’m just going to mention them here. It’s up to you to weigh them. In my case, the introduction of AspectJ because of UiBlock slowed my debug build by three seconds, but the benefits of UiBlock were obvious, so I used it anyway.
Set minSdkVersion to 21 in dev package
Because we do not enable obfuscation when debugging, the debug package needs to use Mulitdex
Android5.0 has made optimization for mulitdex. For details, please refer to the official article. I will directly say how to do it. Add a flavors option to the Gradle configuration, for example, dev, and set the minimum supported Android version to 21.
Then select devDebug during build so that you debug with API 21 minimum.
Special note: You now write the lowest version as 21 to speed things up, assuming you might end up supporting 16. This is a risk because as will assume that your application supports 21 when you write the code, so there will be no risk warning for some 16 to 21 apis. Therefore, using apis between 16 and 21 requires human attention, which is the biggest risk point!!
Open the offline
This is the simplest direct acceleration scheme, the effect is extremely obvious, who uses who knows!
Optimize the gradle
Gradle optimizations are already available on the web.
My own configuration is as follows:
org.gradle.daemon=true
org.gradle.parallel=true
# ndk
android.useDeprecatedNdk=true
org.gradle.configureondemand=truex
org.gradle.jvmargs=-Xmx3072m -XX:MaxPermSize=1024m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8Copy the code
In general, except for the memory increase, which feels somewhat useful, the rest of the configuration is not painful. Finally, I added a 4G memory module directly, which solved most of the problems at one time.
Optimized crashlytics Upload
All of the above mentioned acceleration in the build process, but packaging includes not only build, but also obfuscation, signing, etc. If your project uses Crashlytics, Crashlytics will automatically upload the map file to the server when it is confused. This will help you to analyze the crash and see the code and line count before the confusion, which is very convenient. Everything has pros and cons. The map file of our project is about 6m, and the crashlytics server is abroad, so it will take a long time every time. The optimization point is to improve uplink bandwidth and network speed. The former requires hardware support, while the latter can be optimized through VPN. This problem can be partially solved by configuring the Release package packaging command without deleting the build directory every time.
Use the MultiChannelPackageTool for multi-channel packaging
Our apps may be distributed to multiple channels, and we want to do data analysis for multiple channels, which creates the current situation of Multiple channel packages for Android. This article is a detailed analysis of the most efficient packaging solutions in the country. It is short and concise and worth reading. I chose multichannel Package Pool for packaging, which is the fastest and very simple to use. His principle is to add the channel number to the comment of the ZIP file so that the channel number can be written without breaking the ZIP signature. Since APK is itself a ZIP file, this rule is reliable and fully applicable.
The specific principle and implementation scheme is not difficult, here you can refer to the article written by Zhao Lin for in-depth understanding.
Let me show you the actual situation:
Now we can go through
MCPTool.getChannelId(context, "password", "")Copy the code
Get the channel name. If you are using ummed for monitoring and statistics, you will definitely need to set the ummed key and channel name in your code. Through umeng’s documentation and forums I found that the latest SDK of Umeng provides such a mechanism, so I have the following code:
UMAnalyticsConfig config = new UMAnalyticsConfig(context, appKey, channelId); MobclickAgent.startWithConfigure(config); / / get the key and channel number String appKey. = AnalyticsConfig getAppkey (activity); String channel = AnalyticsConfig.getChannel(activity);Copy the code
Incremental compilation
As currently supports incremental compilation, but the results are really poor and often increase build time, so I would recommend the incremental compilation tool for Jrebel which is constantly updated. I recommended it when I wrote UI Preview Practices in Android, but it was just too expensive back then. Now as out of the incremental compilation, it also can not sit, immediately cut the price, the price is acceptable. In terms of performance, I can say that incremental compilation on Android is by far the best, even better if you’re writing smaller apps. Now it doesn’t require us to configure maven’s repository separately, it’s completely decouples from the project, and it supports annotations and AOP. So if you have the heart to speed up your packing, I highly recommend you try it out for 21 days to see if it’s worth paying for.
Upgrade the JDK and Gradle
The latest Gradle is faster than the old version, jdK1.8 is faster than JDK1.6, so you can update the new version with the official.