Ottieni il file .apk Android VersionName o VersionCode SENZA installare apk


184

Come posso ottenere a livello di codice ottenere il codice della versione o il nome della versione del mio apk dal file AndroidManifest.xml dopo averlo scaricato e senza installarlo.

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

Ad esempio, voglio verificare se una nuova versione è caricata sul mio servizio IIS, dopo averla installata sul dispositivo, se non è una nuova versione non voglio installarla.



A proposito, spero che nessuno pensi che una buona soluzione sia scaricare il file e quindi verificare se è necessario - Qual è ciò che suggerisce la prima frase. Presumibilmente si vorrebbe invece il codice che viene eseguito sul proprio server e risponde con il numero di versione disponibile.
ToolmakerSteve

./aapt d badging release.apk | grep -Po "(?<=\sversion(Code|Name)=')([0-9.]+)"
Kris Roofe,

aapt costerà circa lo 2.6Mspazio su disco, puoi anche scrivere un parser per analizzare il file apk con il AXMLResourcequale costerà solo 52k. Fare riferimento a XML di analisi Java con spazio dei nomi non dichiarato
Kris Roofe,

Risposte:


417

Di seguito ha funzionato per me dalla riga di comando:

aapt dump badging myapp.apk

NOTA: aapt.exe si trova in a build-tools sottocartella di SDK. Per esempio:

<sdk_path>/build-tools/23.0.2/aapt.exe

26
Funziona bene, questo dovrebbe essere contrassegnato come la risposta corretta, per informazioni, aaptè in build-tools / XX in sdk.
Quentin Klein,

8
L'ho trovato in "<sdk_path> /build-tools/21.1.2"
gmuhammad

1
Usando Xamarin su un Mac, era in~/Library/Developer/Xamarin/android-sdk-macosx/build-tools/{version}
cscott530

Cosa viene platformBuildVersionNamestampato quando utilizzo questo comando e perché è vuoto?
Sevastyan Savanyuk,

puoi creare un file bat aapt dump badging %1 pauseper trascinare e rilasciare qualsiasi apk
user924

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();

5
come posso leggere il apkcontenuto senza installare l'applicazione? @PatrickCho
talha06

2
Questo funziona davvero. E non è necessario installarlo. Hai solo un apk da qualche parte nella memoria del tuo dispositivo e fornisci il percorso al gestore dei pacchetti. Restituirà effettivamente le informazioni sul pacchetto.
Daniel Zolnai,

45

Se stai utilizzando la versione 2.2 e successive di Android Studio, in Android Studio usa Build Analyze APKquindi seleziona il file AndroidManifest.xml.


7
OP ha chiesto come ottenerloprogrammatically
forresthopkinsa,

3
Perché non sapeva che uno strumento come questo è integrato in Android Studio. Ha anche chiesto senza installare l'apk .
vovahost,

3
Ovviamente non lo sapeva, perché Android Studio non esisteva al momento della domanda. Inoltre, anche se lo facesse, questo non risolve il suo problema: vuole che il suo dispositivo controlli programmaticamente gli aggiornamenti dal suo server IIS, come ha affermato nella domanda. La risposta di Patrick Cho spiega come farlo a livello di codice senza installarlo.
forresthopkinsa,

10
aapt dump badging test.apk | grep "VersionName" | sed -e "s/.*versionName='//" -e "s/' .*//"

Ciò risponde alla domanda restituendo di conseguenza solo il numero di versione. Però......

L'obiettivo come precedentemente indicato dovrebbe essere quello di scoprire se l'apk sul server è più recente di quello installato PRIMA di tentare di scaricarlo o installarlo. Il modo più semplice per farlo è includere il numero di versione nel nome del file dell'apk ospitato sul server, ad esmyapp_1.01.apk

Per effettuare il confronto, dovrai stabilire il nome e il numero di versione delle app già installate (se installate). Avrai bisogno di un dispositivo rootato o di un mezzo per installare il file binario e la scatola di occupato se non sono già inclusi nella rom.

Questo script otterrà l'elenco delle app dal tuo server e lo confronterà con tutte le app installate. Il risultato è un elenco contrassegnato per l'aggiornamento / l'installazione.

#/system/bin/sh
SERVER_LIST=$(wget -qO- "http://demo.server.com/apk/" | grep 'href' | grep '\.apk' | sed 's/.*href="https://stackoverflow.com//' | \
              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

Come qualcuno ha chiesto come farlo senza usare aapt. È anche possibile estrarre informazioni apk con apktool e un po 'di script. In questo modo è più lento e non semplice in Android, ma funzionerà su Windows / Mac o Linux fino a quando avrai installato apktool funzionante.

#!/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

Considera anche una cartella di rilascio sul tuo server. Carica gli apk su di esso e un'attività cron rinomina e li sposta nella cartella di aggiornamento.

#!/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

8

Al momento, questo può essere fatto come segue

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

In generale, sarà:

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

5

Ora posso recuperare con successo la versione di un file APK dai suoi dati XML binari.

In questo argomento ho trovato la chiave della mia risposta (ho anche aggiunto la mia versione del codice di Ribo): come analizzare il file AndroidManifest.xml all'interno di un pacchetto .apk

Inoltre, ecco il codice di analisi XML che ho scritto, in particolare per recuperare la versione:

Analisi 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;
}

Confronto delle versioni (visto come SystemPackageTools.compareVersions nel frammento precedente) NOTA: questo codice si ispira al seguente argomento: Modo efficiente per confrontare le stringhe di versione in 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;
}

Spero che aiuti.


Hai eseguito l'upgrade al nuovo Gradle o Android Studio? Funziona ancora? Il codice Robio non funziona più per decomprimere l'XML per me.
Envoy GR

4

Per lo scenario di aggiornamento, in particolare, un approccio alternativo potrebbe essere quello di avere un servizio Web che fornisca il numero di versione corrente e verificare che invece di scaricare l'intero apk solo per verificarne la versione. Si risparmierebbe un po 'di larghezza di banda, sarebbe un po' più performante (molto più veloce da scaricare di un apk se l'intero apk non è necessario per la maggior parte del tempo) e molto più semplice da implementare.

Nella forma più semplice potresti avere un semplice file di testo sul tuo server ... http://some-place.com/current-app-version.txt

All'interno di quel file di testo c'è qualcosa di simile

3.1.4

e quindi scaricare quel file e verificare la versione attualmente installata.

Costruire una soluzione più avanzata a questo sarebbe implementare un servizio web adeguato e avere una chiamata API al lancio che potrebbe restituire un po 'di json, ad esempio http://api.some-place.com/versionCheck:

{
    "current_version": "3.1.4"
}

Sei sulla strada giusta - non scaricare file, solo per ottenere la sua versione. Voglio solo menzionare che non consiglierei di creare il file di testo MANUALMENTE - troppo facile per farlo non corrispondere al file apk effettivo. Invece, scrivi uno script (che utilizza una delle altre risposte) per guardare all'interno dell'apk per vedere qual è la sua versione, memorizzandola nel file o nel database, per essere recuperata con i mezzi che discuti.
ToolmakerSteve

Sì, in questo scenario il caso non è che deve corrispondere affatto all'APK, solo che l'ultimo numero di versione è da qualche parte accessibile pubblicamente.
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();
    }

10
Questa non è una risposta alla domanda. Questo è il codice che, all'interno di un'app in esecuzione, ottiene alcune informazioni sulla versione e le visualizza. La domanda è come ottenere queste informazioni da un file APK, senza installare l'app .
ToolmakerSteve

Questo è solo per l'applicazione già installata, non per un file .apk.
Rhusfer,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.