Holen Sie sich Android .apk-Datei VersionName oder VersionCode ohne Installation von apk

183

Wie kann ich programmgesteuert den Versionscode oder den Versionsnamen meiner apk aus der Datei AndroidManifest.xml abrufen, nachdem ich sie heruntergeladen und ohne sie installiert habe?

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

Ich möchte beispielsweise überprüfen, ob eine neue Version in meinen IIS-Dienst hochgeladen wurde, nachdem ich sie auf dem Gerät installiert habe. Wenn es sich nicht um eine neue Version handelt, möchte ich sie nicht installieren.

Big.Child
quelle
Überprüfen Sie diese stackoverflow.com/a/4761689/1109425
Nandeesh
Übrigens, ich hoffe, niemand glaubt, dass eine gute Lösung darin besteht, die Datei herunterzuladen und dann zu prüfen, ob sie benötigt wird - was der erste Satz vorschlägt. Vermutlich möchten Sie stattdessen Code, der auf Ihrem Server ausgeführt wird und mit der verfügbaren Versionsnummer antwortet.
ToolmakerSteve
./aapt d badging release.apk | grep -Po "(?<=\sversion(Code|Name)=')([0-9.]+)"
Kris Roofe
aapt kostet ungefähr 2.6MSpeicherplatz, Sie können auch einen Parser schreiben, um die apk-Datei zu analysieren, mit AXMLResourceder nur ungefähr Speicherplatz kostet 52k. Siehe Java Parse XML mit nicht deklarierten Namespace
Kris Roofe

Antworten:

416

Folgendes hat für mich über die Kommandozeile funktioniert:

aapt dump badging myapp.apk

HINWEIS: aapt.exe befindet sich in einem build-toolsUnterordner des SDK. Beispielsweise:

<sdk_path>/build-tools/23.0.2/aapt.exe
Sileria
quelle
26
Funktioniert einwandfrei, dies sollte markiert werden, da die richtige Antwort zur Information aaptin Build-Tools / XX im SDK enthalten ist.
Quentin Klein
8
Ich fand es unter "<sdk_path> /build-tools/21.1.2"
gmuhammad
1
Mit Xamarin auf einem Mac war es in~/Library/Developer/Xamarin/android-sdk-macosx/build-tools/{version}
cscott530
Was ist platformBuildVersionName gedruckt, wenn ich diesen Befehl verwende, und warum ist er leer?
Sevastyan Savanyuk
Sie können Bat-Datei erstellen aapt dump badging %1 pause, um jede apk zu ziehen und fallen zu lassen
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();
Patrick Cho
quelle
5
Wie kann ich apkInhalte lesen , ohne die Anwendung zu installieren ? @ PatrickCho
talha06
2
Das funktioniert tatsächlich. Und Sie müssen es nicht installieren. Sie haben nur irgendwo im Speicher Ihres Geräts eine Apk und geben den Pfad dazu an den Paketmanager weiter. Es wird in der Tat die Paketinformationen zurückgeben.
Daniel Zolnai
45

Wenn Sie Version 2.2 und höher von Android Studio verwenden, verwenden Sie in Android Studio Build Analyze APKund wählen Sie dann die Datei AndroidManifest.xml aus.

vovahost
quelle
7
OP fragte, wie man es bekommtprogrammatically
forresthopkinsa
3
Weil er nicht wusste, dass ein solches Tool in Android Studio erstellt wurde. Er fragte auch ohne die apk zu installieren .
Vovahost
3
Natürlich wusste er das nicht, weil Android Studio nicht existierte, als diese Frage gestellt wurde. Selbst wenn dies der Fall ist, löst dies sein Problem nicht. Er möchte, dass sein Gerät programmgesteuert nach Updates von seinem IIS-Server sucht , wie in der Frage angegeben. Die Antwort von Patrick Cho erklärt, wie man es programmgesteuert ohne Installation macht.
Forresthopkinsa
10
aapt dump badging test.apk | grep "VersionName" | sed -e "s/.*versionName='//" -e "s/' .*//"

Dies beantwortet die Frage, indem nur die Versionsnummer zurückgegeben wird. Jedoch......

Wie bereits erwähnt, sollte das Ziel darin bestehen, herauszufinden, ob die apk auf dem Server neuer ist als die installierte, bevor Sie versuchen, sie herunterzuladen oder zu installieren. Der einfachste Weg, dies zu tun, besteht darin, die Versionsnummer in den Dateinamen der auf dem Server gehosteten apk aufzunehmen, zmyapp_1.01.apk

