New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Flutter Gradle should automatically add MultiDex #21009

Open
slightfoot opened this Issue Aug 24, 2018 · 25 comments

Comments

@slightfoot
Copy link
Member

slightfoot commented Aug 24, 2018

Steps to Reproduce

Build app with more than 64K methods.

Logs

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

Recommended Changes

  1. Project template or flutter.gradle script should set the multiDexEnabled property to true, and add the multiDexKeepFile file('multidex-flutter.txt') config file. Example: multidex-flutter.txt

    io/flutter/app/FlutterApplication.class
    io/flutter/view/FlutterMain.class
    io/flutter/util/PathUtils.class
    
  2. Project template or flutter.gradle script should add the multidex dependency.
    implementation 'com.android.support:multidex:1.0.3'

  3. FlutterApplication should have the following addition.

    @Override
    protected void attachBaseContext(Context base)
    {
        super.attachBaseContext(base);
        MultiDex.install(this);
    }

Adding the multiDexKeepFile is important as the FlutterApplication class and its dependencies are required to me in the primary dex file or the Application class may its onCreate lifecycle event.

Flutter 0.5.7 • channel v0.5.7 • https://github.com/slightfoot/flutter
Framework • revision 66091f9696 (7 weeks ago) • 2018-07-09 12:52:41 -0700
Engine • revision 6fe748490d
Tools • Dart 2.0.0-dev.63.0.flutter-4c9689c1d2
@slightfoot

This comment has been minimized.

Copy link
Member Author

slightfoot commented Aug 24, 2018

Was just caught by this. @Hixie @eseidel I think this is important addition towards v1.0, especially when we have lots of users that have no previous app-dev experience, and its a big gotcha.

@slightfoot slightfoot closed this Aug 24, 2018

@slightfoot slightfoot reopened this Aug 24, 2018

@BugsBunnyBR

This comment has been minimized.

Copy link

BugsBunnyBR commented Aug 25, 2018

While multidex is important, it should not be the default option. It makes the app loading and compiling time longer.
I think it should be an opt-in feature. Maybe flutter cli could update the project structure to solve this when the developer requests to.

@zoechi zoechi added this to the Goals milestone Aug 25, 2018

@slightfoot

This comment has been minimized.

Copy link
Member Author

slightfoot commented Aug 25, 2018

@BugsBunnyBR I believe the compilation time is not as big of a deal, as it once was. Remember instant-run, and other tooling use multiple dex files and use adb install-multiple. The tooling has vastly improved since multidex's initial launch. Compilation time is a problem in the "Android" world, but because of hot-reload and hot-restart it's not so much of an issue when it comes to Flutter development. Yes, app-loading takes a hit to load multiple dex files, but that's only when there are more than one dex file, and only on pre-v21 since multi-dex is a no-op on v21+ because the framework loads multiple dex files now.

I think as long as it is handled by the Flutter framework and not left to the developer to figure out the black magic then I'm all happy for either, fixed or a opt-in feature, like a extra parameter on the create command, and/or a tick-box in the IntelliJ plugin.

Either way, along as something is done, I'm happy.

@Hixie

This comment has been minimized.

Copy link
Contributor

Hixie commented Sep 25, 2018

@Hixie

This comment has been minimized.

Copy link
Contributor

Hixie commented Sep 25, 2018

@Hixie

This comment has been minimized.

Copy link
Contributor

Hixie commented Sep 25, 2018

@Hixie

This comment has been minimized.

Copy link
Contributor

Hixie commented Sep 25, 2018

