I am moving all my source codes to AS as suggested by Android official website. However, the experience is not very good. It is very sluggish as described here. But this is not my ultimate problem for now.
I have resolved many problems such as updating the compileSdkVersion to 23 so that 99 errors of this kind:
Error:(13) Error retrieving parent for item: No resource found that matches the given name 'android:TextAppearance.Material.Inverse'.
could be rectified. But the problems keep on shooting up as I go. Now I have this 64k Dex issue.
Error:The number of method references in a .dex file cannot exceed 64K.
Learn how to resolve this issue at https://developer.android.com/tools/building/multidex.html
I never had this Dex issue while using Eclipse. The source code I have in AS is exactly the same as when it was in Eclipse. The only differences are those gradle changes needed only to work on AS. Any idea why this sudden Dex issue? if I set multiDexEnabled to true, what are the implications?
First of all, make sure you rebuild project, after importing (Build - Clean, Builde - Rebuild Probject). Fixing this issue with limitation methods reference:
android {
defaultConfig {
...
// Enabling multidex support.
multiDexEnabled true
}
...
}
dependencies {
compile 'com.android.support:multidex:1.0.0'
}
And also update you Application.class in java to support MultiDex. See full information here!
UPDATE:
This options ignore on Eclipse, because methods references limit can be calculated from environment (like AS in our case). Why this options doesn't include in Gradle build - still question...
Android application (APK) files contain executable bytecode files in the form of Dalvik Executable (DEX) files, which contain the compiled code used to run your app. The Dalvik Executable specification limits the total number of methods that can be referenced within a single DEX file to 65,536—including Android framework methods, library methods, and methods in your own code. In the context of computer science, the term Kilo, K, denotes 1024 (or 2^10). Because 65,536 is equal to 64 X 1024, this limit is referred to as the '64K reference limit'.
Source AS Doc
I suspect your dex error is a result of the growth of a library, but without more info, this is hard to debug. The newest version of Android Studio (2.2) provides an APK analyzer tool that makes the dex limit more transparent.
When using Google Play services APIs you should double check to make sure that you're only including the ones used with these directives
compile 'com.google.android.gms:play-services-fitness:9.6.1'
rather than including everything (full list).
If you need all the libraries you're already depending on then, this is typically resolved by enabling multidex in your development environment (requiring development using a device or emulator with L or greater), but then using minificationEnabled in your release builds such that multidex isn't required in your release APK. This results in a combination of fast debug builds and non-multidex your release builds to prevent slow startup times for your release build.
A bit more info:
When you use native multidex in debug builds (requires minSdk set to L or greater) it results in faster incremental builds because modules and libraries deploy as separate dex files and less processing between deploys.
When you use minificationEnabled in your release build it often eliminates the need for the second dex file because methods from your dependendencies that you don't use are trimmed. This typically results in a single dex nullifying the negative effects of multidex (copying the N+1 dex files on app initialization for < version L devices).
Multidex issue occurs when you use lots of libraries in your project. If your app's code have more than 64k methods then it happens.
When your application and the libraries it references reach a certain
size, you encounter build errors that indicate your app has reached a
limit of the Android app build architecture.
You can refer some links like :
How to enable multidexing with the new Android Multidex support library
http://www.rapidvaluesolutions.com/tech_blog/multidex-issue-or-building-application-over-65k-methods/
https://mutualmobile.com/posts/dex-64k-limit-not-problem-anymore-almost
Related
I have a big Android Studio project that has a separate module which contains native code.
The native build is defined with CMake and includes a bunch of source files of C++ code.
I noticed that since a very recent update (might be Android Studio 3.5), NDK rebuilds everything ALL THE TIME.
It can happen with a small change in Java code, switching flavor in an unrelated module in the project, incrementing version code, etc.
This is a major problem, since it can waste 10 minutes at a time for no reason.
I could not find a reasonable way to profile NDK builds in Android Studio and check what's causing the rebuild or what's taking so long.
Unfortunately the project's build files are too big to attach here. Any pointers for things to look after?
For the C/C++ code that you build with cmake, make sure you point cmake to a directory where it can keep its object files and binary outputs.
Let's say you have a dependency on Game in your top level CMakeLists.txt like so:
# dependency: Game
set ( game_src_DIR ../Game )
add_subdirectory( ${game_src_DIR} ${CMAKE_CURRENT_BINARY_DIR}/game )
Then the second parameter to add_subdirectory specified the place where AndroidStudio will keep the object files.
The debug and release object files will live in different subdirs, as will each dependency, so switching debug/release will not clash.
This appears to have been a regression in behavior from Android Studio 3.4 and has been fixed in Android Studio 4.1 Canary 4. Release notes are here.
Often, it helps to split the AS project such that the C++ part, possibly with its Java wrappers become a separate (library) module. This module will hopefully be more stable, not sensitive to increments of app version code.
Such module should not define many flavors, but have careful matching to the flavors of other modules. Sometimes, it makes sense even to disable "debug" variants for such library. There was an effort of the NDK team to better handle debug vs. release build switching, but this is still tricky.
But if after these improvements, insignificant changes to the project still cause massive rebuild, I would suggest to consider ccache.
I have a game that I am working on that uses the LibGDX game framework. Currently the platforms I am targeting are Desktop (PC, Mac, Linux) via a platform independent jar and Android.
The project is hosted at https://github.com/NoxHarmonium/project-whiplash Feel free to take a look if you need to.
The bulk of the code is in a module called core and is written entirely in Kotlin. This module is linked into both the Desktop and Android projects.
This works fine for Android versions 7.1+ and for desktop. For all other versions of Android I get a pile of java.lang.NoClassDefFoundError exceptions on anonymous functions such as this:
val objectObservable = this.observableCache.computeIfAbsent(assetRef, fun(assetRef: AssetRef): Observable<T> {
return Async.start(fun(): T {
...
}).observeOn(this.eventLoopScheduler)
})
Exception Sample:
java.lang.NoClassDefFoundError: com.projectwhiplash.utils.assets.LibGdxDataManager$objectMapFromYaml$objectMapObservable$1
It seems to be caused by an incompatibility with the JVM that Kotlin targets by default (1.8) and the JVM level that older versions of Android support (1.6). I could be wrong but this explains why the latest version of Android works as it supports a later version of the JVM.
The solution should be as simple as forcing Kotlin to emit JVM byte code as version 1.6 but I can't seem to work it out. If you compile Kotlin directly into an Android, this seems to be handled by using the kotlin-android Gradle plugin. Unfortunately I can't use this plugin for the core module because it should not have any Android dependencies.
I tried to override the JVM version using the build settings mentioned at https://kotlinlang.org/docs/reference/using-gradle.html#compiler-options like this:
compileKotlin {
kotlinOptions {
jvmTarget = "1.6"
}
}
However, it did not seem to work no matter which Gradle file I placed it in. In fact I get a "Cannot resolve symbol 'kotlinOptions'" error shown by Intellij when I try it. It is possible that the Kotlin team have changed something and the documentation has not been updated.
I can override the Kotlin settings manually in the Intellij module settings but it gets overridden every time I sync the gradle project and is not a good long term solution. The project is designed to be IDE independent.
Does anyone know how I could set up the core module for max compatibility with older versions of Android?
I currently have the minimum API level set to 9 as this is the current LibGDX default but I'm willing to set this higher if it would be too difficult to target such a low API level.
Edit 1:
I just extracted the jar file produced by the core module and examined the class files using the javap tool.
I ran the following command on a random class file
java -verbose foo.class
and it output text with the following text
...
minor version: 0
major version: 50
...
using this question List of Java class file format major version numbers? I determined that the class file is actually targeting JVM 1.6.
Therefore my original theory is wrong and there is another reason why older Android versions cannot load classes generated by Kotlin lambdas.
It looks like you are using functionality that only exists within the JDK 8 library. Specifically the computeIfAbsent() method on the Map class.
Because of this, even though your code has been compiled down to JVM 1.6 compatibility, the underlying implementation on Android devices is missing that functionality and thus the reason for the NoClassDefFoundError exception you were seeing.
Updated: You can see in the javadoc located at https://docs.oracle.com/javase/8/docs/api/java/util/Map.html#computeIfAbsent-K-java.util.function.Function- that the computeIfAbsent() has only been around since JDK 8
So it is already known that the solution to this error:
com.android.dex.DexException: Too many classes in --main-dex-list, main dex capacity exceeded
Is to enable multidex support. I have done so, as well as applying proguard shrinking, but still get that error. Is there also a limit for multidex (presumably much higher than 64k)?
I am using several huge libraries, but it'd be nice to know the answer to this before somehow re-engineering everything to use fewer libraries. (I'm porting a desktop app to Android.)
The solution to that particular error message is not to enable multi-dex support. The error appears when multi-dex support is enabled, but the build system determined that too many methods need to be added to the main dex file for your app to work.
Depending on your Android Gradle plugin version you might be affected by this bug which causes too many methods to be included in the main dex file. The solution is to upgrade to Android Gradle plugin 2.2 or newer, and build tools 24 or newer.
If that does not solve the problem, you might indeed have to many (transitive) method dependencies in your main activities onCreate() code.
On building with Unity, I am getting an error:
trouble writing output: Too many method references: 78849; max is 65536.
You may try using --multi-dex option.
But I can't figure out how to explicitly tell Unity to use multi dex.
For activation multidex on Unity follow a 3 simple steps:
1) Use Gradle build system:
Unity -> File -> Build Settings -> Build System -> Gradle.
2) Check in "Сustom Gradle Template":
Player Setting... -> Publish Settings -> Check in "Сustom Gradle Template"
3) Change mainTemplate.gradle file:
Open file from path: Assets\Plugins\Android\mainTemplate.gradle
Add folow code:
android
{
...
defaultConfig
{
multiDexEnabled true
...
}
}
Additional information:
https://docs.unity3d.com/Manual/android-gradle-overview.html
I'd suggest to check official Unity3D forum regarding this question:
Multidex Support on Android
And Q/A's from there: Too many method references when I export android build
Here's reply from Unity's official:
So the biggest reason we have not added support for this in Unity is because it doesn't work. Or rather it usually creates more problems than it solves. Unless you are targeting only modern versions of Android I would suggest you do everything in your power including stripping and proguarding your code before you take the route of multi-dex.
If you don't believe me just checkout the known limitations of the multi-dex library at Googles developer page:
https://developer.android.com/tools/building/multidex.html#limitations
So conclusion is to do all best to shrink number of methods to stay within 65K limit. For example, if you use google-play-services.jar, you can replace it with only required submodules of it.
There's also discussion around exporting to Android Studio and build the project there but, apparently, it didn't work out due to dex issue.
Also, I advocate to read this write-up regarding DEX issue.
I hope, it helps. Good luck!
I am trying to integrate my application with Box, Dropbox, and Google Drive. All 3 of these services require a number of 3rd party jars. Additionally, my application already requires a few 3rd party jars. Now when I try to run my application from eclipse I get the following error:
Unable to execute dex: method ID not in [0, 0xffff]: 65536 Conversion
to Dalvik format failed: Unable to execute dex: method ID not in [0,
0xffff]: 65536
It seems that this error occurs because my application has too many methods. I'm fairly certain the bulk of these methods are from the 3rd party jars, so it is unrealistic to try to solve this by simplifying my code. I found these two suggestions online.
add dex.force.jumbo=trueto project.properties (and use adt version 21). I did this but still get the error.
Use multiple dex files as explained here: http://android-developers.blogspot.co.il/2011/07/custom-class-loading-in-dalvik.html. This seems likely to be the only option, but I don't understand how it applies in my case. The issue is that services like Drive have too many dependencies. Wouldn't this solution require that I modify the Drive source to use inflection when referring to its dependencies? (this is clearly not an option).
Use proguard to shrink remove unused code/methods. Exporting my application with proguard does work, and the document service integration works as expected on a >4.0 device. However, classnotfound errors are thrown when testing on a 2.3 device.
So, I am hoping for some advice on this issue. Is option 2 a solution for my case? Is there another solution I should consider?
You can also develop one or more of these as a plugin to your main app, in the form of a separate APK available for download. That APK would expose some component that the main app would use -- since I do not know the nature of your integration with these services, I cannot make a more specific recommendation about that. You would use your own signature-level custom <permission> to secure communications between the two apps. And, as a bonus, if using the third-party library adds requirements for additional permissions, you would only need those permissions in the plugin APK, keeping your main APK smaller.
***NEW**** All of the other answers are now outdated. Here's the new fix
Android 5.0 and higher
Multi-dex support is included automatically. From the docs:
Android 5.0 and higher uses a runtime called ART which natively
supports loading multiple dex files from application APK files. ART
performs pre-compilation at application install time which scans for
classes(..N).dex files and compiles them into a single .oat file for
execution by the Android device. For more information on the Android
5.0 runtime, see Introducing ART.
Below Android 5.0
Simply add the Android mult-dex support tool to your gradle build:
android {
compileSdkVersion 21
buildToolsVersion "21.1.0"
defaultConfig {
...
minSdkVersion 14
targetSdkVersion 21
...
// Enabling multidex support.
multiDexEnabled true
}
...
}
dependencies {
compile 'com.android.support:multidex:1.0.0'
}
The Dalvik VM can have a maximum of 65536 methods per dex file, due to the bytecode instruction set not having a way to refer to method numbers requiring more than 16 bits (as pointed out by #danfuzz in the comments).
While it is possible to fix this using multiple dex files, Facebook found another fix that they could deploy within their app to get around the problem.
See vm/LinearAlloc.c and you can find this code: (5MiB under Android 2.3.3, 8MiB after Android 4.0 as my investigation)
#define DEFAULT_MAX_LENGTH (5*1024*1024)
...
LinearAllocHdr* pHdr;
...
pHdr->mapLength = DEFAULT_MAX_LENGTH;
I suppose that the 'Facebook fix' is editing this memory by using native C pointer.
IMHO LinearAlloc problem and this method ID problem is different thing.
I faced this issue recently. After scouring the web for some more detailed implementation, I realized there wasn't much out there other than:
Good but a little outdated now that Gradle is around: http://android-developers.blogspot.co.il/2011/07/custom-class-loading-in-dalvik.html
Not much details but a vague idea of how it could be done: https://www.facebook.com/notes/facebook-engineering/under-the-hood-dalvik-patch-for-facebook-for-android/10151345597798920
I realized that the problem was not necessarily that there were too many methods in my code, but that the full dex of my code and other libraries was the issue. So, if I could compile my code against the libraries but not include them in the classes.dex, and then dex the libraries separately, and then put it all together at runtime it should work. The only issue left to address is the class loader, which Facebook mentioned in passing.
So with a little reflection and some Groovy code, I think I came up with a relatively stable way to package the libraries and the application code into separate dex files.
https://gist.github.com/nickcaballero/7045993
Most of the problems with hitting the 65k method limit are related with the use of the mastodontic Google Play Services in your apps. Recently, you can get more granularity when using it.
Following this guide, you can use only parts that you want. Probably this will fix the problem, avoiding some black-magic tricks, or using multiDex. For example, if you only want Google Maps in your app (and you aren't using ads, wallet, google wear, analytics, etc...), using the entire dependency is a waste of time/space. You can use it that way:
compile com.google.android.gms:play-services-base:6.5.87
compile com.google.android.gms:play-services-maps:6.5.87
You can read the entire list of "parts" in this link
Here is a script I wrote for counting the number of methods in each jar (and in total) for a specific folder.
Once you count the methods you can concentrate on refactoring and removing heavy libraries.
You need to enable the Dex support for that. So you need to do these steps:
Gradle plugin v0.14.0 for Android adds support for multi-dex. To enable, you just have to declare it in build.gradle:
android {
defaultConfig {
...
multiDexEnabled = true
}
}
if app support > 5.0 (that is, if your minSdkVersion is 20 or below) you also have to dynamically patch the application ClassLoader, so it will be able to load classes from secondary dexes. for that you can add this lib.
dependencies {
...
compile 'com.android.support:multidex:1.0.0'
}
enable in code for that you have these option. choose one which suits you best
A. Add MultiDexApplication in manifest manifest
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.android.multidex.App"> <application
android:name="android.support.multidex.MultiDexApplication">
</application>
</manifest>
B. Extend the application by MultiDexApplication
public class App extends MultiDexApplication { .. }
C. install it in application in attaching base context.
public class App {
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
MultiDex.install(this);
..
}
}
For more go through this link MultiDex.