Thoughts on Multiple dex files define compilation errors in Android
Yesterday, my long brother asked me a question. He said that if a project contains com. x. A enumeration. The imported third-party jar also has A com. x. A enumeration, which enumeration will be used when I use A enumeration in the project? I thought, isn't this a class (enumeration is a special class) Definition conflict? It should be reported during compilation, and I have encountered this problem before, so I confidently told him that this should be reported during compilation, as a result, he came up with the following sentence: No? The operation is successful, and the enumeration A in the project is imported. I will wipe it. I don't think this is my face? I remember very clearly that an error would be reported, so I wrote a AndroidDemo project myself:
The project is very simple, so run it:
Mom, an error was reported, and compilation failed, so I went to him very confidently and said that I could do it. Then he also said that there was no problem, the problem is deadlocked, but I cannot forget it. I doubt it is an environment problem? Engineering Problems? Compiler problems? After a bit of doubt, let's try. First, we guess whether the call methods are the same. He uses maven, and I use build paths. This is a little different, so I told him to change it to me. At the same time, he used NodeCode for annotation. I used NodeCode a = NodeCode. method A, and then asked my brother to change the usage to the same as mine. After compilation, he still did not report an error? Is it an environmental problem? He used idea, I used Eclipse, and then I tried it with the idea tool. It also reported an error, which is not a problem with the editor. After a while, I took a closer look at the error, I found that it was dex. I suddenly thought that my brother was developing Web, I was developing Android, his project was Web engineering, and I was an Android project. Is that a problem? So I immediately created a new Java project to test it (in fact, both the Java project and the Java Web project are the same, because they both use JVM virtual machines and are compiled using javac ):
Run:
Nima, it's all right, it's a project problem, And we print the information in the NodeCode in the project.
When printed information is found, we can know that the enumeration class in the project is imported first by default.
The problem is clarified. The project in Android is not allowed. The Java project is allowed.
But why? So we can only look at the problem through the source code, but before looking at the source code, we can actually guess the root cause of the problem?
In Android, The dex file is used. We know that the dex file is synthesized by using the dx command. Therefore, the dex file is a file and has a specific format, about dex format description, do not understand the students can view this article: http://www.wjdiankong.cn: 8888/blog /? P = 508, and we know that a jar or running Java Project is a class file in the bin directory after compilation. We guess an error is reported during compilation, so it must be the one executed by dx, so I will go to the source code of the dx command, the source code location:Source code directory \ dalvik \ dx \ src \ com \ android \ dx \ command
The main method is as follows:
Let's take a look at this class:Com. android. dx. command. dexer. Main
Main:
Parse the command line parameters and execute the run method:
We can see that there is a very important method here, that is, to merge and reference the dex content of a third-party jar, which is exactly the result we want to know. Let's take a look:
Method annotation:
/**
* Merges the dex files in library jars. If multiple dex files define
* Same type, this fails with an exception.
*/
At this point, we can see the annotation of this method, and we will know that if the same types is defined, an exception will be thrown. We are going to see what type, is the exception thrown out the same as what we see?
There is an important class: DexMerger is located:Com. android. dx. merge. DexMerger
The constructor of this class will pass two DexBuffer entries. The first DexBuffer is the dex content that we have previously operated on, and the second DexBuffer is the dex content of the Library we need to merge, after constructing the class, call the merge method:
The merge method calls the mergeDexBuffers method:
I can see whether it is a bit familiar here. merge has many methods, and these merge actions are the types we mentioned in the dex file format, but here we are class definition conflicts, so it must be the mergeClassDefs method:
In this method, an ordered type is obtained first, and then the offset value and size of the classDefs region are set. Let's look at the getSortedTypes method:
Here we actually sort and merge the typeIds in dexA and dexB. Let's look at the readSortableTypes method:
Okay, I finally see the core content. Here I will judge whether the type already exists. If so, I will throw an exception message, and the exception information is the same as what we saw earlier:
Here we can know that the dex file does not allow the same class (package name + class name), and the package name + class name here is type. This is explained in the format of the dex file previously parsed. I will not explain it here.
In fact, we can think about it. dex is a file with its own file structure. The same content cannot exist and conflicts may occur.
So now we know why the Android project is not working, because dx encountered an error during class-to-dex conversion, that is, during the compilation period.
But it is possible in the Java project. In fact, it is reasonable to think about it, because if we convert a Java project into a Jar file, the jar file is actually a compressed package, there will be no identical files in the compressed package (the package name will be converted to the specified file directory), and there will be no conflicts or problems if there is one, however, we can see from the above example that the class in this project will be imported first, so you can check the source code of the jar tool:
Http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/sun/tools/jar/Main.java
Here, the main method actually uses ZipFile, ZipEntry, and ZipInputStream internally to component a jar. The operation is also very simple. First, we get all the class files, then, convert the package name to the file directory. However, we did not find the place where the same third-party jar contains the same class and will not overwrite the code. Therefore, this step depends on everyone, but we can see from the above example, the class in the project is used by default.
Here, let's say that when exporting a jar, if we want to include a third-party jar, we know two methods:
1. Use the Eclipse plug-in: fat_jar
2. Run an ant script
Another method on the Internet is to use the Export provided by Eclipse, but you need to write a MANIFEST by yourself. MF file, but I have never succeeded in this method. I don't know if it works. However, the jar with third-party jar exported should be in this style:
In fact, we can see the principles of the above two methods:
First, convert the class file in this project into jar, and then operate the third-party jar content under libs. Then, the following operations are equivalent to merging multiple jar files. In fact, this is very simple, we can write a program by ourselves, using ZipFile + ZipEntry. If we find that there is a corresponding ZipEntry, we can skip it, and finally we can merge multiple jar files. Of course, this is just our conjecture, but from the above example, we can see that the Java project can import multiple jar files containing the same class without conflict, but the Android project does not work, the reason is that dx will judge when converting multiple classes into dex.
Questions to consider:
When this problem occurs, we should first think of a problem with the compiler. Therefore, this has nothing to do with DVM in Android and JVM in Java, because virtual machines are used at runtime. Therefore, we should look at the problem from the Android compilation process, which is a solution to the problem.
Knowledge points:The same classes are not allowed in the Android project, and the Java project can.