How to adapt Android App to use Retrofit without multiDex - android

I have an Android project that would benefit from using Retrofit. It's still in very early stages and there are less than a dozen function and class definitions. As soon as I add Retrofit to Gradle, I have a DexIndexOverflowException.
It seems way too early to add multiDex to the project, especially since it seems to be triggered by the addition of one dependency. Surely, I must be doing something wrong since there basic examples don't appear to require multiDex. What should I be doing differently?
apply plugin: 'com.android.application'
android {
compileSdkVersion 23
buildToolsVersion "23.0.3"
defaultConfig {
applicationId "com.mydomain.myapp"
minSdkVersion 15
targetSdkVersion 23
versionCode 1
versionName "1.0"
//multiDexEnabled true
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:24.0.0-beta1'
compile 'com.android.support:support-v4:24.0.0-beta1'
compile 'com.google.android.gms:play-services:9.0.0'
compile 'com.squareup.retrofit2:retrofit:2.1.0'
//compile 'com.squareup.retrofit2:converter-gson:2.1.0'
//compile 'com.android.support:multidex:1.0.0'
}

The solution:
Remove the Google Play Services API dependency and only include the specific Google Play Services API dependencies as needed. I removed:
compile 'com.google.android.gms:play-services:9.0.0'
The reason:
The DEX file hold class definitions and adjunct data for your app including its dependencies. There need to be multiple DEX files if there are > 64K methods, including Android framework methods and library methods.
A brand new app starts out with a lot of methods.
I don't know how many methods are included in the Android framework, but it's a lot and I'm sure it's thousands to tens of thousands.
I don't know how many methods are included in Google Play Services, but rumor has it to be over 28k.
Between the Android framework and Google Play Services, there's probably not room for more than a few thousand more methods.
Retrofit is an abstraction layer built on top of OkHttp. OkHttp covers a lot of things including SPDY, HTTP/2, encryption, connection pooling, etc. and has dependencies on lots of Java frameworks. Any framework that covers something so extensively is going to have many methods. In my situation, it has enough to push past the 64K barrier. It's probably a reasonable amount.
Compile only necessary methods from Google Play Services This answer to a similar problem revealed that you can import only the APIs needed from Google Play Services very easily in the Gradle configuration. See Google's documentation for more information.

Okay, I had the same problem. If you want to use Retrofit with multiDex, and you want to keep Google Play Service in your project to use its APIs, follow the steps below:
1- Keep multiDexEnabled true on your Gradle
2- Add this dependency in your Gradle
compile 'com.android.support:multidex:1.0.1'
3- Create this class:
import android.app.Application;
import android.content.Context;
import android.support.multidex.MultiDex;
public class MultiDexApplication extends Application {
#Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
MultiDex.install(this);
}
}
4- On your AndroidManifest.xml add:
<application
android:name=".MultiDexApplication"
...
</application>
And now, try to recompile your project. I'm using version 8.3.0 of Google Play Service, and version 2.1.0 of Retrofit and Gson in my Gradle.

Related

Cannot fit requested classes in a single dex file, even for earlier commits which previously compiled fine

