Получить Android.apk файл VersionName или VersionCode БЕЗ установки apk

Как программно получить код версии или название версии моего apk из файла AndroidManifest.xml после его загрузки и без установки.

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

Например, я хочу проверить, загружена ли новая версия в мою службу IIS, после установки на устройство, если это не новая версия, я не хочу ее устанавливать.

Ответ 1

После меня работала из командной строки:

aapt dump badging myapp.apk

ПРИМЕЧАНИЕ. Файл aapt.exe находится в подпапке build-tools SDK. Например:

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

Ответ 2

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

Ответ 3

Если вы используете версию 2.2 и выше из Android Studio, то в Android Studio используйте Build Analyze APK, затем выберите файл AndroidManifest.xml.

Ответ 4

Теперь я могу успешно получить версию файла APK из своих двоичных XML-данных.

В этом разделе я получил ключ к моему ответу (я также добавил свою версию кода Ribo): Как проанализировать файл AndroidManifest.xml внутри пакета .apk

Кроме того, здесь код XML-анализа, который я написал, специально для получения версии:

Разбор 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 Manifest XML
    // Source: https://stackoverflow.com/questions/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 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;
}

Сравнение версий (рассматривается как SystemPackageTools.compareVersions в предыдущем фрагменте) ПРИМЕЧАНИЕ. Этот код основан на следующем вопросе: Эффективный способ сравнения строк версии в Java.

/**
 * Compare 2 version strings and tell if the first is higher, equal or lower
 * Source: /questions/20495/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;
}

Надеюсь, это поможет.

Ответ 5

Для сценария обновления конкретный альтернативный подход может заключаться в том, чтобы иметь веб-службу, которая доставляет текущий номер версии, и проверить, чтобы вместо загрузки всего apk просто проверить его версию. Это позволит сэкономить некоторую полосу пропускания, быть немного более результативной (гораздо быстрее загружать, чем apk, если весь apk не требуется большую часть времени) и намного проще реализовать.

В простейшей форме у вас может быть простой текстовый файл на вашем сервере... http://some-place.com/current-app-version.txt

Внутри этого текстового файла есть что-то вроде

3.1.4

а затем загрузить этот файл и проверить его на установленную версию.

Создание более продвинутого решения для этого было бы для внедрения надлежащего веб-сервиса и при запуске вызова api, который мог бы вернуть некоторый json, т.е. http://api.some-place.com/versionCheck:

{
    "current_version": "3.1.4"
}

Ответ 6

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

Это отвечает на вопрос, возвращая в результате только номер версии. Тем не мение......

Цель, как было указано выше, должна состоять в том, чтобы выяснить, является ли apk на сервере более новым, чем установленный, ПЕРЕД попыткой загрузить или установить его. Самый простой способ сделать это - включить номер версии в имя файла apk, размещенного на сервере, например, myapp_1.01.apk

Вам нужно будет установить имя и номер версии уже установленных приложений (если они установлены), чтобы сделать сравнение. Вам потребуется рутованное устройство или средство установки двоичного файла aapt и busybox, если они еще не включены в rom.

Этот скрипт получит список приложений с вашего сервера и сравнит их с любыми установленными приложениями. Результатом является список, помеченный для обновления/установки.

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

Как кто-то спросил, как это сделать, не используя aapt. Также можно извлечь информацию об apk с помощью apktool и небольшого количества сценариев. Этот способ медленнее и не прост в Android, но будет работать на Windows/Mac или Linux, если у вас есть рабочая установка apktool.

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

Также рассмотрите дроп-папку на вашем сервере. Загрузите в него apks, а задача cron переименует и переместит их в папку обновлений.

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

Ответ 7

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

Ответ 8

На данный момент это можно сделать следующим образом

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

В общем, это будет:

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