Obtenez le fichier .apk Android VersionName ou VersionCode SANS installer apk

184

Comment puis-je obtenir par programme le code de version ou le nom de version de mon apk à partir du fichier AndroidManifest.xml après l'avoir téléchargé et sans l'installer.

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="xxx.xx.xxx"
    android:versionCode="1"
    android:versionName="1.1" >

Par exemple, je veux vérifier si une nouvelle version est téléchargée sur mon service IIS, après l'avoir installée sur l'appareil, s'il ne s'agit pas d'une nouvelle version, je ne veux pas l'installer.

Big.Child
la source
Vérifiez ce stackoverflow.com/a/4761689/1109425
nandeesh
BTW, j'espère que personne ne pense qu'une bonne solution consiste à télécharger le fichier, puis à vérifier s'il est nécessaire - C'est ce que suggère la première phrase. Vraisemblablement, vous voudriez plutôt du code qui s'exécute sur votre serveur et qui répond avec le numéro de version disponible.
ToolmakerSteve
./aapt d badging release.apk | grep -Po "(?<=\sversion(Code|Name)=')([0-9.]+)"
Kris Roofe
aapt coûtera environ 2.6Ml'espace disque, vous pouvez également écrire un analyseur pour analyser le fichier apk avec AXMLResourcequi coûtera environ 52k. Reportez-vous à Java parse xml avec un espace de noms non déclaré
Kris Roofe

Réponses:

417

La suite a fonctionné pour moi à partir de la ligne de commande:

aapt dump badging myapp.apk

REMARQUE: aapt.exe se trouve dans un build-toolssous-dossier du SDK. Par exemple:

<sdk_path>/build-tools/23.0.2/aapt.exe
Sileria
la source
26
Fonctionne bien, cela devrait être marqué comme la bonne réponse, pour information, se aapttrouve dans build-tools / XX dans le sdk.
Quentin Klein
8
Je l'ai trouvé sous "<sdk_path> /build-tools/21.1.2"
gmuhammad
1
À l'aide de Xamarin sur un Mac, c'était dans~/Library/Developer/Xamarin/android-sdk-macosx/build-tools/{version}
cscott530
Qu'est-ce platformBuildVersionNamequi est imprimé lorsque j'utilise cette commande et pourquoi est-elle vide?
Sevastyan Savanyuk
vous pouvez créer un fichier chauve-souris aapt dump badging %1 pausepour faire glisser et déposer n'importe quel
apk
85
final PackageManager pm = getPackageManager();
String apkName = "example.apk";
String fullPath = Environment.getExternalStorageDirectory() + "/" + apkName;        
PackageInfo info = pm.getPackageArchiveInfo(fullPath, 0);
Toast.makeText(this, "VersionCode : " + info.versionCode + ", VersionName : " + info.versionName , Toast.LENGTH_LONG).show();
Patrick Cho
la source
5
comment puis-je lire le apkcontenu sans installer l'application? @PatrickCho
talha06
2
Cela fonctionne en effet. Et vous n'avez pas besoin de l'installer. Vous avez juste un apk quelque part sur le stockage de votre appareil et fournissez le chemin d'accès au gestionnaire de paquets. Il renverra en effet les informations du paquet.
Daniel Zolnai
45

Si vous utilisez la version 2.2 et supérieure d' Android Studio, dans Android Studio, utilisez Build Analyze APKpuis sélectionnez le fichier AndroidManifest.xml.

vovahost
la source
7
OP a demandé comment l'obtenirprogrammatically
forresthopkinsa
3
Parce qu'il ne savait pas qu'un outil comme celui-ci est intégré à Android Studio. Il a également demandé sans installer l'apk .
vovahost
3
Bien sûr, il ne le savait pas, car Android Studio n'existait pas lorsque cette question a été posée. De plus, même si c'est le cas, cela ne résout pas son problème - il veut que son appareil vérifie par programme les mises à jour de son serveur IIS, comme il l'a indiqué dans la question. La réponse de Patrick Cho explique comment le faire par programme sans installer.
forresthopkinsa
10
aapt dump badging test.apk | grep "VersionName" | sed -e "s/.*versionName='//" -e "s/' .*//"

