APK를 설치하지 않고 Android .apk 파일 VersionName 또는 VersionCode 가져 오기 AndroidManifest.xml 파일을 다운로드 한 후

AndroidManifest.xml 파일을 다운로드 한 후 설치하지 않고 프로그래밍 방식으로 내 APK의 버전 코드 또는 버전 이름을 어떻게 얻을 수 있습니까?

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

예를 들어 IIS 서비스에 새 버전이 업로드되어 있는지 확인하고 장치에 설치 한 후 새 버전이 아닌 경우 설치하고 싶지 않습니다.



답변

다음은 명령 줄에서 나를 위해 일했습니다.

aapt dump badging myapp.apk

참고 : aapt.exe는 build-toolsSDK 의 하위 폴더에 있습니다. 예를 들면 다음과 같습니다.

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

답변

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

답변

Android Studio 버전 2.2 이상 을 사용하는 경우 Android Studio 에서 Build Analyze APK를 사용한 다음 AndroidManifest.xml 파일을 선택하십시오.


답변

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

결과적으로 버전 번호 만 반환하여 질문에 대답합니다. 하나……

이전에 언급 한 목표는 서버에있는 APK가 다운로드 또는 설치를 시도하기 전에 설치된 APK보다 최신 버전인지 확인하는 것입니다. 가장 쉬운 방법은 서버에서 호스팅되는 APK의 파일 이름에 버전 번호를 포함시키는 것입니다.myapp_1.01.apk

비교하려면 이미 설치된 앱 (설치된 경우)의 이름과 버전 번호를 설정해야합니다. 이미 ROM에 포함되어 있지 않은 경우 루팅 된 장치 또는 aapt 바이너리 및 busybox를 설치하는 방법이 필요합니다.

이 스크립트는 서버에서 앱 목록을 가져와 설치된 모든 앱과 비교합니다. 결과적으로 업그레이드 / 설치용으로 플래그가 지정된 목록이 표시됩니다.

#/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를 사용하지 않고 그것을하는 방법을 물었습니다. apktool과 약간의 스크립팅으로 apk 정보를 추출 할 수도 있습니다. 이 방법은 안드로이드에서 느리고 간단하지 않지만 apktool 설정을 사용하는 한 Windows / Mac 또는 Linux에서 작동합니다.

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

서버의 드롭 폴더도 고려하십시오. apk를 업로드하면 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

답변

현재이 작업은 다음과 같이 수행 할 수 있습니다

$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

답변

이제 이진 XML 데이터에서 APK 파일 버전을 성공적으로 검색 할 수 있습니다.

이 주제는 내 대답의 열쇠를 얻은 곳입니다 (리보 코드 버전도 추가했습니다).
.apk 패키지 내에서 AndroidManifest.xml 파일을 구문 분석하는 방법

또한, 내가 작성한 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;
}

이게 도움이 되길 바란다.


답변

업그레이드 시나리오의 경우 대체 방법은 현재 버전 번호를 제공하는 웹 서비스를 사용하고 전체 APK를 다운로드하는 대신 버전을 확인하는 것입니다. 그것은 약간의 대역폭을 절약하고, 약간 더 성능이 좋으며 (전체 apk가 거의 필요하지 않으면 apk보다 훨씬 빠릅니다) 구현하기가 훨씬 간단합니다.

가장 간단한 형태로 서버에 간단한 텍스트 파일을 가질 수 있습니다 … http://some-place.com/current-app-version.txt

그 텍스트 파일 안에는 다음과 같은 것이 있습니다.

3.1.4

그런 다음 해당 파일을 다운로드하고 현재 설치된 버전을 확인하십시오.

그것에 대한 고급 솔루션을 구축하는 것은 적절한 웹 서비스를 구현하고 시작할 때 json을 반환 할 수있는 API 호출을 갖는 것입니다 http://api.some-place.com/versionCheck.

{
    "current_version": "3.1.4"
}