Android - Large Version Migrations
If your module is several DevApp versions behind the latest release, walking through each version's migration notes one-by-one is tedious and error-prone. This guide describes a faster approach: replace your local DevApp project with a fresh copy at the target version, then drop your module back in.
That single move picks up every change to the DevApp shell — build.gradle,
gradle.properties, the Gradle wrapper, default_config.gradle,
settings.gradle, build scripts, KSP / KAPT plugin shifts, JVM and AGP
upgrades, and any new template files we ship in the DevApp. The only thing
you still need to migrate by hand is your own module's build.gradle —
because that file is yours, the fresh DevApp can't update it for you.
Use this approach when you're jumping 3 or more minor versions (for
example, going from 26.2.0 to 26.6.0). For single-version bumps, the
per-release migration notes in the release notes
section are still the right path.
build.gradleThe DevApp swap updates everything except your own module's
build.gradle. If a release added KSP, dropped KAPT, bumped JVM versions,
removed the kotlin-android plugin alias, etc. — those changes still need
to be applied to your module's gradle file. Read the release notes for any
versions you're skipping over to know what to apply.
Quick reference (engineers)
For folks comfortable on the command line. Detailed walkthrough is in the next section.
The flow is: swap the whole DevApp project for a fresh one at the target tag, then re-attach your module.
Set two variables and run from the parent directory of your existing
devapp-android checkout. Replace the values with yours:
TARGET=26.6.0 # release tag you're migrating to
MODULE=my_module # your module's folder name in the project
# 1. Park your current project so you keep a fallback.
mv devapp-android devapp-android.old
# 2. Clone a fresh DevApp at the target tag.
git clone --branch "$TARGET" --depth 1 \
https://code.q2developer.com/q2e/development/mobile/msdk/devapp-android.git
cd devapp-android
# 3. Replace the example "module_name" with your module folder.
rm -rf module_name
cp -R "../devapp-android.old/$MODULE" "./$MODULE"
# 4. Wire your module into the fresh project.
# Edit settings.gradle: include ':module_name' -> include ":$MODULE"
# Edit devapp/build.gradle: implementation project(':module_name') -> implementation project(":$MODULE")
# 5. Restore your local-only files from the old project.
cp ../devapp-android.old/local.properties ./
cp ../devapp-android.old/google-services.json ./ # if you use Firebase
cp ../devapp-android.old/secure.properties ./ # if you use it
# 6. Update YOUR module's build.gradle to match the new toolchain.
# The fresh DevApp shipped a template module/build.gradle showing the
# expected shape. You deleted the folder in step 3, but the file is
# still in git — view it for reference:
git show "$TARGET:module_name/build.gradle"
# Common changes across versions: KAPT→KSP, kotlin-android alias removal,
# JVM target bumps. Apply them to ./$MODULE/build.gradle.
# 7. Build.
./gradlew clean assembleDebug
Green build → run a normal DevApp test cycle (sign in, load your module's
screens) to confirm runtime behavior. Once happy, delete
../devapp-android.old.
Detailed walkthrough
This section is for folks who want the same steps spelled out with more context — what each step does and what to look out for.
1. Identify what you need to keep
Before you touch anything, make a list of what's yours in the current local copy. Most of your DevApp project is shared scaffolding that ships with each release. The pieces specific to you usually are:
- Your module folder — typically named after your module (e.g.
my_payments_module/rather thanmodule_name/). Contains yourbuild.gradle,consumer-rules.pro,proguard-rules.pro, and your module's source code. local.properties— your local SDK path. Not committed to git.google-services.json— your Firebase config (if you use Firebase).secure.properties— any secrets your module reads from the build.- Edits to
settings.gradle— typically a one-lineinclude ':<your-module>'. - Edits to
devapp/build.gradle— typically a one-line dependency on your module, e.g.implementation project(':<your-module>'). - Edits to
gradle.properties— if you changedmoduleGroupId,sdkVersionName, etc.
Everything else — the root build.gradle, the Gradle wrapper, the
default_config.gradle, the module_name/ template — is DevApp
scaffolding that the fresh copy will provide for you.
2. Back up your current local copy
Don't delete anything yet. Rename your existing project directory so you can fall back to it if anything goes wrong.
cd /path/to/where/your/project/lives
mv devapp-android devapp-android.old
If you had uncommitted local changes in there, this preserves them. You can also push a branch to git first if you'd rather have your work in a remote backup.
3. Clone a fresh copy at the target version
Replace 26.6.0 with the version you're targeting.
git clone --branch 26.6.0 --depth 1 \
https://code.q2developer.com/q2e/development/mobile/msdk/devapp-android.git
cd devapp-android
--branch 26.6.0 checks out the release tag directly. --depth 1 skips
historical commits to make the clone fast.
4. Remove the example template module
The fresh DevApp ships with an example module called module_name/. You
don't need it — your module is going to take its place.
rm -rf module_name
5. Drop your module into the fresh project
Copy your module folder from the old project into the new one.
cp -R ../devapp-android.old/<your-module-folder> ./<your-module-folder>
Replace <your-module-folder> with whatever your module is actually named.
6. Wire your module into the fresh project
The new DevApp's settings.gradle and devapp/build.gradle reference the
example module_name module. Update both to reference yours instead.
settings.gradle — find the line that reads:
include ':module_name'
Replace module_name with your module's folder name. If you have multiple
modules, add include lines for each.
devapp/build.gradle — find the dependencies block. Look for:
implementation project(':module_name')
Replace it with a line referencing your module(s).
7. Restore your local-only files
Copy these from your .old directory back into the fresh project:
cp ../devapp-android.old/local.properties ./
cp ../devapp-android.old/google-services.json ./ # if you use it
cp ../devapp-android.old/secure.properties ./ # if you use it
8. Update YOUR module's build.gradle
This is the step that the DevApp swap cannot do for you. The fresh
DevApp's example module_name/build.gradle shows what shape a module
gradle file should have at the target version. Compare:
module_name/build.gradlefrom the fresh clone (the new shape) — but remember you deleted it in step 4. You cangit show 26.6.0:module_name/build.gradleinside the fresh repo to see it again, or peek at the DevApp template on GitLab.- Your module's existing
build.gradle(the old shape).
Apply the differences to your module's build.gradle. The most common
changes you'll see across versions:
- JVM target bumps — for example,
jvmTarget = "17"→ derived fromq2libs.versions.javaVersion. - KAPT → KSP —
alias q2libs.plugins.kotlin.kapt→alias q2libs.plugins.ksp, and everykapt(...)dependency →ksp(...). - Plugin alias removals — for example,
kotlin-androidno longer needs to be applied as a separate plugin in newer AGP versions. apply plugin: 'kotlin-android'indefault_config.gradlemay be removed.
Read the release notes for each version you're skipping over and apply any module-side changes those notes call out.
9. Update gradle.properties if needed
The fresh gradle.properties will already have the correct q2Version
for the target release. But your old project may have customized values
elsewhere — for example, moduleGroupId or sdkVersionName. Open both
files side-by-side and copy any of your customizations forward.
10. Build it
./gradlew clean assembleDebug
If the build fails, the error usually points at your module's
build.gradle (step 8) or a missing reference in settings.gradle /
devapp/build.gradle (step 6).
11. Run a full test cycle
Even if the build is green, run your module through a normal DevApp test cycle. Sign in, open the screens your module owns, run any flows that talk to the host — confirm everything still works. Toolchain bumps sometimes change subtle runtime behavior (lifecycle ordering, Compose recomposition, R8 rules) that a clean build won't catch.
12. Test with R8 / minify enabled
Toolchain upgrades — especially major AGP, Kotlin, or Hilt bumps — are the most likely place for new R8 issues to appear. Enable minify in your DevApp build and run through your module again.
See Consumer ProGuard Rules for how to do this.
13. Clean up
Once you're confident everything works, delete the backup:
rm -rf ../devapp-android.old
If you committed your work onto a backup branch, you can leave that branch in place as long as you'd like — just don't merge it.
Troubleshooting
Build fails with Unresolved reference: <something>
Your module is referencing something the new SDK Interfaces version removed or renamed. Search the release notes for the missing symbol — the release that removed it will explain what to use instead.
Build fails with Plugin [...] not found
Your module's build.gradle is applying a plugin that no longer exists
in the version catalog at the target version. The most common offenders
are kotlin-android and kotlin-kapt. See step 8 above.
Build succeeds but module crashes on launch
This is almost always a missing ProGuard / R8 keep rule. Test with minify disabled first to confirm it's R8 — if the crash goes away with minify off, it's an obfuscation issue. See Consumer ProGuard Rules.
My host app uses a different DevApp branch
If your team customizes the DevApp itself (rather than just consuming releases), you can't simply replace it with the upstream copy — you'd lose your customizations. In that case, do this in reverse: pull the upstream tag into a branch and merge it into your customized DevApp, resolving conflicts.
Why this approach works
Each DevApp release ships two distinct things:
- Code that lives inside your module — your business logic, your
build.gradle, your ProGuard rules. These are yours and Q2 never touches them. - Scaffolding around your module — the host DevApp that loads your module, the Gradle build wiring, the test harness, the build scripts. This is shared across every Q2 mobile module and updates as the platform evolves.
A migration is really a migration of (2). Reading per-version release
notes and re-applying every scaffolding change by hand is error-prone
because the scaffolding isn't a single file — it's spread across build.gradle,
gradle.properties, default_config.gradle, the wrapper, the build
scripts, and template files. Replacing your local copy with a fresh one
is an exact, atomic update of (2). All you're left with is (1) — your
own module — which you'd have to migrate either way.