Cela répond à la question en renvoyant uniquement le numéro de version. Toutefois......

L'objectif, comme indiqué précédemment, devrait être de savoir si l'apk sur le serveur est plus récent que celui installé AVANT de tenter de le télécharger ou de l'installer. Le moyen le plus simple de le faire est d'inclure le numéro de version dans le nom de fichier de l'apk hébergé sur le serveur, par exemplemyapp_1.01.apk

Vous devrez établir le nom et le numéro de version des applications déjà installées (si elles sont installées) afin de faire la comparaison. Vous aurez besoin d'un périphérique rooté ou d'un moyen d'installer le binaire aapt et busybox s'ils ne sont pas déjà inclus dans la rom.

Ce script obtiendra la liste des applications de votre serveur et la comparera avec toutes les applications installées. Le résultat est une liste marquée pour la mise à niveau / l'installation.

#/system/bin/sh
SERVER_LIST=$(wget -qO- "http://demo.server.com/apk/" | grep 'href' | grep '\.apk' | sed 's/.*href="https://' | \
              sed 's/".*//' | grep -v '\/' | sed -E "s/%/\\\\x/g" | sed -e "s/x20/ /g" -e "s/\\\\//g")
LOCAL_LIST=$(for APP in $(pm list packages -f | sed -e 's/package://' -e 's/=.*//' | sort -u); do \
              INFO=$(echo -n $(aapt dump badging $APP | grep -e 'package: name=' -e 'application: label=')) 2>/dev/null; \
              PACKAGE=$(echo $INFO | sed "s/.*package: name='//" | sed "s/'.*$//"); \
              LABEL=$(echo $INFO | sed "s/.*application: label='//" | sed "s/'.*$//"); if [ -z "$LABEL" ]; then LABEL="$PACKAGE"; fi; \
              VERSION=$(echo $INFO | sed -e "s/.*versionName='//" -e "s/' .*//"); \
              NAME=$LABEL"_"$VERSION".apk"; echo "$NAME"; \
              done;)
OFS=$IFS; IFS=$'\t\n'
for REMOTE in $SERVER_LIST; do
    INSTALLED=0
    REMOTE_NAME=$(echo $REMOTE | sed 's/_.*//'); REMOTE_VER=$(echo $REMOTE | sed 's/^[^_]*_//g' | sed 's/[^0-9]*//g')
    for LOCAL in $LOCAL_LIST; do
        LOCAL_NAME=$(echo $LOCAL | sed 's/_.*//'); LOCAL_VER=$(echo $LOCAL | sed 's/^[^_]*_//g' | sed 's/[^0-9]*//g')
        if [ "$REMOTE_NAME" == "$LOCAL_NAME" ]; then INSTALLED=1; fi
        if [ "$REMOTE_NAME" == "$LOCAL_NAME" ] && [ ! "$REMOTE_VER" == "$LOCAL_VER" ]; then echo remote=$REMOTE ver=$REMOTE_VER local=$LOCAL ver=$LOCAL_VER; fi
    done
    if [ "$INSTALLED" == "0" ]; then echo "$REMOTE"; fi
done
IFS=$OFS

Comme quelqu'un a demandé comment le faire sans utiliser aapt. Il est également possible d'extraire des informations apk avec apktool et un peu de script. Cette méthode est plus lente et pas simple sous Android, mais fonctionnera sous Windows / Mac ou Linux tant que vous avez une configuration apktool fonctionnelle.

#!/bin/sh
APK=/path/to/your.apk
TMPDIR=/tmp/apktool
rm -f -R $TMPDIR
apktool d -q -f -s --force-manifest -o $TMPDIR $APK
APK=$(basename $APK)
VERSION=$(cat $TMPDIR/apktool.yml | grep "versionName" | sed -e "s/versionName: //")
LABEL=$(cat $TMPDIR/res/values/strings.xml | grep 'string name="title"' | sed -e 's/.*">//' -e 's/<.*//')
rm -f -R $TMPDIR
echo ${LABEL}_$(echo $V).apk

Pensez également à un dossier de dépôt sur votre serveur. Téléchargez des apks dessus et une tâche cron les renomme et les déplace vers votre dossier de mise à jour.

