Have both GMS and HMS in the project - android
How does one go about having both Google Mobile Services and Huawei Mobile Services in the app?
Being that Huawei have lost the license over GMS, it seems we need to replace all the GMS services used in the apps with Huawei provided ones. What would a "best practice" be for this? Use flavors and somehow handle each class individually, or copy paste the project and start replacing? Or ... better yet, is there a way to perhaps have both and ... somehow let the app decide which service to use based on the device it's on? Obviously the last one would presume an increase in the APK file size.
Any ideas?
So, I managed to do it like this:
Defined two flavours
gms {
dimension "services"
buildConfigField "String", "SERVICE_USED", '"g"'
}
hms {
dimension "services"
buildConfigField "String", "SERVICE_USED", '"h"'
}
I use the "g" and "h" in the code whenever I need to decide on doing things like: the API requires a deviceType of "android" or "iOS" and with the inclusion of the Huawei build we defined another constant "huawei". I use SERVICE_USED to know what constant to send.
I then did this at the top of the build.gradle:
apply plugin: 'com.android.application'
if (getGradle().getStartParameter().getTaskRequests().toString().contains("Hms")) {
//*meh*
} else {
apply plugin: 'io.fabric'
}
because I was using fabric (and fabric / firebase ... don't really work with HMS) and I also did this at the very bottom of the build.gradle
if (getGradle().getStartParameter().getTaskRequests().toString().contains("Hms")) {
apply plugin: 'com.huawei.agconnect'
} else {
apply plugin: 'com.google.gms.google-services'
}
to only include the proper plugin.
I then started handling each thing that was using gms (maps, location, push notifications, analytics ) by making a wrapper and separating the code in each flavour. i.e. for push notifications i created a HPushNotif which has an getToken method. I define the same class and method in both flavours but I implement them according to the type of service (gms or hms).
I used this type of notation when including dependencies in the project:
//GMS stuff
gmsImplementation 'com.crashlytics.sdk.android:crashlytics:2.10.1'
gmsImplementation 'com.google.firebase:firebase-core:16.0.9'
gmsImplementation 'com.google.firebase:firebase-messaging:18.0.0'
gmsImplementation 'com.google.firebase:firebase-crash:16.2.1'
gmsImplementation 'com.google.android.gms:play-services-maps:16.1.0'
gmsImplementation 'com.google.android.gms:play-services-location:16.0.0'
gmsImplementation 'com.google.android.gms:play-services-tagmanager:16.0.8'
//HMS stuff
hmsImplementation 'com.huawei.agconnect:agconnect-core:1.0.0.300'
hmsImplementation 'com.huawei.hms:push:4.0.3.301'
hmsImplementation 'com.huawei.hms:maps:4.0.1.301'
hmsImplementation 'com.huawei.hms:location:4.0.3.303'
The gms and hms before the Implementation refer to the name of the flavours. Those dependencies will only be loaded when the appropriate BuildVariant is selected (i.e. appropriate flavour is being built).
Basically I wrapped the logic for maps, analytics, location and push notifications for both cases. This is how the structure looks. Nothing special.
That's it. When they created HMS they basically copied GMS class by class and methd by method. You'll see that the exact method names match exactly, to the calling parameters even and returning values. They're 99.99% the same. That makes things easier. Basically you just need to copy the code in two classes and import the proper things (at the top of the class). You rarely need to change the code you've already written for GMS.
Hope it helps someone.
Before I answer your question here is short explanation what is HMS and GMS:
HMS stands for Huawei Mobile Services
GMS stands for Google Mobile Services
You can publish your app (which is using Google's libraries) in Huawei's app store (named AppGallery) but this app will be visible and available to download only for Huawei's devices containing HMS+GMS (all devices till 2020 had HMS and GMS).
However the newer phones i.e. Mate 30 series, P40 - will have installed only HMS. So if you want to make your app visible for all Huawei devices (HMS+GMS and HMS) then you will have to implement in you app function for detecting what service is on on user's device. It will decide what proper function to call (i.e initialize instance of Huawei Maps or Google Maps).
Here is the code for detecting HMS and GMS:
For Huawei Mobile Services we use:
HuaweiApiAvailability.getInstance().isHuaweiMobileServicesAvailable(context);
https://developer.huawei.com/consumer/en/doc/development/HMS-References/huaweiapiavailability
For Google Mobile Services we use:
GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(context);
https://developers.google.com/android/reference/com/google/android/gms/common/GoogleApiAvailability
Here is the code how to properly handle detecting HMS and GMS:
public static boolean isHmsAvailable(Context context) {
boolean isAvailable = false;
if (null != context) {
int result = HuaweiApiAvailability.getInstance().isHuaweiMobileServicesAvailable(context);
isAvailable = (com.huawei.hms.api.ConnectionResult.SUCCESS == result);
}
Log.i(TAG, "isHmsAvailable: " + isAvailable);
return isAvailable;
}
public static boolean isGmsAvailable(Context context) {
boolean isAvailable = false;
if (null != context) {
int result = GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(context);
isAvailable = (com.google.android.gms.common.ConnectionResult.SUCCESS == result);
}
Log.i(TAG, "isGmsAvailable: " + isAvailable);
return isAvailable;
}
AFAIK these classes (HuaweiApiAvailability/GoogleApiAvailability) are available if you implement any of the Huawei's kit/Google's lib.
While it really depends on architecture of your app, there are 2 reasonable alternatives so far;
Using flavors and variants, this will give you more flexibility. Establishing the architecture and implementation would be relatively more time consuming yet it is a clean approach providing a nice isolation of code. Since those ecosystems has different markets (AppGallery for Huawei), with flavors and variants, it is quite handy to establish separate build pipelines. It gives you the ability of maintaining different apk for different ecosystem
Using a wrapper/bridge approach. Simply, implement the wrapper classes to decide and forward requests to corresponding endpoints. With this approach, it is possible to maintain single for both markets. HMS actually provides a robust tool for this. It analyzes the code which depends on GMS, then automatically generates wrapper classes and converts the original code to use wrapper classes. It is called "HMS Converter" and has an Android Studio plugin even. https://developer.huawei.com/consumer/en/huawei-toolkit/
Synthetizing all the good answers given before: https://github.com/abusuioc/from-gms-to-hms#step-5-integrate-hms-sdks-in-your-app
For most apps, a single build with dependencies on both GMS and HMS SDKs + deciding at runtime (based on the availability on the device) which one to use is the recommended way.
One has to set up google and huawei as productFlavors and subsequently, as sourceSets.
Root project build.gradle:
buildscript {
repositories {
google()
mavenCentral()
maven { url "https://developer.huawei.com/repo/" }
}
dependencies {
classpath "com.android.tools.build:gradle:7.2.2"
classpath "com.google.gms:google-services:4.3.13"
classpath "com.huawei.agconnect:agcp:1.7.0.300"
}
}
Module build.gradle:
plugins {
id "com.android.application"
id "androidx.navigation.safeargs"
}
def json_huawei_release = "src/huaweiRelease/agconnect-services.json"
def json_huawei_debug = "src/huaweiDebug/agconnect-services.json"
def json_google = "src/google/google-services.json"
if (getGradle().getStartParameter().getTaskRequests().toString().contains('Huawei')) {
if (project.file(json_huawei_debug).exists() || project.file(json_huawei_release).exists()) {
apply plugin: "com.huawei.agconnect"
}
}
if (getGradle().getStartParameter().getTaskRequests().toString().contains('Google')) {
if (project.file(json_google).exists()) {
println "found: ${project.file(json_google)}"
apply plugin: "com.google.gms.google-services"
apply plugin: "com.google.firebase.crashlytics"
} else {
println "missing: ${project.file(json_google)}"
}
}
android {
...
flavorDimensions "vendor"
productFlavors {
google {
dimension "vendor"
versionNameSuffix "-google"
}
huawei {
dimension "vendor"
versionNameSuffix "-huawei"
}
}
sourceSets {
google {
java.srcDirs = ['src/main/java', 'src/google/java']
}
huawei {
java.srcDirs = ['src/main/java', 'src/huawei/java']
}
}
}
dependencies {
/** Google Play Services */
googleImplementation "com.google.android.gms:play-services-base:18.0.1"
googleImplementation "com.google.android.gms:play-services-basement:18.0.0"
googleImplementation "com.google.android.gms:play-services-auth:20.0.0"
googleImplementation "com.google.android.gms:play-services-identity:18.0.0"
googleImplementation "com.google.android.gms:play-services-oss-licenses:17.0.0"
/** Google Firebase */
googleImplementation "com.google.firebase:firebase-auth:21.0.1"
googleImplementation "com.google.firebase:firebase-database:20.0.3"
googleImplementation "com.google.firebase:firebase-messaging:23.0.0"
googleImplementation "com.google.firebase:firebase-functions:20.0.1"
googleImplementation "com.google.firebase:firebase-crashlytics:18.2.6"
googleImplementation "com.google.firebase:firebase-analytics:20.0.2"
googleImplementation "com.google.firebase:firebase-perf:20.0.4"
// googleImplementation "com.firebaseui:firebase-ui-auth:8.0.0"
/** Huawei Mobile Services */
huaweiImplementation "com.huawei.hms:base:6.1.0.302"
huaweiImplementation "com.huawei.hms:push:6.1.0.300"
huaweiImplementation "com.huawei.hms:hianalytics:6.3.0.300"
/** Huawei AppGallery Connect */
huaweiImplementation "com.huawei.agconnect:agconnect-core:1.6.5.300"
huaweiImplementation "com.huawei.agconnect:agconnect-auth:1.6.5.300"
huaweiImplementation "com.huawei.agconnect:agconnect-remoteconfig:1.6.5.300"
huaweiImplementation "com.huawei.agconnect:agconnect-function:1.6.5.300"
huaweiImplementation "com.huawei.agconnect:agconnect-cloud-database:1.5.2.300"
huaweiImplementation "com.huawei.agconnect:agconnect-applinking:1.6.5.300"
huaweiImplementation "com.huawei.agconnect:agconnect-crash:1.6.5.300"
huaweiImplementation "com.huawei.agconnect:agconnect-apms:1.5.2.309"
huaweiImplementation "com.huawei.agconnect:agconnect-storage:1.5.0.100"
huaweiImplementation "com.huawei.agconnect:agconnect-appmessaging:1.6.5.300"
}
This permits to provide custom implementations for everything; it will build two different artifacts.
Switching build-variants and test-devices has to be considered when testing - but one can pass task-names and device serial-numbers in IDE run configurations (in order to run the correct build variant on the correct test device).
Both #AndreiBogdan and #deadfish's answer are correct. I'd like to add a little more:
First, you need to select a proper solution (G+H or G2H) based on the application scenario and development/test costs.
If you choose G+H solution, you need to check whether the GMS is available. If the GMS interface cannot be used properly, HMS is required. For details, refer to #deadfish’s answer. You are advised to use this solution, which can
Reduce the complexity of app packaging. A package can be released to both Google Play and AppGallery.
Reduce the code maintenance cost. The HMS+GMS adaptation layer code is added to the original logic code. In this way, proper code can be automatically called based on the mobile phone. That is, you only need to invoke the method to check whether GMS is available on the existing logic code, and you don’t need to maintain two sets of code.
If you choose G2H solution, the workload of compatibility test is small. You only need to test the new APK on Huawei phones. Release your app both on HUAWEI AppGallery and Google Play, with different packages. The app you release on AppGallery contains only Huawei's logic code. You may refer to #AndreiBogdan's answer, or see docs Supporting Multiple Channels.
As #captaink say, you can use HMS Toolkit Convertor. It supports G+H and G2H conversion. Currently, HMS Toolkit supports Java and Kotlin. Supported Android Studio versions: 3.3.2~4.1.
Related
Is it possible to get dependency version at runtime, including from library itself?
Background Suppose I make an Android library called "MySdk", and I publish it on Jitpack/Maven. The user of the SDK would use it by adding just the dependency of : implementation 'com.github.my-sdk:MySdk:1.0.1' What I'd like to get is the "1.0.1" part from it, whether I do it from within the Android library itself (can be useful to send to the SDK-server which version is used), or from the app that uses it (can be useful to report about specific issues, including via Crashlytics). The problem I can't find any reflection or gradle task to reach it. What I've tried Searching about it, if I indeed work on the Android library (that is used as a dependency), all I've found is that I can manage the version myself, via code. Some said I could use BuildConfig of the package name of the library, but then it means that if I forget to update the code a moment before I publish the dependency, it will use the wrong value. Example of using this method: plugins { ... } final def sdkVersion = "1.0.22" android { ... buildTypes { release { ... buildConfigField "String", "SDK_VERSION", "\"" + sdkVersion + "\"" } debug { buildConfigField "String", "SDK_VERSION", "\"" + sdkVersion + "-unreleased\"" } } Usage is just checking the value of BuildConfig.SDK_VERSION (after building). Another possible solution is perhaps from gradle task inside the Android-library, that would be forced to be launched whenever you build the app that uses this library. However, I've failed to find how do it (found something here) The question Is it possible to query the dependency version from within the Android library of the dependency (and from the app that uses it, of course), so that I could use it during runtime? Something automatic, that won't require me to update it before publishing ? Maybe using Gradle task that is defined in the library, and forced to be used when building the app that uses the library?
You can use a Gradle task to capture the version of the library as presented in the build.gradle dependencies and store the version information in BuildConfig.java for each build type. The task below captures the version of the "appcompat" dependency as an example. dependencies { implementation 'androidx.appcompat:appcompat:1.4.0' } task CaptureLibraryVersion { def libDef = project.configurations.getByName('implementation').allDependencies.matching { it.group.equals("androidx.appcompat") && it.name.equals("appcompat") } if (libDef.size() > 0) { android.buildTypes.each { it.buildConfigField 'String', 'LIB_VERSION', "\"${libDef[0].version}\"" } } } For my example, the "appcompat" version was 1.4.0. After the task is run, BuildConfig.java contains // Field from build type: debug public static final String LIB_VERSION = "1.4.0"; You can reference this field in code with BuildConfig.LIB_VERSION. The task can be automatically run during each build cycle.
The simple answer to your question is 'yes' - you can do it. But if you want a simple solution to do it so the answer transforms to 'no' - there is no simple solution. The libraries are in the classpath of your package, thus the only way to access their info at the runtime would be to record needed information during the compilation time and expose it to your application at the runtime. There are two major 'correct' ways and you kinda have described them in your question but I will elaborate a bit. The most correct way and relatively easy way is to expose all those variables as BuildConfig or String res values via gradle pretty much as described here. You can try to generify the approach for this using local-prefs(or helper gradle file) to store versions and use them everywhere it is needed. More info here, here, and here The second correct, but much more complicated way is to write a gradle plugin or at least some set of tasks for collecting needed values during compile-time and providing an interface(usually via your app assets or res) for your app to access them during runtime. A pretty similar thing is already implemented for google libraries in Google Play services Plugins so it would be a good place to start. All the other possible implementations are variations of the described two or their combination.
You can create buildSrc folder and manage dependencies in there. after that, you can import & use Versions class in anywhere of your app.
Difference between using tensorflow-lite:0.0.0-nightly and tensorflow-lite in an Android App
I am trying to debug an existing Android app that uses tensorflow-lite to detect objects. The app implements the tensorflow library like below : implementation('org.tensorflow:tensorflow-lite:0.0.0-nightly') { changing = true } implementation('org.tensorflow:tensorflow-lite-gpu:0.0.0-nightly') { changing = true } implementation('org.tensorflow:tensorflow-lite-support:0.0.0-nightly') { changing = true } But examples I have found online for object detection, have implemented tensorflow-lite in the following way : implementation 'org.tensorflow:tensorflow-lite-task-vision:0.2.0' My questions are: What is the difference between using the nightly snapshot and the "normal" library. From what I can gather online, the nightly build is an experimental branch and may contain bugs? I'm just confused about this because the existing app does not contain a reference to sonatype maven repository, which I understand was required to get the nightly builds to work in the app. allprojects { mavenCentral maven { name 'ossrh-snapshot' url 'http://oss.sonatype.org/content/repositories/snapshots' } } My second question is what does this do line do : { changing = true } ? PS: We are using our own custom trained model/tflite.
Changing, or snapshot versions are used when you need Gradle to get a new version of the dependency with the same name from time to time (once in 24 hours, unless specified explicitly otherwise). I believe that whoever chose the nightly version of tensorflow, was wrong. As you say, this version may have bugs, and worse, these bugs will change overnight. Find some fixed version that you are comfortable with, study its changelog, and reset your implementation to refer to this version.
Implementing Huawei Mobile Services
I've been reading the huawei's documentation to implement their services. The documentation is easy and clear, but I have a few doubts. Huawei's SDK can exist in the same apk for both stores? or I need implement a different apk for gms and hms? If I implement hms how can I know if hms is reading data from other devices like MOTO etc.
You can use both of HMS and GMS services. Some of HMS service support to non-Huawei devices (like Scan Kit) and some of kit tighlty bound the EMUI and Huawei phones. If you need to apply you already created applıcation moved into HMS ecosystem, you can use HMS Toolkit and quickly implemented HMS services. (details)
Huawei's SDK can exist in the same apk for both stores? - yes, you can create one app and implement libraries for GMS and HMS. If I implement hms how can I know if hms is reading data from other devices like MOTO etc. You call functions from Google's or Huawei's responsible for detecting services. Please check out my latest answer from here: https://stackoverflow.com/a/60587678/619673
There are a couple ways to handle this. Of course you can choose to maintain 2 sets of source code, which is highly not recommended, and you can choose to keep libraries from both sides, detect which service is available and call them accordingly. I would however recommend implementating different product flavours and build your product according to the platform. android{ flavorDimensions "default" productFlavors{ hmsVersion{ //select the dimension of flavor dimension "default" //Configure this flavor specific app name published in Huawei App Gallery resValue "string", "flavored_app_name", "App name" } gmsVersion{ //select the dimension of flavor dimension "default" //Configure this flavor specific app name published in Play Store resValue "string", "flavored_app_name", "App Name" } } } and then you can do something like this // HMS Flavor hmsVersionImplementation 'com.huawei.hms:hianalytics:4.0.3.300' // GMS Flavor gmsVersionImplementation 'com.google.firebase:firebase-analytics:17.4.0'
'ProductFlavor.resConfigs' has a value 'auto' which is obsolete and has not been replaced
How to fix the warning below? Are there any alternatives to 'auto'? Warning:DSL element 'ProductFlavor.resConfigs' has a value 'auto' which is obsolete and has not been replaced. It will be removed at the end of 2018 android { ... flavorDimensions "device", "paid", "market" productFlavors { phone { // Phone config version for the application resConfigs ("auto") dimension "device" matchingFallbacks = ['mobile'] } ... } ... }
This is the error after updating to Android Studio 3.1: Based on the official advise here the best thing to do is removing the tag entirely if you support all the languages or supply an array with the language's code your app supports like: resConfigs "en", "de", "it", "fr" // etc. etc. More info: This is one of the resources optimization proposed by the official documentation here so i decided to test this flag with those FirebaseUI dependencies in a sample project implementation "com.firebaseui:firebase-ui-auth:$firebase_ui_version" implementation "com.firebaseui:firebase-ui-database:$firebase_ui_version" implementation "com.firebaseui:firebase-ui-storage:$firebase_ui_version" creating the debug APK with both the options and those are the results: Using resConfigs "auto" the debug APK was: 3,793 KB Using resConfigs "en" (so 1 language only) the debug APK was: 3,294 KB This means that with all the string resources for all the languages of those dependencies I got only ~500KB of size increase. That's something you could reason about, you definitely should make a test with the dependencies you use and see if the size increase is negligible or not and consequently decide to provide the list of supported languages or remove the resConfigs option. PS: If you are using Android FirebaseUI this was one of the suggested optimizations, I've created an issue here about the thing and this has been solved immediately by the awesome guy called SUPERCILEX
auto is no longer supported because it created a number of issues with multi-module projects. Instead, you should specify the locale that your app supports. Android plugin 3.1.0 and higher ignore the auto argument, and Gradle packages all string resources your app and its dependencies provide. com.android.tools.build:gradle:3.2.0-alpha08 BaseFlavor.java * <p><b>Note:</b> <code>auto</code> is no longer supported because it created a number of * issues with multi-module projects. Instead, you should specify the locale that your app * supports, as shown in the sample above. Android plugin 3.1.0 and higher ignore the <code>auto * </code> argument, and Gradle packages all string resources your app and its dependencies * provide.
How to shrink code - 65k method limit in dex
I have a rather large Android app that relies on many library projects. The Android compiler has a limitation of 65536 methods per .dex file and I am surpassing that number. There are basically two paths you can choose (at least that I know of) when you hit the method limit. 1) Shrink your code 2) Build multiple dex files (see this blog post) I looked into both and tried to find out what was causing my method count to go so high. The Google Drive API takes the biggest chunk with the Guava dependency at over 12,000. Total libs for Drive API v2 reach over 23,000! My question I guess is, what do you think I should do? Should I remove Google Drive integration as a feature of my app? Is there a way to shrink the API down (yes, I use proguard)? Should I go the multiple dex route (which looks rather painful, especially dealing with third party APIs)?
It looks like Google has finally implementing a workaround/fix for surpassing the 65K method limit of dex files. About the 65K Reference Limit 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. Getting past this limit requires that you configure your app build process to generate more than one DEX file, known as a multidex configuration. Multidex support prior to Android 5.0 Versions of the platform prior to Android 5.0 use the Dalvik runtime for executing app code. By default, Dalvik limits apps to a single classes.dex bytecode file per APK. In order to get around this limitation, you can use the multidex support library, which becomes part of the primary DEX file of your app and then manages access to the additional DEX files and the code they contain. Multidex support for Android 5.0 and higher 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. See: Building Apps with Over 65K Methods Multidex Support Library This library provides support for building apps with multiple Dalvik Executable (DEX) files. Apps that reference more than 65536 methods are required to use multidex configurations. For more information about using multidex, see Building Apps with Over 65K Methods. This library is located in the /extras/android/support/multidex/ directory after you download the Android Support Libraries. The library does not contain user interface resources. To include it in your application project, follow the instructions for Adding libraries without resources. The Gradle build script dependency identifier for this library is as follows: com.android.support:multidex:1.0.+ This dependency notation specifies the release version 1.0.0 or higher. You should still avoid hitting the 65K method limit by actively using proguard and reviewing your dependencies.
you can use the multidex support library for that, To enable multidex 1) include it in dependencies: dependencies { ... compile 'com.android.support:multidex:1.0.0' } 2) Enable it in your app: defaultConfig { ... minSdkVersion 14 targetSdkVersion 21 .... multiDexEnabled true } 3) if you have a application class for your app then Override the attachBaseContext method like this: package ....; ... import android.support.multidex.MultiDex; public class MyApplication extends Application { .... #Override protected void attachBaseContext(Context context) { super.attachBaseContext(context); MultiDex.install(this); } } 4) if you don't have a application class for your application then register android.support.multidex.MultiDexApplication as your application in your manifest file. like this: <application ... android:name="android.support.multidex.MultiDexApplication"> ... </application> and it should work fine!
Play Services 6.5+ helps: http://android-developers.blogspot.com/2014/12/google-play-services-and-dex-method.html "Starting with version 6.5, of Google Play services, you’ll be able to pick from a number of individual APIs, and you can see" ... "this will transitively include the ‘base’ libraries, which are used across all APIs." This is good news, for a simple game for example you probably only need the base, games and maybe drive. "The complete list of API names is below. More details can be found on the Android Developer site.: com.google.android.gms:play-services-base:6.5.87 com.google.android.gms:play-services-ads:6.5.87 com.google.android.gms:play-services-appindexing:6.5.87 com.google.android.gms:play-services-maps:6.5.87 com.google.android.gms:play-services-location:6.5.87 com.google.android.gms:play-services-fitness:6.5.87 com.google.android.gms:play-services-panorama:6.5.87 com.google.android.gms:play-services-drive:6.5.87 com.google.android.gms:play-services-games:6.5.87 com.google.android.gms:play-services-wallet:6.5.87 com.google.android.gms:play-services-identity:6.5.87 com.google.android.gms:play-services-cast:6.5.87 com.google.android.gms:play-services-plus:6.5.87 com.google.android.gms:play-services-appstate:6.5.87 com.google.android.gms:play-services-wearable:6.5.87 com.google.android.gms:play-services-all-wear:6.5.87
In versions of Google Play services prior to 6.5, you had to compile the entire package of APIs into your app. In some cases, doing so made it more difficult to keep the number of methods in your app (including framework APIs, library methods, and your own code) under the 65,536 limit. From version 6.5, you can instead selectively compile Google Play service APIs into your app. For example, to include only the Google Fit and Android Wear APIs, replace the following line in your build.gradle file: compile 'com.google.android.gms:play-services:6.5.87' with these lines: compile 'com.google.android.gms:play-services-fitness:6.5.87' compile 'com.google.android.gms:play-services-wearable:6.5.87' for more reference, you can click here
Use proguard to lighten your apk as methods that are unused will not be in your final build. Double check you have following in your proguard config file to use proguard with guava (my apologies if you already have this, it wasn't known at time of writing) : # Guava exclusions (http://code.google.com/p/guava-libraries/wiki/UsingProGuardWithGuava) -dontwarn sun.misc.Unsafe -dontwarn com.google.common.collect.MinMaxPriorityQueue -keepclasseswithmembers public class * { public static void main(java.lang.String[]); } # Guava depends on the annotation and inject packages for its annotations, keep them both -keep public class javax.annotation.** -keep public class javax.inject.** In addition, if you are using ActionbarSherlock, switching to the v7 appcompat support library will also reduce your method count by a lot (based on personal experience). Instructions are located : http://developer.android.com/tools/support-library/features.html#v7-appcompat Actionbar http://developer.android.com/tools/support-library/setup.html#libs-with-res
You could use Jar Jar Links to shrink huge external libraries like Google Play Services (16K methods!) In your case you will just rip everything from Google Play Services jar except common internal and drive sub-packages.
For Eclipse users not using Gradle, there are tools that will break down the Google Play Services jar and rebuild it with only the parts you want. I use strip_play_services.sh by dextorer. It can be difficult to know exactly which services to include because there are some internal dependencies but you can start small and add to the configuration if it turns out that needed things are missing.
I think that in the long run breaking your app in multiple dex would be the best way.
Multi-dex support is going to be the official solution for this issue. See my answer here for the details.
If not to use multidex which making build process very slow. You can do the following. As yahska mentioned use specific google play service library. For most cases only this is needed. compile 'com.google.android.gms:play-services-base:6.5.+' Here is all available packages Selectively compiling APIs into your executable If this will be not enough you can use gradle script. Put this code in file 'strip_play_services.gradle' def toCamelCase(String string) { String result = "" string.findAll("[^\\W]+") { String word -> result += word.capitalize() } return result } afterEvaluate { project -> Configuration runtimeConfiguration = project.configurations.getByName('compile') println runtimeConfiguration ResolutionResult resolution = runtimeConfiguration.incoming.resolutionResult // Forces resolve of configuration ModuleVersionIdentifier module = resolution.getAllComponents().find { it.moduleVersion.name.equals("play-services") }.moduleVersion def playServicesLibName = toCamelCase("${module.group} ${module.name} ${module.version}") String prepareTaskName = "prepare${playServicesLibName}Library" File playServiceRootFolder = project.tasks.find { it.name.equals(prepareTaskName) }.explodedDir def tmpDir = new File(project.buildDir, 'intermediates/tmp') tmpDir.mkdirs() def libFile = new File(tmpDir, "${playServicesLibName}.marker") def strippedClassFileName = "${playServicesLibName}.jar" def classesStrippedJar = new File(tmpDir, strippedClassFileName) def packageToExclude = ["com/google/ads/**", "com/google/android/gms/actions/**", "com/google/android/gms/ads/**", // "com/google/android/gms/analytics/**", "com/google/android/gms/appindexing/**", "com/google/android/gms/appstate/**", "com/google/android/gms/auth/**", "com/google/android/gms/cast/**", "com/google/android/gms/drive/**", "com/google/android/gms/fitness/**", "com/google/android/gms/games/**", "com/google/android/gms/gcm/**", "com/google/android/gms/identity/**", "com/google/android/gms/location/**", "com/google/android/gms/maps/**", "com/google/android/gms/panorama/**", "com/google/android/gms/plus/**", "com/google/android/gms/security/**", "com/google/android/gms/tagmanager/**", "com/google/android/gms/wallet/**", "com/google/android/gms/wearable/**"] Task stripPlayServices = project.tasks.create(name: 'stripPlayServices', group: "Strip") { inputs.files new File(playServiceRootFolder, "classes.jar") outputs.dir playServiceRootFolder description 'Strip useless packages from Google Play Services library to avoid reaching dex limit' doLast { def packageExcludesAsString = packageToExclude.join(",") if (libFile.exists() && libFile.text == packageExcludesAsString && classesStrippedJar.exists()) { println "Play services already stripped" copy { from(file(classesStrippedJar)) into(file(playServiceRootFolder)) rename { fileName -> fileName = "classes.jar" } } } else { copy { from(file(new File(playServiceRootFolder, "classes.jar"))) into(file(playServiceRootFolder)) rename { fileName -> fileName = "classes_orig.jar" } } tasks.create(name: "stripPlayServices" + module.version, type: Jar) { destinationDir = playServiceRootFolder archiveName = "classes.jar" from(zipTree(new File(playServiceRootFolder, "classes_orig.jar"))) { exclude packageToExclude } }.execute() delete file(new File(playServiceRootFolder, "classes_orig.jar")) copy { from(file(new File(playServiceRootFolder, "classes.jar"))) into(file(tmpDir)) rename { fileName -> fileName = strippedClassFileName } } libFile.text = packageExcludesAsString } } } project.tasks.findAll { it.name.startsWith('prepare') && it.name.endsWith('Dependencies') }.each { Task task -> task.dependsOn stripPlayServices } project.tasks.findAll { it.name.contains(prepareTaskName) }.each { Task task -> stripPlayServices.mustRunAfter task } } Then apply this script in your build.gradle, like this apply plugin: 'com.android.application' apply from: 'strip_play_services.gradle'
If using Google Play Services, you may know that it adds 20k+ methods. As already mentioned, Android Studio has the option for modular inclusion of specific services, but users stuck with Eclipse have to take modularisation into their own hands :( Fortunately there's a shell script that makes the job fairly easy. Just extract to the google play services jar directory, edit the supplied .conf file as needed and execute the shell script. An example of its use is here.
If using Google Play Services, you may know that it adds 20k+ methods. As already mentioned, Android Studio has the option for modular inclusion of specific services, but users stuck with Eclipse have to take modularisation into their own hands :( Fortunately there's a shell script that makes the job fairly easy. Just extract to the google play services jar directory, edit the supplied .conf file as needed and execute the shell script. An example of its use is here. Just like he said, I replaces compile 'com.google.android.gms:play-services:9.0.0' just with the libraries that I needed and it worked.