Maybe we should trigger this automatically when we hit the limit? (You're likely to hit the limit with some of our plugins.)

@gspencergoog gspencergoog added this to To do in Flutter Tool Tasks via automation Sep 25, 2018

@matthew-carroll

This comment has been minimized.

Copy link
Contributor

matthew-carroll commented Sep 26, 2018

Seems reasonable to support. I agree with the earlier comment that it should not be enabled by default. We might consider exposing this via pubspec for ephemeral projects. Once the host app is made editable, it's up to the developer to deal with this, I think.

@slightfoot

This comment has been minimized.

Copy link
Member Author

slightfoot commented Sep 28, 2018

I just upgraded to Android Studio 3.2.0 and saw this in the notes:

Native multi-dex enabled by default: The Android plugin for Gradle now enables native multidex for all modules that set minSdkVersion = 21 or higher.

This might have an effect on this ticket.

@gspencergoog

This comment has been minimized.

Copy link
Contributor

gspencergoog commented Sep 28, 2018

That's great, and it helps, but I don't think it has too much effect here, because Flutter supports API Level 16 and above, so we still have to do something about API levels 16-20.

@filiph

This comment has been minimized.

Copy link
Contributor

filiph commented Sep 28, 2018

Correct. Just setting minSdkVersion = 21 in build.gradle makes the multidex issue go away. But Flutter has minSdkVersion set to 16 by default, and it's one of the selling points of the SDK.

@slightfoot

This comment has been minimized.

Copy link
Member Author

slightfoot commented Sep 28, 2018

My earlier remark was not to set the minSdk, but rather to be aware of the build change, just in case the app developer changes the minSdk to 21 or above and screws up the possible fix for this ticket.

@slightfoot

This comment has been minimized.

Copy link
Member Author

slightfoot commented Sep 28, 2018

Either way you always going to need the multiDexKeepFile or some of the initialisation files might go in classes2.dex or something and the app fails to load.

@raveesh-me

This comment has been minimized.

Copy link

raveesh-me commented Oct 1, 2018

Maybe we should trigger this automatically when we hit the limit? (You're likely to hit the limit with some of our plugins.)

This would be amazing...

@drexel-ue

This comment has been minimized.

Copy link

drexel-ue commented Nov 10, 2018

I've just run into the same issue trying to build

D8: Cannot fit requested classes in a single dex file (# methods: 85866 > 65536)

attempting the fix now

@robert-stevens

This comment has been minimized.

Copy link

robert-stevens commented Nov 13, 2018

@drexel-ue i've just run into it now, do you have any fix?

@slightfoot

This comment has been minimized.

Copy link
Member Author

slightfoot commented Nov 14, 2018

Enable Multidex as described here, and adjust it with my instructions in the description of the ticket.
https://developer.android.com/studio/build/multidex

If that doesn't work, today is Wednesday which is Flutter #HumpDayQandA and you can join us on Zoom to chat about the issue and resolve it. https://tinyurl.com/HumpDayQandA

@Leisser

This comment has been minimized.

Copy link

Leisser commented Jan 1, 2019

Am new to flutter and also to app-dev but not sure how i must treat this code

@Override protected void attachBaseContext(Context base) { super.attachBaseContext(base); MultiDex.install(this); }

in my flutter application, am i supposed to put it in my main.dart. Am too confused.

I've edited my app/build.gradle script and enabled multiDexEnabled, added implementation 'com.android.support:multidex:1.0.3' and multiDexKeepFile file('multidex-flutter.txt').
The app crashes on after installation, thought am only missing the override bit but don't know how to handle it.

@wtoalabi

This comment has been minimized.

Copy link

wtoalabi commented Feb 9, 2019

Am new to flutter and also to app-dev but not sure how i must treat this code

@Override protected void attachBaseContext(Context base) { super.attachBaseContext(base); MultiDex.install(this); }

in my flutter application, am i supposed to put it in my main.dart. Am too confused.

I've edited my app/build.gradle script and enabled multiDexEnabled, added implementation 'com.android.support:multidex:1.0.3' and multiDexKeepFile file('multidex-flutter.txt').
The app crashes on after installation, thought am only missing the override bit but don't know how to handle it.

I am seriously looking for a solution to this.
I found this: https://stackoverflow.com/questions/53740908/flutter-enable-multidex-for-sdk-less-than-21
But just like you, no way to know where to put that snippet of code.

@slightfoot

This comment has been minimized.

Copy link
Member Author

slightfoot commented Feb 9, 2019

For those that are having problems.. follow these instructions.

  1. Open Android Studio and choose "Open and existing Android Studio project" and choose the android directory inside your project directory.

  2. Wait for gradle to sync.

  3. In the project explorer panel on the left open the app/java folder and keep expanding down until you find MainActivity.java then right click on the parent folder and choose New > Java Class. Name the new class appropriately for your app. I'll use ExampleApp. (do not actually use this name)

  4. In the new ExampleApp.java file edit your class to extend from FlutterApplication, and override attachBaseContext the result should look similar to below, save it.

    package com.example.mobile;
    
    import android.content.Context;
    
    import android.support.multidex.MultiDex;
    import io.flutter.app.FlutterApplication;
    
    
    public class ExampleApp extends FlutterApplication
    {
        @Override
        protected void attachBaseContext(Context base)
        {
            super.attachBaseContext(base);
            MultiDex.install(this);
        }
    }
    

    If you have any red errors or issues. Open your app modules build.gradle script and add this line to your dependencies section and run gradle sync again.
    compileOnly files("$flutterRoot/bin/cache/artifacts/engine/android-x64/flutter.jar")

  5. Now expand the manifests section in your project explorer and open AndroidManifest.xml. Find the <application> tag delete the contents of the android:name attribute and replace it with .ExampleApp (the name of your class from the previous step).

Those instructions show you how to create a android Application class and attach MultiDex support library to it. More info can be found here: https://developer.android.com/studio/build/multidex

Don't forget you'll need to add multiDexEnabled true and multiDexKeepFile file('multidex-flutter.txt') to your defaultConfig in your build.gradle file. Place the multidex-flutter.txt in your android\app folder. Example: multidex-flutter.txt

io/flutter/app/FlutterApplication.class
io/flutter/view/FlutterMain.class
io/flutter/util/PathUtils.class

Side Note: If you upgrade your Android project to use AndroidX then you'll need to change the dependency from com.android.support:multidex:1.0.3 to androidx.multidex:multidex:2.0.0 and replace android.support.multidex.MultiDex with androidx.multidex.MultiDex. Migration details here: https://developer.android.com/jetpack/androidx/migrate

@wtoalabi

This comment has been minimized.

Copy link

wtoalabi commented Feb 9, 2019

Very detailed.
Thanks so much!
Here are my issues...Note that this is a fresh flutter installation with only a handful official plugins installed for firebase.

Following your instructions, here are my observations and where i'm stuck:

  1. In the newly created java class file, this line:
    import android.support.multidex.MultiDex;
    shows "Cannot resolve symbol "MultiDex"

  2. I think this has to do with the error you warned about, so I added:
    compileOnly files("$flutterRoot/bin/cache/artifacts/engine/android-x64/flutter.jar")
    to my gradle.build dependencies.
    However, I cant see where to "sync gradle again" as you advised.
    And also, I am not sure if "$flutterRoot" should be passed in raw as it is, or if I should change that string to the directory of my flutter sdk.

I dont feel comfortable going further with the rest of the instructions until I am able to sort these 2 out.
Thanks so much for your help!

@slightfoot

This comment has been minimized.

Copy link
Member Author

slightfoot commented Feb 9, 2019

You need to have implementation 'com.android.support:multidex:1.0.3' in your app module dependencies section.

@wtoalabi

This comment has been minimized.

Copy link

wtoalabi commented Feb 9, 2019

Apologies if I am sounding so dumb....I cant see where to sync gradle.
I have added the line as suggested, yet the Cannot resolve symbol MultiDex still shows.
I am on the latest Android Studio (3.3.1)

@wtoalabi

This comment has been minimized.

Copy link

wtoalabi commented Feb 10, 2019

So, I realise I cant beat the 64k limit, at least not with my constraints (below 20SDKs are a must). I eventually settled with adding Proguard to the debug section of my buildTypes (within the project level build.gradle file).
The result was the same, as I got over 43,000 methods scrapped, to give me just a little above 27,000 methods.
And now, I get to test and debug my app on low SDK phones without hitting the limits as Proguards does amazing job of scrapping off unused methods.
Shout out to @slightfoot for helping me out...even via screen sharing sessions.
I am grateful!

@Leisser

This comment has been minimized.

Copy link

Leisser commented Feb 15, 2019

@slightfoot thanks for the help am humbled.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment