Отримайте Android .apk файл VersionName або VersionCode БЕЗ встановлення apk


184

Як я можу програмно отримати код версії або назву версії свого 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 після встановлення її на пристрої, якщо це не нова версія, я не хочу її встановлювати.


перевірити це stackoverflow.com/a/4761689/1109425
nandeesh

BTW, я сподіваюся, що ніхто не думає, що вдалим рішенням є завантаження файлу, а потім перевірити, чи потрібен він - Що саме пропонує перше речення. Імовірно, ви б хотіли, щоб код, який працює на вашому сервері , відповідав номеру версії, який доступний.
ToolmakerSteve

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

aapt коштуватиме близько 2.6Mмісця на диску, ви також можете написати аналізатор, щоб розібрати файл apk, з AXMLResourceяким просто обійдеться 52k. Зверніться до розбору Java xml з недекларованим простором імен
Kris Roofe

Відповіді:


417

Наступні працювали для мене з командного рядка:

aapt dump badging myapp.apk

ПРИМІТКА: aapt.exe знаходиться в build-toolsпідпапці SDK. Наприклад:

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

26
Працює чудово, це має бути позначено як правильна відповідь, для інформації, aaptзнаходиться у build-tools / XX у sdk.
Квентін Кляйн

8
Я знайшов це під "<sdk_path> /build-tools/21.1.2"
gmuhammad

1
Використання Xamarin на Mac, це було в~/Library/Developer/Xamarin/android-sdk-macosx/build-tools/{version}
cscott530

Що це platformBuildVersionNameдрукується, коли я використовую цю команду і чому вона порожня?
Севастьян Саванюк

ви можете створити bat-файл, aapt dump badging %1 pauseщоб перетягнути будь-який 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
як я можу читати apkвміст, не встановлюючи додаток? @PatrickCho
talha06

2
Це дійсно працює. І вам не потрібно його встановлювати. Ви просто маєте apk десь на пам’яті свого пристрою та надаєте шлях до нього менеджеру пакунків. Він дійсно поверне інформацію про пакет.
Даніель Золнай

45

Якщо ви використовуєте версію 2.2 і вище в Android Studio , то використовується Android Studio Build Analyze APKвиберіть AndroidManifest.xml файл.


7
ОП запитали, як його отриматиprogrammatically
forresthopkinsa

3
Тому що він не знав, що такий інструмент вбудований в Android Studio. Він також запитав, не встановлюючи apk .
vovahost

3
Звичайно, він цього не знав, оскільки Android Studio не існувало, коли було задано це питання. Крім того, навіть якщо це сталося, це не вирішує його проблеми - він хоче, щоб його пристрій програмно перевіряв наявність оновлень на своєму IIS-сервері, як він заявив у питанні. Відповідь Патріка Чо пояснює, як це зробити програмно, не встановлюючи.
forresthopkinsa

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

Це відповідає на питання, повертаючи лише номер версії в результаті. Однак ......

Мета, як було зазначено раніше, має бути з'ясувати, чи apk на сервері новіший, ніж встановлений ДО ПІД намагання завантажити або встановити його. Найпростіший спосіб зробити це - включити номер версії у назві файлу apk, розміщеного на сервері, наприкладmyapp_1.01.apk

Вам потрібно буде встановити ім'я та номер версії вже встановлених додатків (якщо вони встановлені) для порівняння. Вам знадобиться вкорінений пристрій або засіб встановлення aapt бінарних та зайнятих ящиків, якщо вони вже не включені до програми.

Цей скрипт отримає список програм із вашого сервера та порівняє його з будь-якими встановленими програмами. Результатом є список, позначений для оновлення / встановлення.

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

Як хтось запитав, як це зробити, не використовуючи 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

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

5

Зараз я можу успішно отримати версію файлу APK з його двійкових XML-даних.

У цій темі я отримав ключ до своєї відповіді (я також додав свою версію коду Рібо): Як розібрати файл 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'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;
}

Порівняння версій (розглядається як SystemPackageTools.compareVersions у попередньому фрагменті) ПРИМІТКА. Цей код заснований на наступній темі: Ефективний спосіб порівняння рядків версій на 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;
}

Я сподіваюся, що це допомагає.


Ви перейшли до нового Gradle чи Android Studio? Це все ще працює? Код Robio більше не працює для розпакування XML.
GR-посланник

4

Для сценарію оновлення конкретно альтернативним підходом може бути веб-сервіс, який доставляє номер поточної версії та перевіряє, чи замість завантаження всього 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"
}

Ви на правильному шляху - не слід завантажувати файл, а лише отримати його версію. Я просто хочу зазначити, що я б не рекомендував створювати текстовий файл Вручну - занадто просто, щоб він не відповідав фактичному файлу apk. Натомість напишіть скрипт (на який використовується одна з інших відповідей), щоб заглянути все до apk, щоб побачити, яка його версія, зберігаючи його у файлі чи базі даних, щоб бути отриманим засобами, які ви обговорюєте.
ToolmakerSteve

Так, у цьому випадку справа не в тому, що він взагалі повинен відповідати APK, лише те, що номер останньої версії десь є загальнодоступним.
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
Це не відповідь на запитання. Це код, який всередині запущеної програми отримує інформацію про версію та відображає її. Питання полягає в тому, як отримати цю інформацію з файлу APK, не встановлюючи додаток .
ToolmakerSteve

Це лише для вже встановленої програми, а не для файлу .apk.
Rhusfer
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.