#!/bin/sh
# Drop Folder script for renaming APKs
# Read apk file from SRC folder and move it to TGT folder while changing filename to APKLABEL_APKVERSION.apk
# If an existing version of the APK exists in the target folder then script will remove it
# Define METHOD as "aapt" or "apktool" depending upon what is available on server 

# Variables
METHOD="aapt"
SRC="/home/user/public_html/dropfolders/apk"
TGT="/home/user/public_html/apk"
if [ -d "$SRC" ];then mkdir -p $SRC
if [ -d "$TGT" ]then mkdir -p $TGT

# Functions
get_apk_filename () {
    if [ "$1" = "" ]; then return 1; fi
    local A="$1"
    case $METHOD in
        "apktool")
            local D=/tmp/apktool
            rm -f -R $D
            apktool d -q -f -s --force-manifest -o $D $A
            local A=$(basename $A)
            local V=$(cat $D/apktool.yml | grep "versionName" | sed -e "s/versionName: //")
            local T=$(cat $D/res/values/strings.xml | grep 'string name="title"' | sed -e 's/.*">//' -e 's/<.*//')
            rm -f -R $D<commands>
            ;;
        "aapt")
            local A=$(aapt dump badging $A | grep -e "application-label:" -e "VersionName")
            local V=$(echo $A | sed -e "s/.*versionName='//" -e "s/' .*//")
            local T=$(echo $A | sed -e "s/.*application-label:'//" -e "s/'.*//")
            ;;
        esac
    echo ${T}_$(echo $V).apk
}

