Obtenga el archivo .apk de Android VersionName o VersionCode SIN instalar apk


184

¿Cómo puedo obtener programáticamente el código de versión o el nombre de la versión de mi apk del archivo AndroidManifest.xml después de descargarlo y sin instalarlo?

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

Por ejemplo, quiero verificar si una nueva versión está cargada en mi servicio IIS, después de instalarla en el dispositivo, si no es una nueva versión, no quiero instalarla.



Por cierto, espero que nadie piense que una buena solución es descargar el archivo y luego verificar si es necesario, que es lo que sugiere la primera oración. Presumiblemente, en su lugar, querría un código que se ejecute en su servidor y responda con el número de versión disponible.
ToolmakerSteve

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

aapt le costará 2.6Mespacio en el disco, también puede escribir un analizador para analizar el archivo apk con el AXMLResourceque solo le costará 52k. Consulte Java parse xml con espacio de nombres no declarado
Kris Roofe

Respuestas:


417

Lo siguiente funcionó para mí desde la línea de comando:

aapt dump badging myapp.apk

NOTA: aapt.exe se encuentra en una build-toolssubcarpeta del SDK. Por ejemplo:

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

26
Funciona bien, esto debe marcarse como la respuesta correcta, para información, aaptestá en build-tools / XX en el sdk.
Quentin Klein

8
Lo encontré en "<sdk_path> /build-tools/21.1.2"
gmuhammad

1
Usando Xamarin en una Mac, estaba en~/Library/Developer/Xamarin/android-sdk-macosx/build-tools/{version}
cscott530

¿Qué se platformBuildVersionNameimprime cuando uso este comando y por qué está vacío?
Sevastyan Savanyuk

puedes crear un archivo bat aapt dump badging %1 pausepara arrastrar y soltar cualquier 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();

55
¿Cómo puedo leer apkcontenido sin instalar la aplicación? @PatrickCho
talha06

2
Esto de hecho funciona. Y no necesitas instalarlo. Solo tiene un apk en algún lugar del almacenamiento de su dispositivo y proporciona la ruta al administrador de paquetes. De hecho, devolverá la información del paquete.
Daniel Zolnai

45

Si está utilizando la versión 2.2 y superior de Android Studio , use Android Studio Build Analyze APKluego seleccione el archivo AndroidManifest.xml.


77
OP preguntó cómo conseguirloprogrammatically
forresthopkinsa

3
Porque no sabía que una herramienta como esta está construida en Android Studio. También preguntó sin instalar el apk .
vovahost

3
Por supuesto, él no lo sabía, porque Android Studio no existía cuando se hizo esta pregunta. Además, incluso si lo hiciera, esto no resuelve su problema: quiere que su dispositivo verifique programáticamente las actualizaciones de su servidor IIS, como dijo en la pregunta. La respuesta de Patrick Cho explica cómo hacerlo mediante programación sin instalar.
forresthopkinsa

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

Esto responde a la pregunta devolviendo solo el número de versión como resultado. Sin embargo......

El objetivo, como se indicó anteriormente, debería ser averiguar si la apk en el servidor es más nueva que la instalada ANTES de intentar descargarla o instalarla. La forma más fácil de hacerlo es incluir el número de versión en el nombre de archivo de la apk alojada en el servidor, por ejemplomyapp_1.01.apk

Deberá establecer el nombre y el número de versión de las aplicaciones ya instaladas (si está instalada) para poder realizar la comparación. Necesitará un dispositivo rooteado o un medio para instalar el binario aapt y busybox si aún no están incluidos en la rom.

Este script obtendrá la lista de aplicaciones de su servidor y se comparará con cualquier aplicación instalada. El resultado es una lista marcada para actualización / instalación.

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

Como alguien preguntó cómo hacerlo sin usar aapt. También es posible extraer información de apk con apktool y un poco de secuencias de comandos. Esta forma es más lenta y no es simple en Android, pero funcionará en Windows / Mac o Linux siempre que tenga la configuración de apktool en funcionamiento.

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

Considere también una carpeta de colocación en su servidor. Sube apks y una tarea cron cambia el nombre y los mueve a tu carpeta de actualización.

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

Por el momento, esto se puede hacer de la siguiente manera

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

En general, será:

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

5

Ahora puedo recuperar con éxito la versión de un archivo APK de sus datos XML binarios.

Este tema es donde obtuve la clave de mi respuesta (también agregué mi versión del código de Ribo): Cómo analizar el archivo AndroidManifest.xml dentro de un paquete .apk

Además, aquí está el código de análisis XML que escribí, específicamente para obtener la versión:

Análisis 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;
}

Comparación de versiones (visto como SystemPackageTools.compareVersions en fragmento anterior) NOTA: Este código está inspirado en el siguiente tema: Forma eficiente de comparar cadenas de versiones 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;
}

Espero que esto ayude.


¿Te has actualizado al nuevo Gradle o Android Studio? ¿Esto todavía funciona? El código Robio ya no funciona para descomprimir el XML.
GR Enviado

4

Para el escenario de actualización, específicamente, un enfoque alternativo podría ser tener un servicio web que entregue el número de versión actual y verificar eso en lugar de descargar el apk completo solo para verificar su versión. Ahorraría algo de ancho de banda, sería un poco más eficiente (mucho más rápido de descargar que un apk si no se necesita todo el apk la mayor parte del tiempo) y mucho más simple de implementar.

En la forma más simple, podría tener un archivo de texto simple en su servidor ... http://some-place.com/current-app-version.txt

Dentro de ese archivo de texto tiene algo como

3.1.4

y luego descargue ese archivo y verifique la versión instalada actualmente.

Construir una solución más avanzada para eso sería implementar un servicio web adecuado y tener una llamada API en el lanzamiento que podría devolver algunos json, es decir http://api.some-place.com/versionCheck:

{
    "current_version": "3.1.4"
}

Estás en el camino correcto: no debes descargar el archivo, solo para obtener su versión. Solo quiero mencionar que no recomendaría crear el archivo de texto MANUALMENTE, demasiado fácil para que no coincida con el archivo apk real. En su lugar, escriba un script (que use una de las otras respuestas) para mirar dentro del apk y ver cuál es su versión, almacenándola en un archivo o base de datos, para recuperarla por los medios que discute.
ToolmakerSteve

Sí, en este escenario, el caso no es que deba coincidir con el APK, solo que el último número de versión esté en algún lugar accesible al público.
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
Esta no es una respuesta a la pregunta. Este es un código que, dentro de una aplicación en ejecución, obtiene información de la versión y la muestra. La pregunta es cómo obtener esta información de un archivo APK, sin instalar la aplicación .
ToolmakerSteve

Esto es solo para aplicaciones ya instaladas, no para un archivo .apk.
Rhusfer
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.