Can I enable multidex in Android debug build only? - android

Dears,
I read in many blog posts that multidex apps startup is slower than normal apps.
My app uses a lot of libraries that exceed 64k methods so I use multidex. But when I use proguard in release build, the final apk becomes less than 64k methods
So My question is: Can I enable multidex in Android debug build only so I don't have runtime error? and disable multi dex in release build as I don't need it?
If yes, how ?
If No, Is Android smart enough to speedup startup as it should recognize that app didn't exceed 64k even if it is multi dex app ?

Yes, you can. When you declare your buildTypes include multidex only for debug:
buildTypes {
release {
multiDexEnabled false
    }
debug {
multiDexEnabled true
}
}

Instead of enabling multidex only for debug, you can change your min sdk version to 21 only for debug so gradle can speed up dexing with ART:
android {
productFlavors {
// Define separate dev and prod product flavors.
dev {
// dev utilizes minSDKVersion = 21 to allow the Android gradle plugin
// to pre-dex each module and produce an APK that can be tested on
// Android Lollipop without time consuming dex merging processes.
minSdkVersion 21
}
prod {
// The actual minSdkVersion for the application.
minSdkVersion 14
}
}
...
buildTypes {
release {
runProguard true
proguardFiles getDefaultProguardFile('proguard-android.txt'),
'proguard-rules.pro'
}
}
}
dependencies {
compile 'com.android.support:multidex:1.0.0'
}
http://developer.android.com/tools/building/multidex.html

suggested methods are not needed anymore as android studio became "smart enough".
In fact, it will now give you a warning when you use minSdkVersion 21 (the old way) to speed up build time with dex:
You no longer need a dev mode to enable multi-dexing during
development, and this can break API version checks less...
In the past, our documentation recommended creating a dev product
flavor with has a minSdkVersion of 21, in order to enable multidexing
to speed up builds significantly during development. That workaround
is no longer necessary, and it has some serious downsides, such as
breaking API access checking (since the true minSdkVersion is no
longer known.) In recent versions of the IDE and the Gradle plugin,
the IDE automatically passes the API level of the connected device
used for deployment, and if that device is at least API 21, then
multidexing is automatically turned on, meaning that you get the same
speed benefits as the dev product flavor but without the downsides.

Yes, it even works with the multidex support library for Android versions prior to Lollipop with a little trick.
First specify multiDexEnabled for the debug build in build.gradle:
buildTypes {
...
debug {
...
multiDexEnabled true
}
}
Then create an AndroidManifest.xml file under src/debug.
src/debug/AndroidManifest.xml:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<application
android:name="android.support.multidex.MultiDexApplication"
tools:replace="android:name"/>
</manifest>
That should do the trick. If your app uses a custom application class then you have to create a subclass of your application class and specify the name of that subclass in the manifest.
The application subclass should look like this:
public class MyDebugApplication extends MyApplication {
#Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
MultiDex.install(this);
}
}

Related

openOptionsMenu does not work: wrong SDK?

I use openOptionsMenu() to open the menu in applications with special GUI - for example, apps that only show graphics (fullscreen apps without toolbars etc.), upon a long press of the screen.
I can see that I made it work in the past on other applications, and I suppose that it was by using in the Manifest,
android:minSdkVersion="15"
android:targetSdkVersion="15"
I am now unable to make this work (the code would be):
myButton.setOnLongClickListener(
new OnLongClickListener() {
#Override public boolean onLongClick(View v) {
openOptionsMenu();
return false;
}
}
);
I think that Android Studio is using the wrong SDK. In fact,
the values I put in the Manifest ("min" and "target" set to 15) are overridden;
the values used should be those of build.gradle - and again, I set "compile", "min" and "target" set to 15, but I do not see the app
working properly and I think the SDK selection may not be happening;
as I check the produced APK the Manifest entries for the SDK (minSdkVersion, targetSdkVersion) are missing.
The build.gradle file according to the Android Studio interface shows:
android {
compileSdkVersion 15
buildToolsVersion "28.0.2"
defaultConfig {
applicationId '[...AppID...]'
minSdkVersion 15
targetSdkVersion 15
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
}
}
productFlavors {
}
}
As written above, the AndroidManifest.xml in the compiled APK (after apktool -d) does not mention any SDK selection.
I expect that the app compiled for SDK 15 would open the (very required) menu upon call of openOptionsMenu(). What the compiled app does now, upon debugging, is to go instead in the method that would /close/ the menu (clearly a satanic intention).
EDIT: the information about the specific case of Google butchering openOptionsMenu() after some SDK version is at
openOptionsMenu function not working in ICS?
I suppose I have made a mistake.
I realized that my past, working code did not just compile for SDK 15, it also contained an override as follows, and as suggested by user tallicalord in the previously linked page:
#Override public void openOptionsMenu() {
Configuration cfg = getResources().getConfiguration();
if( (cfg.screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK)>Configuration.SCREENLAYOUT_SIZE_LARGE){
int shelf = cfg.screenLayout;
cfg.screenLayout = Configuration.SCREENLAYOUT_SIZE_LARGE;
super.openOptionsMenu();
cfg.screenLayout = shelf;
}else{
super.openOptionsMenu();
}
}
So it is possible that the SDK selection, even though more mysterious in Android-Gradle (which removes references in the Manifest), was not the problem.
I shall note that it is very unfortunate how Google manages to make people waste uncountable hours for problems that did not need to exist at all.

