3

I am trying to implement Android App Bundles and I need it to work outside of the Play store.

  • I have created a new project with an activity containing a button
  • I have created a new Dynamic Feature Module through File > New > New Module > Dynamic Feature Module

Now my Android project structure is as follows: (omitting not meaningful files)

app
 ├ AndroidManifest.xml
 ├ java
 │  └ my.package.name
 │    └ MainAtivity
 └ res
    └ layout
       └ activity_main.xml

dynamic_feature
 ├ AndroidManifest.xml
 ├ java
 │  └ my.package.name_dynamic_feature
 │     └ ModuleActivity
 └ res
    └ layout
       └ activity_module.xml
Gradle Scripts
 ├ build.gradle (project)
 ├ bulid.gradle (app)
 └ build.gradle (dynamic_feature)

My build.gradle (app) file contains

apply plugin: 'com.android.application'
android {
    compileSdkVersion 28
    defaultConfig {
        applicationId "my.package.name"
        minSdkVersion 21
        targetSdkVersion 28
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    dynamicFeatures = [":dynamic_feature"]
    bundle {
        language { enableSplit = true}
        density { enableSplit = true}
        abi { enableSplit = true}
    }
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
}
dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    api 'com.android.support:appcompat-v7:28.0.0-rc02'
    api 'com.android.support.constraint:constraint-layout:1.1.3'
    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'
}

My build.gradle (dynamic_feature) file contains

apply plugin: 'com.android.dynamic-feature'
android {
    compileSdkVersion 28
    defaultConfig {
        minSdkVersion 21
        targetSdkVersion 28
    }
}
dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation project(':app')
}

and for testing purposes I request my dynamic_feature with

Intent intent = new Intent()
    .setClassName(MainActivity.this, "my.package.name_dynamic_feature.ModuleActivity");
startActivity(intent);

in an OnClickListener attached to the button in my MainActivity.

Using Build > Build Bundle(s)/APK(s) > Build Bundle(s) everything builds fine, I get my app.aab file and I generate the out.apks file using bundletool with the following command

java -jar bundletool-all-0.9.0.jar build-apks --bundle=D:\folder\app.aab --output=D:\folder\out.apks

and after that, I extract base-master.apk and dynamic_feature-master.apk simply dragging them out of out.apks opened in WinRar.

I install base-master.apk and works fine except for when I click on the button because it obviously throws java.lang.ClassNotFoundException for not finding ModuleActivity.

While when I try to install my dynamic_feature-master.apk I get a message saying There was a problem while parsing the package and I don't see any noticeable error in the logcat when this message comes out. I don't know how to go forward.

Ryan Godlonton-Shaw
  • 584
  • 1
  • 5
  • 18
Cliff Burton
  • 3,414
  • 20
  • 33
  • 47
  • Why do you extract the APKs? Couldn't you just run "bundletool install-apks" on the *.apks generated by bundletool previously? – Pierre Apr 04 '19 at 16:05
  • @Pierre doing so the device will only get `base_master.apk` at the first time? I want my dynamic feature to be installed only on demand – Cliff Burton Apr 05 '19 at 07:20
  • When you use adb install, it tries to replace the app. Because the dynamic module APK is missing the base, it fails installing. However, I think there's a flag you can pass to adb to say that it's an additional APK as opposed to a replacement. I'll try to find it on Monday. Feel free to ping this thread again if I forget by then. – Pierre Apr 06 '19 at 14:09

2 Answers2

3

When you use adb install without additional flags, it tries to replace the app (thus removing the base APK). Because the dynamic module APK is missing the base, it fails installing.

To manually install a dynamic module APK (and its config splits) on top of the base APK, you can use the following command:

adb install-multiple -r --dont-kill -p com.myapp module-master.apk module-en.apk module-armv7a.apk module-xxxhdpi.apk

We should probably add a convenience method in bundletool to do the same. If you feel the need for this, then file a feature request on bundletool github project.

Note however that it doesn't simulate exactly what would happen when you install the module via the Play Core API, and in particular you will not get the INSTALLED callback.

The best way to test installation of modules today is to go through the internal test track of the Play Console.

Hope that helps,

Pierre
  • 15,865
  • 4
  • 36
  • 50
  • Thanks for your answer @Pierre, when I run `adb install-multiple...` I get the message _adb: failed to create session Exception occurred while executing: java.lang.IllegalArgumentException: Size must be positive_ and I don't know what it refers to. – Cliff Burton Apr 08 '19 at 13:02
  • _"Did you run this after the base was already installed?"_ Yeph! - I know that the Play Console is the best way to deliver dynamic features since Dynamic Delivery is specifically designed to work with the Play Store (I suppose) but for this project, to be standalone from the store is a must. I have read that you are a Google engineer and you are the top user of the `android-app-bundle` tag here, so I am confident asking you: is (or will be) dynamic delivery doable (eventually with the Play Core API) completely outside of the play store? – Cliff Burton Apr 09 '19 at 08:56
  • [Internal app sharing](https://support.google.com/googleplay/android-developer/answer/9303479?hl=en) was launched at Google I/O which helps the testing of dynamic features without having to create a release track, bump the versionCode or even sign the App Bundle. – Pierre May 18 '19 at 17:24
1

It's not okay to simply extract the apks using winrar out.apks file. If you want to install only the base apk, you can do it by (I assume the name of your base module is base-master):

bundletool install-apks --apks=D:\folder\out.apks --modules="base-master"

The --modules option gives you the chance to install just the specified modules. However, I guess there is no way to test the dynamic delivery (on-demand download of dynamic modules) locally since your app interacts with Play Core library.

By the way, if you are insisted on extracting the APKs, you can use bundle tool:

bundletool extract-apks 
     --apks=D:\folder\out.apks
     --output-dir=D:\folder2
noidsirius
  • 409
  • 2
  • 12