Optimizing component switching
Componentization, here is not wordy, too many similar articles Here’s how to automate the core gradle script plug-in https://github.com/genius158/ReferenceDump/blob/main/moduleconfig.gradle major premise, If (isdebug.toboolean ()) {apply plugin: if (isdebug.toboolean ()) {apply plugin: ‘com.android.application’} else {apply plugin: ‘com.android.library’} if(isDebug.toBoolean()) { manifest.srcFile ‘src/debug/AndroidManifest.xml’ } else { manifest.srcFile ‘SRC/release/AndroidManifest. XML’} this: the if (isNeedCahtModule. ToBoolean ()) {implementation project (‘ chat ‘)}
– Switch library to Application
The apply plugin, after which the corresponding configuration is added to project.plugins, If we want to remove project.plugins.withtype (LibraryPlugin){project.plugins.remove(it) } will be submitted to the abnormal Java. Lang. UnsupportedOperationException (no error message), that is to say we can’t do this, this part of the present methods didn’t think too good, finally in the form of the script to replace
if (hookInclude) { project.ant.replace( file: build, token: "apply plugin: 'com.android.library'", value: InsertAppConfig (project)} else {project.ant. Replace (file: build, token: "apply plugin: 'com.android.application'", value: "apply plugin: 'com.android.library'" ) }Copy the code
By the way, ant commands (zip, unzip, copy, replace ect.) are super useful. Replace can even change the code by adding dependsOn to the javac task to obtain the input of the task. Before converting Java to class, we can take the class we want to change and make matching changes to it. (Tip: If you change a file that is not in the intermediate stage, is it dangerous without code versioning, or do you need to do some backup and restore?)
Look at note 1
/ / injection applicationId p.a ndroid. DefaultConfig. ApplicationId "com. Yan. Application" / / The following main purpose under the component model to increase the main level module directory into the manifest File def srcDir = new File (p.p rojectDir. CanonicalPath + "/ SRC") def moduleDir = new File(srcDir.canonicalPath + "/module") def moduleManifest = new File(p.projectDir.canonicalPath + "/src/module/AndroidManifest.xml") if (! moduleDir.exists()) moduleDir.mkdirs() def manifest = new File(srcDir.canonicalPath + "/main/AndroidManifest.xml") if (! moduleManifest.exists()) { p.ant.copy(file: "$manifest.canonicalPath", tofile: "$moduleManifest.canonicalPath") } def moduleManifestTxt = moduleManifest.getText() if (! moduleManifestTxt.contains("<application")) { moduleManifestTxt = moduleManifestTxt.replaceAll("\\s/\\s", "") moduleManifestTxt = moduleManifestTxt.replace("</manifest>",<application>..." ) moduleManifestTxt (moduleManifestTxt)} // Change the MANIFEST to a component p.android.sourceSets.main.manifest.srcFile("src/module/AndroidManifest.xml")Copy the code
AndroidManifest refers to the AndroidManifest of our module and is automatically tagged with the application tag. In the same way, our componentized test code can be thrown into the Module directory with the corresponding script, and Java configuration points can be added, mainly so that when we switch to lib mode, there will not be any code files added.
– Settings. gradle adjusts on demand
apply from: "./moduleconfig.gradle" if (! hookInclude || foreIncludeAll) { include ':test' ... }Copy the code
Focus on the Moduleconfig. Gradle customization section
Ext {// foreIncludeAll = false // Which modules need to be automatically lib to app autoModule = [":test", IncludeModule (":test", [":plugin2", ":router", ":moduleadapter"])Copy the code
// Import our component, Other modules that correspond to this component depend on def includeModule(module, Ext. hookInclude = true moduleListConfig[module] = dependencies include module dependencies.each { m -> include m } }Copy the code
If we use as to automatically generate a new module, then its automatic include in the setting script does not meet our requirements, so we add a judgment, does not meet the prompt adjustment script, of course, using Ant’s replace method we can even automatically adjust the dependency.
def checkSettingChange() { gradle.projectsLoaded { def rootPath = rootProject.projectDir.canonicalPath def setting = file(rootPath + "/settings.gradle") def settingText = setting.getText() def unModulePart = settingText.replaceAll("if.*hookInclude.*\\).*\\{[^}]+}", "") if (unmodulePart. contains("include")) {throw new RuntimeException(" Please make sure in settings.gra... )}}}Copy the code
How do we rely on our components
Here I added a configuration modelDependencies that corresponds to the original Dependencies
ModelDependencies {implementation Project (":test")}Copy the code
The following sections correspond to the script
def modelDependencies = project.getExtensions().create("modelDependencies", ModelDependencies.class) project.afterEvaluate { modelDependencies.implementations.each { module -> // moduleList ModuleListConfig for includeModule (); moduleListConfig for includeModule (); If (modulelist. find {entity -> entity.key.contains(module.name)} == null) {project.dependencies { implementation module } } } }Copy the code
Component memory optimization
During the development process, memory may have a memory problem. An object itself is very large, or a reference is very large. It may need to analyze whether there is a memory allocation problem and whether it can be optimized. Some objects are so numerous that you may need to examine whether there is a way to create a large number of temporary objects in a short period of time. And then there’s the old story of memory leaks.
How can these problems be found conveniently and in time? How do we usually check for memory problems? Dump Java heap with Profiler? Yes, in most cases, we use the built-in function of AS to check problems. You can find objects with a lot of memory, or memory leaks, and then do some optimization, but what are the problems during component development?
- DumpHprof gives us so many objects that I just want to know the memory snapshot of the class file under the current component, or a package name.
- Or, more intuitively, I don’t just want to know which object this object is held by in that other class, I also want to know which method the object is generated by.
- For 1, of course, we can get a memory snapshot after filtering but too slow, use the profiler, for large projects in addition to slow, and even trigger a few times more, soon as card of deformed, often also in case of insufficient memory for a short life cycle method of instantaneous created a pile of objects is difficult to judge, In this case, if only a few hundred milliseconds can view a memory snapshot; Using leakCanary and opening a new thread or process to parse does solve the problem of the profiler’s performance. However, after dumpHprof, We can’t shorten the time of the analysis process. To see the results of the analysis, we often have to wait 10 seconds and 20 seconds, and then we filter the statistics, the whole process will be longer.
So what do I want to do? During our development process, we can check the object creation and destruction under the current module in real time, without waiting, or waiting for the least time, we can compare the memory data from time to time, and the cost of finding problems is very low. When we are bored, we can click on the memory comparison.
How to do
To do this, you simply use a peg (asm instruction level operation) to link the object to a weak reference when executing the NEW operation command, along with the method executed by the executing class. Of course object and reflection generated, but the development process, we won’t deliberately to the vast majority of cases using reflection, a lot of time to avoid using reflection, so as to avoid reflection of some performance problems, to record the reflection generated object can also, of course, is to insert the code in the constructor of the object to record, of course the system class we are powerless. Take a look at the corresponding log
/ / object RefCount total number -- -- -- -- > 23 | | < -- - / / StringBuilder is 14 CLASS - Java. Lang. StringBuilder count: $onCreate$1#timer ----> size(byte): 4624 count: 11 com.yan.referencecounttest.App$onCreate$1#timer(LBurialTimer; LString; LString; LString; J)V // 1064 bytes, generated by App$onCreate$1#<clinit>()V ----> size(byte): 1064 count: 3 com. Yan. Referencecounttest. App $onCreate $1 # < clinit > V / / accounting for a total of 5688 bytes () -- -- -- -- > the size (byte) : 5688 *Copy the code
How to found the problem, this piece of log above, each time for memory logs App onCreateonCreateonCreate1 # timer under the path of the StringBuilder has been on the rise, belongs to the production of large Numbers of temporary objects, we can use a member variable to optimize
BurialTimer.getTimer().setListener { ignore, className, methodName, des, cost ->
if (cost>0){
Log.e("BurialTimer","$cost $className $methodName ")
}
}
Copy the code
To optimize the
private val stringBuilder = StringBuilder()
override fun onCreate() {
BurialTimer.getTimer().setListener { ignore, className, methodName, des, cost ->
if (cost > 0) {
stringBuilder.clear()
stringBuilder.append(cost).append(" ").append(className).append(" ").append(methodName)
Log.e("BurialTimer", stringBuilder.toString())
stringBuilder.clear()
}
}
}
Copy the code
Git address https://github.com/genius158/ReferenceDump