So I've just hit the maximum method count limit for my android project, which fails to build with the following error message:
Error: null, Cannot fit requested classes in a single dex file (# methods: 117407 > 65536)
I understand what the message means, and how to resolve it (running proguard, enabling multidex etc). My problem is that I don't understand why I'm suddenly getting this message - I was doing was removing some old bits of code which were redundant, hit build, and now I get this message.
Question 1: How can it be possible that my method count (117407 according to the error message) is suddenly massively over the limit (65536), even though I did not add any library dependencies? I actually removed code, and suddenly I have like 50 thousand methods too many?
Now this is where it gets really weird: I wanted to analyse the APK to figure out what's causing the problem, but of course I can't build it. So instead of enabling multidex I decided to revert my code to yesterday (which definitely absolutely did build fine yesterday - I have the app on my phone to prove it!), but I still get this build error message. I don't understand how this is possible. I tried reverting to several days ago, same thing (cloning a new repo and checking out an earlier commit).
So, question 2: How am I getting this build error for the exact same code which just yesterday built fine without error?
The only thing I can think of is that a library that I am using as a dependency has suddenly increased in size - but I'm declaring specific versions of everything in my gradle build, for example:
// RxJava
implementation 'io.reactivex.rxjava2:rxandroid:2.1.0'
implementation 'io.reactivex.rxjava2:rxjava:2.2.4'
// Retrofit
implementation 'com.squareup.retrofit2:retrofit:2.5.0'
implementation 'com.squareup.retrofit2:converter-gson:2.5.0'
So, surely my dependencies should not have changed?
Any ideas what I can do to figure this out are greatly appreciated. I've tried cleaning my project, and invalidating caches/restart in android studio. I really don't want to enable multidex or have to run proguard on my debug build.
Here's the full build.gradle:
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'
android {
compileSdkVersion 28
defaultConfig {
applicationId "XXXXXXXXX"
minSdkVersion 19
targetSdkVersion 28
versionCode 1
versionName "0.1"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
vectorDrawables.useSupportLibrary = true // see https://developer.android.com/studio/write/vector-asset-studio#sloption
}
buildTypes {
release {
minifyEnabled false
// Do code shrinking!
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
// Core stuff
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'com.android.support:appcompat-v7:28.0.0'
implementation 'com.android.support:recyclerview-v7:28.0.0'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
implementation 'android.arch.lifecycle:extensions:1.1.1'
implementation 'com.android.support:design:28.0.0'
implementation 'com.android.support:support-vector-drawable:28.0.0'
implementation 'com.google.android.gms:play-services-wearable:16.0.1'
// Dagger
implementation 'com.google.dagger:dagger:2.21'
kapt 'com.google.dagger:dagger-compiler:2.21'
// Dagger for Android
implementation 'com.google.dagger:dagger-android:2.21'
implementation 'com.google.dagger:dagger-android-support:2.21' // if you use the support libraries
kapt 'com.google.dagger:dagger-android-processor:2.21'
// Constraint layout
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
// Associated WearOS project
wearApp project(':wear')
// Common library project
implementation project(':common')
// These were added to resolve gradle error on the 'com.android.support:appcompat-v7:28.0.0' implementation:
// All com.android.support libraries must use the exact same version specification (mixing versions can lead to
// runtime crashes). Found versions 28.0.0, 26.1.0. Examples include com.android.support:animated-vector-drawable:28.0.0
// and com.android.support:support-media-compat:26.1.0
// This seems to be related to linking the wear project. If the wear project was not linked, the error went away.
implementation 'com.android.support:support-media-compat:28.0.0'
implementation 'com.android.support:support-v4:28.0.0'
// RxJava
implementation 'io.reactivex.rxjava2:rxandroid:2.1.0'
implementation 'io.reactivex.rxjava2:rxjava:2.2.4'
// Retrofit
implementation 'com.squareup.retrofit2:retrofit:2.5.0'
implementation 'com.squareup.retrofit2:converter-gson:2.5.0'
// Retrofit RxJava
implementation 'com.squareup.retrofit2:adapter-rxjava2:2.5.0'
// Retrofit logging:
implementation 'com.squareup.okhttp3:logging-interceptor:3.12.1'
// Room
def room_version = "1.1.1"
implementation "android.arch.persistence.room:runtime:$room_version"
implementation "android.arch.persistence.room:common:$room_version"
implementation "android.arch.persistence.room:rxjava2:$room_version"
kapt "android.arch.persistence.room:compiler:$room_version"
// For modern time handling (java.time requires API 26 or higher)
implementation 'com.jakewharton.threetenabp:threetenabp:1.1.1'
// Graphing
implementation 'com.github.PhilJay:MPAndroidChart:v3.1.0-alpha'
// Dropbox
implementation 'com.dropbox.core:dropbox-core-sdk:3.0.11'
// OpenCSV
implementation 'com.opencsv:opencsv:4.5'
}
EDIT
So after enabling multidex, there are some heavy dependencies showing up under the following TLDs when I analyse the APK using Android Studio (I'm not sure if I should be looking at defined or referenced method numbers?):
com.dropbox: 26000 defined methods, 34000 referenced methods
com.android (mainly support libraries): 18700 defined, 24600 referenced
org.apache (commons, log etc): 15000 defined, 15700 referenced
These alone take me up to the limit. I still don't get why this is suddenly happening though :( Surely if I have not added any libraries, these numbers should not have changed?
Simple add this to your gradle (Module: app) >> multiDexEnabled true
android {
defaultConfig {
...
minSdkVersion 21
targetSdkVersion 28
multiDexEnabled true
}
...
}
then Rebuild Project
in Menu click => Build>Rebuild Project.
After looking at your entire build gradle file, your issue definitely stems from your dependencies! Attempt to clean them up and remove as many as you can that you don't use. Chances are you were very close to the limit and any of those dependencies may have been cached using older versions. You can attempt to remove the entire build folder (and clean your gradle cache) but I am fairly certain the issue will not go away.
If all of these dependencies are required unfortunately you will have to go the routes you mentioned, either multi-dex or minifying debug builds. Multi-dex should be ok and shouldn't cause any unforeseen issues while minifying will slow down your builds and potentially cause Android Studio to become unstable (especially instant run/apply changes!)
Good luck, one thing to take from this is to keep your dependencies clean and precise, only add when absolutely needed, and if all else fails, multi-dex is your friend.
None of the answers they gave you were exhaustive. The problem lies in the Multidex. You must add the library in the app gradle:
implementation 'com.android.support:multidex:1.0.3'
After, you should add in the defaultConfig of the app gradle:
multiDexEnabled true
From the Android docs:
"If your minSdkVersion is set to 21 or higher, multidex is enabled by default and you do not need the multidex support library."
As an alternative to manually enabling multidex, you could simply increase your minSdkVersion if possible.
I would recommend building the application with multidex, and then extracting the method ids from the multiple dex files from the new apk, and also extract the method ids from the old, single-dex apk and comparing the two lists.
roughly, something like:
baksmali list dex new.apk
baksmali list method new.apk/classes.dex > new.list
baksmali list method new.apk/classes2.dex >> new.list
sort new.list > new.sorted.list
baksmali list method old.apk > old.list
diff new.sorted.list old.list
Although, if you're using proguard, you may need to figure out some way to apply the reverse proguard name mangling before comparing the lists.
After reading your question I can only suggest try invalidate cache and restart after and force refresh your dependency using this .
./gradlew build --refresh-dependencies
As your problem, I had to delete the build folder and the *.iml files (Android Studio project files) y I had to recreate the project, then the build and then all worked fine again.
I encountered the same issue and the solution was to enable Instant Run under File -> Setting -> Build, Execution, Deployment -> Instant Run and that solved my problem. Hope it's helpful.
I tried this.
hopes it helps, found it on some documentation
( forgot the url :( )
build.gradle app
dependencies {...
grdef multidex_version ='2.0.1'
implementation "androidx.multidex:multidex:$multidex_version"
}...
in bulid.gradle app
implementation 'com.android.support:multidex:2.0.1'
android {
multiDexEnabled true
}
android > app > build.gradle
android {
defaultConfig {
versionCode 1
versionName "1.0"
+ multiDexEnabled true
}
}
Add implementation 'com.android.support:multidex:1.0.3'
in dependencies block
dependencies {
+ implementation 'com.android.support:multidex:1.0.3'
}
Community wiki
Its easy just increase this minSdkVersion, targetSdkVersion :
Old:
defaultConfig { minSdkVersion 19 targetSdkVersion 28 versionCode flutterVersionCode.toInteger() versionName flutterVersionName }
New:
defaultConfig { minSdkVersion 21 targetSdkVersion 29 versionCode flutterVersionCode.toInteger() versionName flutterVersionName }
also this compileSdkVersion:
Old:
android { compileSdkVersion 28
New:
android { compileSdkVersion 29

What is the correct way to migrate a program from an old Android Studio version to a newer one?

I'm curious about this. I made an Android program using an earlier version of the Google Android Studio (I believe 2.3.x), and recently upgraded to a newer version that came out (3.1.2), however, when I tried to bring in the program directly to this version and fire it up, I got a lot of compile errors due to various things being out of date. I tried to fix these in a rather ad-hoc manner because I could not find any comprehensive information despite much googling detailing how to properly migrate a project and it seemed to work (and so I can't post what they were), but now it seems to have broken again (perhaps because of another automatic upgrade) and I am getting this, for which searching has not yielded anything helpful:
Could not find recyclerview-v7.jar (com.android.support:recyclerview-v7:27.1.1).
Searched in the following locations:
https://jcenter.bintray.com/com/android/support/recyclerview-v7/27.1.1/recyclerview-v7-27.1.1.jar
Please install the Android Support Repository from the Android SDK Manager.
Open Android SDK Manager
Yet I go to the "Android SDK Manager" (that shows as a link in the output) and it says the relevant package is both installed and apparently at its latest version (since it does not say any updates are available). Moreover I was not aware I was even using the control "recyclerview" in the program, so I am rather puzzled as to why I am getting this error and I suspect it is because I did the migration wrong (not surprising due to the frustrating lack of information). I still have the project original from the earlier version so I could repeat it, but I'd like to then know how to do it correctly as I suspect this is resulting from the fact that I don't really know what I'm doing - and so what is the way to do that?
FWIW, the present project build.gradle is
apply plugin: 'com.android.application'
android {
compileSdkVersion 27
buildToolsVersion "27.0.3"
defaultConfig {
applicationId "sg.simetricclock.kumari.metricclock"
minSdkVersion 18
targetSdkVersion 25
versionCode 2
versionName "1.1"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
productFlavors {
}
}
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
androidTestImplementation('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
implementation 'com.android.support:appcompat-v7:27.1.1'
implementation 'com.android.support.constraint:constraint-layout:1.1.2'
implementation 'com.android.support:support-v4:27.1.1'
implementation 'com.android.support:design:27.1.1'
testCompile 'junit:junit:4.12'
}
repositories {
maven {
url 'https://maven.google.com'
}
}
dependencies {
compile 'com.android.support.constraint:constraint-layout:2.0.0-alpha1'
}
dependencies {
compile 'com.android.support.constraint:constraint-layout:+'
}
First Change Your Compile statement to Implimentation and Then Change Your Targeted SDK Version 28 And Then SYNC Your Project. I Hope After That You Will Not get Error

The system cannot find the file(httpclient-4.0.1.jar) specified in android studio?

The system cannot find the file (httpclient-4.0.1.jar) specified in android studio ?
I am trying to create a new app in Android Studio, When I try to Run or debug it shows a compile time error like :
Error:Execution failed for task ':SampleApp:compileDebugJavaWithJavac'.
> java.io.FileNotFoundException: C:\Users\VM\Desktop\SampleApp\libs\httpclient-4.0.1.jar (The system cannot find the file specified)
build.gradle(Module:SampleApp) file
apply plugin: 'com.android.application' android {
compileSdkVersion 23
buildToolsVersion "23.0.2"
useLibrary 'org.apache.http.legacy'
defaultConfig {
applicationId "com.app.SampleApp"
minSdkVersion 14
targetSdkVersion 23
manifestPlaceholders = [manifestApplicationId : "${applicationId}",
onesignal_app_id : " ",
onesignal_google_project_number: ""]
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
}
} } dependencies {
compile project(':library_pull_to_refresh')
compile project(':view_pager_indicator')
compile 'com.google.code.gson:gson:2.4'
compile 'com.google.android.gms:play-services-ads:7+'
compile 'com.actionbarsherlock:actionbarsherlock:4.4.0#aar'
compile files('libs/httpclient-4.0.1.jar')
compile files('libs/universal-image-loader-1.8.6.jar')
compile 'com.onesignal:OneSignal:2.+#aar'
compile 'com.google.android.gms:play-services-gcm:7+'
compile 'com.google.android.gms:play-services-analytics:7+' }
Even after that I can't find any solution.
Please any one guide me.
Apache HTTP Client was removed since API level 23:
This preview removes support for the Apache HTTP client. If your app
is using this client and targets Android 2.3 (API level 9) or higher,
use the HttpURLConnection class instead. This API is more efficient
because it reduces network use through transparent compression and
response caching, and minimizes power consumption. To continue using
the Apache HTTP APIs, you must first declare the following
compile-time dependency in your build.gradle file:
android {
useLibrary 'org.apache.http.legacy'
}
Android is moving away from OpenSSL to the BoringSSL library. If
you’re using the Android NDK in your app, don't link against
cryptographic libraries that are not a part of the NDK API, such as
libcrypto.so and libssl.so. These libraries are not public APIs, and
may change or break without notice across releases and devices. In
addition, you may expose yourself to security vulnerabilities.
Instead, modify your native code to call the Java cryptography APIs
via JNI or to statically link against a cryptography library of your
choice.
Reference:
https://developer.android.com/preview/behavior-changes.html#behavior-apache-http-client
If you are using HttpClient for http request and response you can use HTTP Library by adding this line in your app gradle instead of adding jar file :-
android
{
useLibrary 'org.apache.http.legacy'
}
From where you downloaded your .jar file? I just downloaded it from "https://mvnrepository.com/artifact/org.apache.httpcomponents/httpclient/4.0.1" right now and it works.
Upd.: also you can try to add "compile group: 'org.apache.httpcomponents', name: 'httpclient', version: '4.0.1'" instead "compile files('libs/httpclient-4.0.1.jar')"

Bundle third-party library in my .aar

I'm building an Android library for music streaming. It has ExoPlayer as dependency (excellent library btw!).
We use this library for another project that we're also developing right now, in Xamarin. Currently, we need to add both libraries (my .aar and a .jar for ExoPlayer). That's a bit annoying to be honest, I'd love to just drop my .aar in, and go.
So two questions:
is there a way I can bundle the ExoPlayer inside my .aar, using gradle and stuff? (I'm quite a beginner here, be thorough please)
I realise it might not be the best thing to do (dependency should be managed by app, blah blah blah), but really we will always test ExoPlayer and my lib together every time we update the former. So is there a strong reason I should not bundle ExoPlayer in my lib, or is that ok?
And here's my current gradle file. Nothing exciting to look at though. But as I converted the initial app to a lib, maybe there's something odd, who knows...
apply plugin: 'com.android.library'
android {
compileSdkVersion 23
buildToolsVersion "23.0.2"
defaultConfig {
minSdkVersion 19
targetSdkVersion 23
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile ('com.google.android.exoplayer:exoplayer:r1.5.6')
testCompile 'junit:junit:4.12'
testCompile 'org.mockito:mockito-core:1.10.19'
compile 'com.android.support:appcompat-v7:23.2.0'
}
There are no solutions for this until this issue is solved, but a viable alternative for someone can be to bundle the .aar(s) singularly using the "Import .JAR/.AAR Package" while clicking on "New Module".
The only problem with this approach is that you can bundle only 1 AAR at a time in this way, so you need to create a module for each one of them.
In alternative you can try with Kezong fat aar library:
https://github.com/kezong/fat-aar-android

Multiple Build Flavors with and without Ads (new Google Developer Policy)

I have published an App with 2 Build Flavors: a "normal" version including ads and an ad-free-version.
In the Google Play Developer Console you now have to mark your App if it uses Ads. This is ok for the normal version but the ad-free-version uses the same dependencies as the pro version (especially google play services). So I get a warning when I set this version to ad-free because ad-libs were found.
Is it possible to change dependencies depending on gradle build flavor?
build.gradle:
android {
(...)
productFlavors {
lite {
signingConfig signingConfigs.Release
versionCode 14
versionName '1.1.5'
buildConfigField "boolean", "IS_PRO", "false"
}
pro {
applicationId 'com.example.exampleadfree'
signingConfig signingConfigs.Release
targetSdkVersion 21
versionCode 14
versionName '1.1.5'
buildConfigField "boolean", "IS_PRO", "true"
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:support-v4:21.0.3'
compile 'com.android.support:appcompat-v7:21.0.3'
compile 'com.android.support:cardview-v7:21.0.2'
compile 'com.google.android.gms:play-services:6.1.+'
compile project(':libraries:SharedItems')
compile 'com.android.support:recyclerview-v7:21.0.2'
}
You can change
compile 'com.google.android.gms:play-services:6.1.+'
to
liteCompile 'com.google.android.gms:play-services:6.1.+'
and that will include the play services lib with your lite version only.
But you are not done, because now the code in your app that creates the AdView and related classes from the play services library will not compile when you create the pro version.
My solution in a similar situation (with the billing library) was to move all the code that refers to the excluded library and related classes to a source file which is also only built with the lite flavor, and then provide a dummy implementation for the pro version that does not refer to the library.
For example, create two flavor-specific src directories with the same-named java class in each:
src/lite/java/com/example/myapp/util/AdUtil.java
src/pro/java/com/example/myapp/util/AdUtil.java
In the lite version of AdUtil, you can make calls to google play services and get an AdView to return:
View getAdView(...)
{
View adView = new AdView(...);
adView.setAdSize(...);
adView.setAdUnitId(...);
...
return adView;
}
And in the pro version of that class, you can just put a dummy implementation that does not refer to the play services lib:
View getAdView(...)
{
return null;
}
Then in your main app code, when you call AdUtil.getAdView(), you will get a View in the lite version which you can place on the screen. In the pro version you will get a null so you skip adding the view (but you are likely already checking if you are pro or lite before trying to create the adview in the first place).
When a project declares Product Flavors, these extends the main configuration.
From here. So Product Flavors effectively adds new configurations for every flavor your declare. In gradle it is possible to add dependencies that are specific to a configuration. For example,
dependencies {
<configname> <dependency>
}
If you want to list all the configurations that your project has added:
configurations.findAll().each{println "$it.name"}
In the case if your project you'll see configs that are named the same as your product flavors. So as #cwbowron commented, to add a compile-time dependency for flavor lite:
dependencies {
liteCompile <dependency>
}
From the Google Play Support Chat I was addressed to say "No" in Google Play Console, despite the detection. So there shouldn't be problem in including the Google libs.
On the other hand, Doug's answer is elegant.
Regards,

Categories

Resources