A simple standalone Python script with no extra library dependencies that merges multiple .apk
files (like in aab
format, or splitted by native libraries arch (arm64-v8a
, armeabi-v7a
, x86_64
, x86
), dpi resources (xxxhdpi
, xxhdpi
, xhdpi
, hdpi
, etc.), string localizations (en
, de
, etc.), etc. by the app store) into a single universal old-school "fat" .apk
file.
First, you have to get all the split apk files you want to merge. There are multiple ways to do this.
If you want to merge an apk from your device installed from the Google Play Store, you must first dump those apk files from your device to our computer using the adb
tool.
Let's see an example of the Telegram app.
First, install it from Google Play. Second, we have to know its package name. The pm
commandline tool can give us all the paths to all apk files. We know that the app's package name should contain a telegram
string. Let's find it via the pm
tool among all installed third-party applications on the device (pm list packages
to get all the installed application packages, and -3
flag to filter only user-installed packages):
$ adb shell pm list packages -3 | grep telegram
package:org.telegram.messenger
Now, we know its package name: org.telegram.messenger
. Let's get the apk file paths:
$ adb shell pm path org.telegram.messenger
package:/data/app/~~cMh9jYycQY1WXQGmCCN5bw==/org.telegram.messenger-9E3jXBJfFDorL8hQKF-xRg==/base.apk
package:/data/app/~~cMh9jYycQY1WXQGmCCN5bw==/org.telegram.messenger-9E3jXBJfFDorL8hQKF-xRg==/split_config.arm64_v8a.apk
package:/data/app/~~cMh9jYycQY1WXQGmCCN5bw==/org.telegram.messenger-9E3jXBJfFDorL8hQKF-xRg==/split_config.en.apk
package:/data/app/~~cMh9jYycQY1WXQGmCCN5bw==/org.telegram.messenger-9E3jXBJfFDorL8hQKF-xRg==/split_config.xxhdpi.apk
Now all we have to do is dump these files via the adb pull
command:
$ adb pull /data/app/~~cMh9jYycQY1WXQGmCCN5bw==/org.telegram.messenger-9E3jXBJfFDorL8hQKF-xRg==/base.apk
$ adb pull /data/app/~~cMh9jYycQY1WXQGmCCN5bw==/org.telegram.messenger-9E3jXBJfFDorL8hQKF-xRg==/split_config.arm64_v8a.apk
$ adb pull /data/app/~~cMh9jYycQY1WXQGmCCN5bw==/org.telegram.messenger-9E3jXBJfFDorL8hQKF-xRg==/split_config.en.apk
$ adb pull /data/app/~~cMh9jYycQY1WXQGmCCN5bw==/org.telegram.messenger-9E3jXBJfFDorL8hQKF-xRg==/split_config.xxhdpi.apk
Alternatively, this can all be done in a single convenient one-liner:
adb shell pm path org.telegram.messenger | sed 's/package://g' | xargs -L 1 adb pull
If you use zsh
on MacOS or Linux distro, you can use this function (add it to your .zshrc
file):
function pullapks() {
adb shell pm path $1 | sed 's/package://g' | xargs -L 1 adb pull
}
And then just call it like this:
pullapks org.telegram.messenger
There are a number of services that allow you to download applications from Google Play for different device profiles via a convenient web interface.
For example, you want to have an apk that will have native libraries for all possible architectures.
You simply download multiple different versions of it from apkcombo
settings for a different architecture type (arm64-v8a
, armeabi-v7a
, x86_64
, x86
) for each download.
Then you can merge them all with the script and get a universal apk this way.
The usage of the script is very simple.
First, clone the repo and make sure the script has execution permission:
git clone https://github.com/LuigiVampa92/merge-apks
cd merge-apks
chmod +x mergeapks.py
Get your apk files ready, put them near the script, and execute the script:
python mergeapks.py base.apk split_config.arm64-v8a.apk split_config.xxhdpi.apk split_config.en.apk
Note that you must set the path to the base
apk as the first argument and paths to other config apks after it as subsequent arguments.
You can put the symlink to this script into the path. Like this (the absolute path to the script depends on your OS and home directory settings):
ln -s /home/username/github/merge-apks/mergeapks.py /usr/local/bin/mergeapks
After that, the script can be executed from any directory, like this:
mergeapks base.apk split_config.arm64-v8a.apk split_config.xxhdpi.apk split_config.en.apk
The result apk file result.apk
will be placed into your current working directory, from which you have executed the script.
You do not need any Python dependencies to run the script; however, you MUST have some tools installed in your OS, and paths to their executable MUST be set to the $PATH
environment variable. The script relies on that.
These tools are apktool, zipalign and apksigner.
apktool
can be installed via your OS package manager: apt
, brew
, whatever, or pulled directly from GitHub. zipalign
and apksigner
are part of the Android SDK build-tools distribution and must be installed via sdkmanager
in Android Studio or via CLI.
Do not forget to make symlinks of these tools to the system's $PATH
environment variable, OR add the entire build-tools directory to it.
Since repackaging the splitted app bundle into the universal apk requires changing the original app's manifest file, the original signature will be broken, and the app must be resigned before you can install it on a real device.
The easiest way to do it is to create an mergeapks.sign.properties
file with the values of your keystore file (see mergeapks.sign.properties.example
for an example).
This file must be placed in the same directory with mergeapks.py
script, OR you can put it in your user home directory (~
).
This way, repacked apk files will be signed automatically.
If you don't want to create a dedicated keystore to sign the result apk files, you can use your default Android SDK debug keystore. In this case, the contents of the mergeapks.sign.properties
file should look like this:
sign.enabled=true
sign.keystore.file=/home/username/.android/debug.keystore
sign.keystore.password=android
sign.key.alias=androiddebugkey
sign.key.password=android
The sign.keystore.file
value in the example above is for Linux. Set the absolute path according to your OS and system user name.
By default, the resigning of the result apk files is disabled. If you do not want to sign it automatically, you don't have to do it. You can just sign the apk file manually after the conversion is completed.