Sie müssen den Namen und die Versionsnummer der bereits installierten Apps (falls installiert) festlegen, um den Vergleich durchführen zu können. Sie benötigen ein gerootetes Gerät oder ein Mittel zum Installieren der aapt-Binärdatei und der Busybox, wenn diese nicht bereits im ROM enthalten sind.

Dieses Skript ruft die Liste der Apps von Ihrem Server ab und vergleicht sie mit allen installierten Apps. Das Ergebnis ist eine Liste, die für das Upgrade / die Installation markiert ist.

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

Als jemand fragte, wie es geht, ohne aapt zu verwenden. Es ist auch möglich, apk-Informationen mit apktool und ein bisschen Scripting zu extrahieren. Dieser Weg ist langsamer und nicht einfach in Android, funktioniert aber unter Windows / Mac oder Linux, solange Sie Apktool-Setup arbeiten.

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

Betrachten Sie auch einen Ablageordner auf Ihrem Server. Laden Sie Apks hoch und eine Cron-Task benennt sie um und verschiebt sie in Ihren Update-Ordner.

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

Im Moment kann dies wie folgt erfolgen

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

Im Allgemeinen wird es sein:

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

Ich kann jetzt erfolgreich die Version einer APK-Datei aus ihren binären XML-Daten abrufen.

In diesem Thema habe ich den Schlüssel zu meiner Antwort erhalten (ich habe auch meine Version von Ribos Code hinzugefügt): So analysieren Sie die Datei AndroidManifest.xml in einem APK-Paket

Außerdem ist hier der XML-Parsing-Code, den ich speziell zum Abrufen der Version geschrieben habe:

XML-Analyse

/**
 * 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;
}

Versionsvergleich (im vorherigen Snippet als SystemPackageTools.compareVersions angesehen) HINWEIS: Dieser Code basiert auf dem folgenden Thema: Effiziente Methode zum Vergleichen von Versionszeichenfolgen 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;
}

Ich hoffe das hilft.

Mathieu
quelle
Haben Sie ein Upgrade auf das neue Gradle oder Android Studio durchgeführt? Funktioniert das noch Der Robio-Code funktioniert nicht mehr, um das XML für mich zu dekomprimieren.
GR Envoy
4

Für das Upgrade-Szenario könnte ein alternativer Ansatz darin bestehen, einen Webdienst zu haben, der die aktuelle Versionsnummer liefert, und dies zu überprüfen, anstatt die gesamte apk herunterzuladen, nur um die Version zu überprüfen. Es würde etwas Bandbreite sparen, etwas leistungsfähiger sein (viel schneller als eine Apk herunterzuladen, wenn die gesamte Apk die meiste Zeit nicht benötigt wird) und viel einfacher zu implementieren.

In der einfachsten Form könnten Sie eine einfache Textdatei auf Ihrem Server haben ... http://some-place.com/current-app-version.txt

In dieser Textdatei befindet sich so etwas wie

3.1.4

Laden Sie dann diese Datei herunter und vergleichen Sie sie mit der aktuell installierten Version.

Eine fortschrittlichere Lösung dafür zu entwickeln, würde darin bestehen, einen ordnungsgemäßen Webdienst zu implementieren und beim Start einen API-Aufruf zu erhalten, der etwas json zurückgeben könnte, dh http://api.some-place.com/versionCheck:

{
    "current_version": "3.1.4"
}
Grego
quelle
Sie sind auf dem richtigen Weg - sollten keine Datei herunterladen, nur um ihre Version zu erhalten. Ich möchte nur erwähnen, dass ich nicht empfehlen würde, die Textdatei MANUELL zu erstellen - zu einfach, um sie nicht mit der tatsächlichen APK-Datei übereinstimmen zu lassen. Schreiben Sie stattdessen ein Skript (das eine der anderen Antworten verwendet), um in die apk zu schauen, um zu sehen, wie ihre Version ist, und speichern Sie diese in einer Datei oder Datenbank, um sie mit den von Ihnen besprochenen Mitteln abzurufen.
ToolmakerSteve
Ja, in diesem Szenario ist es nicht so, dass es überhaupt mit der APK übereinstimmen muss, sondern dass die neueste Versionsnummer irgendwo öffentlich zugänglich ist.
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
quelle
10
Dies ist keine Antwort auf die Frage. Dies ist Code, der in einer laufenden App einige Versionsinformationen abruft und diese anzeigt. Die Frage ist, wie diese Informationen aus einer APK-Datei abgerufen werden können, ohne die App zu installieren .
ToolmakerSteve
Dies gilt nur für bereits installierte Anwendungen, nicht für eine APK-Datei.
Rhusfer