APKs supporting Android Wear must have a minimum SDK version of at least 23

I received this error while uploading my APK to GooglePlay:
"APKs supporting Android Wear must have a minimum SDK version of at least 23, this APK has 20."
Both my mobile app and wear app have their minimum SDKs set to 20.
I've never had any problems updating my app previously, this appears to be a new restriction.
Is it a valid error? I thought the minimum SDK for Wear is 20 [Android 4.4W / Kitkat]
I tried un-ticking: "Pricing & Distribution: Distribute your app on Android Wear [ ]", but the error still occurs.
The problem is that I have folks using SDKs 21 & 22 as well. Also, while it is a dedicated Wear app, it has some utility as a standalone mobile app as well.
Any advice?
Apparently these rejections are due to changes Google made in preparation for Android Wear 2.0 standalone apps. Sometimes this restriction is inadvertently blocking phone apps that contain Android Wear 1.0 apps due to a manifest entry.
If you have a uses-feature manifest entry in your host/phone app for android.hardware.type.watch, remove it and you should be able to upload and get past the validation. Complete manifest entry below:
<uses-feature
android:name="android.hardware.type.watch"
android:required="false />
I have same problem and I couldn't find different solution to upload Multiple APK. This solution is not best(there is no wear support 23-) but no way to upload APK.
First I seperate AndroidManifest.xml 23- between 23+ cause of
<uses-feature
android:name="android.hardware.type.watch"
android:required="false"/>
App gradle:
productFlavors {
demoVer {
versionName = android.defaultConfig.versionName + "-TEST"
}
prodVer {
signingConfig signingConfigs.config
minSdkVersion 17
maxSdkVersion 22
versionCode 74
}
prodVerMin23 {
signingConfig signingConfigs.config
minSdkVersion 23
versionCode 75
}
}
dependencies {wearApp project(path: ':wearapp', configuration: 'prodVerMin23Release')}
Wear gradle:
compileSdkVersion 23
buildToolsVersion "23.0.3"
defaultConfig {
minSdkVersion 20
targetSdkVersion 23
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
productFlavors {
prodVerMin23 {
minSdkVersion 20
}
}
I ran into the same problem and found that you have to upload Multiple APKs to Google Play.
One APK which supports API level 23 and up (included wear 23 and up) and another one which supports API level 20 to 22 (included wear 20 to 22).
More info:
https://developer.android.com/google/play/publishing/multiple-apks.html
P.S.: Sorry for my English.

May Proguard save going to multidex app ?

I have an app with a few libs, that reached the red-line of 65536 method count.
I achieved to setup the app as an multidex APK.
For size optimisation, I decided to use Proguard, because I just use a few feature of Guava and common.java.lang, and those libs bring their whole family with them.
After Proguard job, my app ref ~ 45 Kmethods
I often read that multidex app may crash time to time
And that because of second-dex runtime loading, this take time.
Does 4 and 5 are true ?
Then I just tried to not using mutidex, because my end methods count is < 56Kmethods with prodGuard, but it failed as if it has more !
To do so, I just put the gradle parameter multiDexEnabled to false
Is there something else to check/do ?
Here is a part of my Gradle :
android {
compileSdkVersion ANDROID_BUILD_SDK_VERSION
buildToolsVersion ANDROID_BUILD_TOOLS_VERSION
defaultConfig {
applicationId "XXXX"
targetSdkVersion ANDROID_BUILD_TARGET_SDK_VERSION
minSdkVersion ANDROID_BUILD_MIN_SDK_VERSION
versionCode ANDROID_BUILD_VERSION_CODE
versionName ANDROID_BUILD_APP_VERSION_NAME
// Enabling multidex support.
multiDexEnabled false
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
debuggable false
ext.enableCrashlytics = true
renderscriptOptimLevel 3
signingConfig android.signingConfigs.release
zipAlignEnabled true
minifyEnabled true
// shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro', 'proguard-rules-new.pro', 'proguard-rules-eventbus.pro', 'proguard-rules-firebase.pro', 'proguard-rules-fabric.pro', 'proguard-rules-leakcanary.pro'
}
debug {
debuggable true
renderscriptOptimLevel 3
applicationIdSuffix ".debug"
versionNameSuffix "debug"
}
}
I often read that multidex app may crash time to time
From the android developers documentation page (http://developer.android.com/tools/building/multidex.html#limitations):
Applications using a multidex configuration that make very large
memory allocation requests may crash during run time due to a Dalvik
linearAlloc limit (Issue 78035). The allocation limit was increased in
Android 4.0 (API level 14), but apps may still run into this limit on
Android versions prior to Android 5.0 (API level 21)
ART has built in support for multi-dex apks, so multi dexing should not cause any problems in lollipop and above. You may see issues on some devices running kitkat and below, although this should be rare unless you have a very high method count or memory requirements.
And that because of second-dex runtime loading, this take time.
Yes, multidex does slow down the very first start-up time of your app significantly. (upto 200% in case of yelp, when they went 20k methods above the limit) Even cold-start times increase.
Hence, if you can avoid multi-dexing, it is strongly recommended that you do so.
Even if you go above the limit, you should still try to minimize the method count as more and more methods slow down the app startup time on pre-lollipop devices.
In your case, if your build succeeded but if you're seeing run-time crashes (especially such as "No Class def. found") then it could be that you haven't configured proguard correctly, and it may be striping away some required components.
Timothy Meller from yelp has given a detailed talk on this issue, in which he also shares some multi-dex optimizations and the importance of proguard configurations:
https://www.youtube.com/watch?v=skmOBriQ28E
I'd recommend you watch this if you want a better understanding of multi-dexing on android

gradle - Android Studio build too slow multidex application

When I add to my project the multidex:true, and make an Application class that extends from the MultiDexApplication, my project build time passed from 20 sec to around 90 sec.How to do some faster?
If you are like me who already tried Vic Vu's solution but still can't avoid enabling multiDex then you can try this (as long as your are using a device that has Android 5.0 and above).
Note This will only speed up your development build. Your production build will still be slow.
Basically you need to introduce 2 product flavors one for dev and one for prod.
Add multiDexEnabled true
android {
productFlavors {
// Define separate dev and prod product flavors.
dev {
// dev utilizes minSDKVersion = 21 to allow the Android gradle plugin
// to pre-dex each module and produce an APK that can be tested on
// Android Lollipop without time consuming dex merging processes.
minSdkVersion 21
}
prod {
// The actual minSdkVersion for the application.
minSdkVersion 14
}
}
...
buildTypes {
release {
runProguard true
proguardFiles getDefaultProguardFile('proguard-android.txt'),
'proguard-rules.pro'
}
defaultConfig {
applicationId "com.something.something"
targetSdkVersion 23
versionCode 1
versionName "1.0.0"
multiDexEnabled true
}
}
dependencies {
compile 'com.android.support:multidex:1.0.1'
}
And I have a class which extends Application so I had to override attachBaseContext()
#Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
MultiDex.install(this);
}
If you are not extending Application simply use MultiDexApplication in your AndroidManifest.xml application tag.
Ensure that in your Android Studio Build Variants you are pointing to devDebug.
Read the complete instructions here https://developer.android.com/studio/build/multidex.html#dev-build
Supplying as an answer because this is better fit with the formatting.
To simply answer your question: No, there is no way. Multidex is a process meant to help lift the burden of the 65k method limit. This process is complicated and will simply make your build times longer.
The best you can can do is lower your method count.
In your build.gradle (supplied here) you're using:
`compile 'com.google.android.gms:play-services:8.3.0'`
But if you look at the most recent play services api you can pick and choose what services you actually need.
Look at Table 1 on this page.
Only use the ones you need. Google play services as a whole is somewhere around 30k methods.
That should help.
Multidexing uses more memory. As you get closer to your max heap size in Java you'll find Java spends more time doing GC than it does doing any real work, this can slow things down a lot.
I'd strongly recommend increasing the max heap size when using multidex. Add the following to the android closure in your build.gradle file to make the max heap size 4GB (Make it larger/smaller if you wish):
dexOptions {
javaMaxHeapSize "4g"
}
It depends.
You haven't specified it in your question, but if you just want to speed-up your development builds - then you can avoid the extra work. Official documentation includes a whole section about that.

Is there an easy way to have a test version of an app and a release version installed on an android phone?

Say I have a production version com.android.xyz and this is production
then I am developing something and i want to load both this version and the production version on my phone so it's side by side. I know I can create a new package like com.android.abc and then I would have a second app which is basically a clone of com.android.xyz.
Thoughts?
Thanks in advance,
Reid
IF you are using Android Studio with Gradle, there is an easy way to do this. I still keep the the same packageName in AndroidManifest.xml (at least current gradle needs this duplicate definition)
AndroidManifest.xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
android:installLocation="internalOnly"
package="com.android.xyz">
build.gradle
def devBuildName = "dev"
def testBuildName = "test"
android {
defaultConfig {
versionCode 70
versionName "2.2.3"
minSdkVersion 10
targetSdkVersion 19
packageName "com.android.xyz"
}
buildTypes {
debug {
packageNameSuffix "."+devBuildName
versionNameSuffix "-"+devBuildName.toUpperCase()
}
test.initWith(buildTypes.debug)
test {
packageNameSuffix "."+testBuildName
versionNameSuffix "-"+testBuildName.toUpperCase()
}
}
}
You can look at my full dev/release example at github.
You need to change the package name. IMO the easiest way to do this is by writing a perl/python script to iterate through the files and change the package name based on the build type. Or run a C style macro preprocessor over the files first.

Categories

Resources