รับไฟล์ 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 ของฉันหรือไม่หลังจากติดตั้งบนอุปกรณ์หากไม่ใช่เวอร์ชันใหม่ฉันไม่ต้องการติดตั้ง



BTW ฉันหวังว่าไม่มีใครคิดว่าวิธีแก้ปัญหาที่ดีคือการดาวน์โหลดไฟล์แล้วตรวจสอบว่าจำเป็นหรือไม่ - ซึ่งเป็นประโยคแรกที่แนะนำ สันนิษฐานว่าคุณต้องการรหัสที่ทำงานบนเซิร์ฟเวอร์ของคุณและตอบกลับด้วยหมายเลขเวอร์ชันที่มีอยู่
ToolmakerSteve

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

aapt จะเสียค่าใช้จ่ายเกี่ยวกับ2.6Mพื้นที่ดิสก์คุณยังสามารถเขียน parser เพื่อแยกไฟล์ APK ด้วยซึ่งก็จะมีค่าใช้จ่ายเกี่ยวกับAXMLResource 52kอ้างถึงJava parse 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
Quentin Klein

8
ฉันพบมันภายใต้ "<sdk_path> /build-tools/21.1.2"
gmuhammad

1
ใช้ Xamarin บน Mac มันเป็น~/Library/Developer/Xamarin/android-sdk-macosx/build-tools/{version}
cscott530

อะไรคือสิ่งplatformBuildVersionNameที่จะถูกพิมพ์เมื่อฉันใช้คำสั่งนี้และทำไมมันว่างเปล่า?
Sevastyan Savanyuk

คุณสามารถสร้างไฟล์ bat aapt dump badging %1 pauseเพื่อลาก n ปล่อย 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 อยู่ที่ไหนสักแห่งในที่เก็บข้อมูลของอุปกรณ์ของคุณและจัดหาเส้นทางไปยังผู้จัดการบรรจุภัณฑ์ มันจะส่งคืนข้อมูลแพคเกจแน่นอน
Daniel Zolnai

45

หากคุณใช้เวอร์ชั่น 2.2 ขึ้นไปของAndroid Studioจากนั้นใน Android Studio ให้ใช้Build Analyze APKจากนั้นเลือกไฟล์ AndroidManifest.xml


7
OP ถามว่าจะรับได้อย่างไรprogrammatically
forresthopkinsa

3
เพราะเขาไม่รู้ว่าเครื่องมือแบบนี้สร้างขึ้นใน Android Studio นอกจากนี้เขายังถามไม่ต้องติดตั้ง apk
vovahost

3
แน่นอนว่าเขาไม่รู้เช่นนั้นเพราะ Android Studio ไม่มีอยู่เมื่อมีการถามคำถามนี้ นอกจากนี้ถึงแม้ว่ามันจะทำนี้ไม่ได้แก้ปัญหาของเขา - เขาต้องการอุปกรณ์ของเขาไปยังโปรแกรมตรวจสอบการปรับปรุงจากเซิร์ฟเวอร์ของเขา IIS ในขณะที่เขาระบุไว้ในคำถาม คำตอบของ Patrick Cho อธิบายวิธีการเขียนโปรแกรมโดยไม่ต้องติดตั้ง
forresthopkinsa

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

วิธีนี้จะตอบคำถามโดยส่งกลับเฉพาะหมายเลขเวอร์ชันตามผลลัพธ์ แต่ ......

เป้าหมายตามที่ระบุไว้ก่อนหน้านี้ควรตรวจสอบว่า apk บนเซิร์ฟเวอร์ใหม่กว่าที่ติดตั้งไว้ก่อนที่จะพยายามดาวน์โหลดหรือติดตั้ง วิธีที่ง่ายที่สุดในการทำเช่นนี้คือรวมหมายเลขรุ่นในชื่อไฟล์ของ apk ที่โฮสต์บนเซิร์ฟเวอร์เช่นmyapp_1.01.apk

คุณจะต้องสร้างชื่อและหมายเลขเวอร์ชันของแอพที่ติดตั้งแล้ว (หากติดตั้งไว้) เพื่อทำการเปรียบเทียบ คุณจะต้องมีอุปกรณ์ที่รูทหรือติดตั้งไบนารี 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 นอกจากนี้ยังเป็นไปได้ที่จะดึงข้อมูล 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 แบบไบนารีได้สำเร็จ

หัวข้อนี้คือที่ที่ฉันได้รับกุญแจสำคัญในคำตอบของฉัน (ฉันได้เพิ่มรหัส 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'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
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.