# Begin script
for APK in $(ls "$SRC"/*.apk); do
    APKNAME=$(get_apk_filename "$APK")
    rm -f $TGT/$(echo APKNAME | sed "s/_.*//")_*.apk
    mv "$APK" "$TGT"/$APKNAME
done
5p0ng3b0b
la source
8

Pour le moment, cela peut être fait comme suit

$ANDROID_HOME/build-tools/28.0.3/aapt dump badging /<path to>/<app name>.apk

En général, ce sera:

$ANDROID_HOME/build-tools/<version_of_build_tools>/aapt dump badging /<path to>/<app name>.apk
SergeyUr
la source
5

Je peux maintenant récupérer avec succès la version d'un fichier APK à partir de ses données XML binaires.

C'est dans cette rubrique que j'ai obtenu la clé de ma réponse (j'ai également ajouté ma version du code de Ribo): Comment analyser le fichier AndroidManifest.xml dans un package .apk

De plus, voici le code d'analyse XML que j'ai écrit, spécifiquement pour récupérer la version:

Analyse XML

/**
 * Verifies at Conductor APK path if package version if newer 
 * 
 * @return True if package found is newer, false otherwise
 */
public static boolean checkIsNewVersion(String conductorApkPath) {

    boolean newVersionExists = false;

    // Decompress found APK's Manifest XML
    // Source: /programming/2097813/how-to-parse-the-androidmanifest-xml-file-inside-an-apk-package/4761689#4761689
    try {

        if ((new File(conductorApkPath).exists())) {

            JarFile jf = new JarFile(conductorApkPath);
            InputStream is = jf.getInputStream(jf.getEntry("AndroidManifest.xml"));
            byte[] xml = new byte[is.available()];
            int br = is.read(xml);

            //Tree tr = TrunkFactory.newTree();
            String xmlResult = SystemPackageTools.decompressXML(xml);
            //prt("XML\n"+tr.list());

            if (!xmlResult.isEmpty()) {

                InputStream in = new ByteArrayInputStream(xmlResult.getBytes());

                // Source: http://developer.android.com/training/basics/network-ops/xml.html
                XmlPullParser parser = Xml.newPullParser();
                parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false);

                parser.setInput(in, null);
                parser.nextTag();

                String name = parser.getName();
                if (name.equalsIgnoreCase("Manifest")) {

                    String pakVersion = parser.getAttributeValue(null, "versionName");
                            //NOTE: This is specific to my project. Replace with whatever is relevant on your side to fetch your project's version
                    String curVersion = SharedData.getPlayerVersion();

                    int isNewer = SystemPackageTools.compareVersions(pakVersion, curVersion); 

                    newVersionExists = (isNewer == 1); 
                }

            }
        }

    } catch (Exception ex) {
        android.util.Log.e(TAG, "getIntents, ex: "+ex);
        ex.printStackTrace();
    }

    return newVersionExists;
}

Comparaison de version (vu comme SystemPackageTools.compareVersions dans l'extrait de code précédent) REMARQUE: Ce code est inspiré de la rubrique suivante: Moyen efficace de comparer les chaînes de version en Java

/**
 * Compare 2 version strings and tell if the first is higher, equal or lower
 * Source: /programming/6701948/efficient-way-to-compare-version-strings-in-java
 * 
 * @param ver1 Reference version
 * @param ver2 Comparison version
 * 
 * @return 1 if ver1 is higher, 0 if equal, -1 if ver1 is lower
 */
public static final int compareVersions(String ver1, String ver2) {

    String[] vals1 = ver1.split("\\.");
    String[] vals2 = ver2.split("\\.");
    int i=0;
    while(i<vals1.length&&i<vals2.length&&vals1[i].equals(vals2[i])) {
      i++;
    }

    if (i<vals1.length&&i<vals2.length) {
        int diff = Integer.valueOf(vals1[i]).compareTo(Integer.valueOf(vals2[i]));
        return diff<0?-1:diff==0?0:1;
    }

    return vals1.length<vals2.length?-1:vals1.length==vals2.length?0:1;
}

J'espère que ça aide.

Mathieu
la source
Avez-vous mis à niveau vers le nouveau Gradle ou Android Studio? Cela fonctionne-t-il toujours? Le code Robio ne fonctionne plus pour décompresser le XML pour moi.
GR Envoy
4

Pour le scénario de mise à niveau, une approche alternative pourrait être de disposer d'un service Web fournissant le numéro de version actuelle et de le vérifier au lieu de télécharger l'intégralité de l'apk juste pour vérifier sa version. Cela économiserait de la bande passante, serait un peu plus performant (beaucoup plus rapide à télécharger qu'un apk si l'ensemble de l'apk n'est pas nécessaire la plupart du temps) et beaucoup plus simple à implémenter.

Dans la forme la plus simple, vous pourriez avoir un simple fichier texte sur votre serveur ... http://some-place.com/current-app-version.txt

À l'intérieur de ce fichier texte, il y a quelque chose comme

3.1.4

puis téléchargez ce fichier et vérifiez la version actuellement installée.

Construire une solution plus avancée à cela serait d'implémenter un service Web approprié et d'avoir un appel d'API au lancement qui pourrait renvoyer des json, c'est http://api.some-place.com/versionCheck-à- dire :

{
    "current_version": "3.1.4"
}
Grego
la source
Vous êtes sur la bonne voie - il ne faut pas télécharger le fichier, juste pour obtenir sa version. Je veux juste mentionner que je ne recommanderais pas de créer le fichier texte MANUELLEMENT - trop facile pour qu'il ne corresponde pas au fichier apk réel. Au lieu de cela, écrivez un script (qui utilise l'une des autres réponses) pour regarder à l'intérieur de l'apk pour voir quelle est sa version, en le stockant dans un fichier ou une base de données, à récupérer par les moyens dont vous discutez.
ToolmakerSteve
Oui, dans ce scénario, le cas n'est pas qu'il doit correspondre du tout à l'APK, mais simplement que le dernier numéro de version est accessible au public.
grego
1
    EditText ET1 = (EditText) findViewById(R.id.editText1);

    PackageInfo pinfo;
    try {
        pinfo = getPackageManager().getPackageInfo(getPackageName(), 0);
        String versionName = pinfo.versionName;
        ET1.setText(versionName);
        //ET2.setText(versionNumber);
    } catch (NameNotFoundException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
Wayne Len
la source
10
Ce n'est pas une réponse à la question. C'est du code qui, dans une application en cours d'exécution, obtient des informations sur la version et les affiche. La question est de savoir comment obtenir ces informations à partir d'un fichier APK, sans installer l'application .
ToolmakerSteve
Ceci est uniquement pour une application déjà installée et non pour un fichier .apk.
Rhusfer