Tải tập tin Android .apk VersionName hoặc VersionCode KHÔNG cài đặt apk


184

Làm cách nào tôi có thể lấy mã phiên bản hoặc tên phiên bản apk của tôi từ tệp AndroidManifest.xml sau khi tải xuống và không cần cài đặt.

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

Ví dụ: tôi muốn kiểm tra xem phiên bản mới có được tải lên trên dịch vụ IIS của mình không, sau khi cài đặt nó trên thiết bị, nếu đó không phải là phiên bản mới, tôi không muốn cài đặt nó.



BTW, tôi hy vọng không ai nghĩ rằng một giải pháp tốt là tải xuống tệp, và sau đó kiểm tra xem nó có cần thiết không - đó là những gì câu đầu tiên gợi ý. Có lẽ thay vào đó bạn sẽ muốn mã chạy trên máy chủ của mình và phản hồi với số phiên bản có sẵn.
ToolmakerSteve

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

aapt sẽ tốn khoảng 2.6Mdung lượng đĩa, bạn cũng có thể viết một trình phân tích cú pháp để phân tích tệp apk AXMLResourcemà sẽ chỉ tốn khoảng 52k. Tham khảo phân tích cú pháp Java xml với không gian tên chưa được khai báo
Kris Roofe

Câu trả lời:


417

Sau đây làm việc cho tôi từ dòng lệnh:

aapt dump badging myapp.apk

LƯU Ý: aapt.exe được tìm thấy trong build-toolsthư mục con của SDK. Ví dụ:

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

26
Hoạt động tốt, điều này nên được đánh dấu là câu trả lời chính xác, đối với thông tin, aaptlà trong công cụ xây dựng / XX trong sdk.
Quentin Klein

8
Tôi đã tìm thấy nó trong "<sdk_path> /build-tools/21.1.2"
gmuhammad

1
Sử dụng Xamarin trên máy Mac, nó đã ở trong~/Library/Developer/Xamarin/android-sdk-macosx/build-tools/{version}
cscott530

Cái gì platformBuildVersionNameđược in khi tôi sử dụng lệnh này và tại sao nó lại trống?
Sevastyan Savanyuk

bạn có thể tạo tập tin bat aapt dump badging %1 pauseđể kéo n thả bất kỳ apk nào
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
Làm thế nào tôi có thể đọc apknội dung mà không cần cài đặt ứng dụng? @PatrickCho
Talha06

2
Điều này thực sự làm việc. Và bạn không cần phải cài đặt nó. Bạn chỉ cần có apk ở đâu đó trên bộ lưu trữ của thiết bị và cung cấp đường dẫn đến nó cho người quản lý gói. Nó thực sự sẽ trả về thông tin gói.
Daniel Zolnai 7/03/2015

45

Nếu bạn đang sử dụng phiên bản 2.2 trở lên của Android Studio thì trong Android Studio sử dụng Build Analyze APKsau đó chọn tệp AndroidManifest.xml.


7
OP đã hỏi làm thế nào để có được nóprogrammatically
forresthopkinsa

3
Bởi vì anh ta không biết rằng một công cụ như thế này được tích hợp trong Android Studio. Ông cũng hỏi mà không cài đặt apk .
vovahost

3
Tất nhiên anh không biết điều đó, vì Android Studio không tồn tại khi câu hỏi này được hỏi. Ngoài ra, ngay cả khi có, điều này không giải quyết được vấn đề của anh ấy - anh ấy muốn thiết bị của mình kiểm tra lập trình các bản cập nhật từ máy chủ IIS của anh ấy, như anh ấy đã nêu trong câu hỏi. Câu trả lời của Patrick Cho giải thích cách thực hiện theo chương trình mà không cần cài đặt.
forresthopkinsa

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

Điều này trả lời câu hỏi bằng cách chỉ trả lại số phiên bản. Tuy nhiên......

Mục tiêu như đã nêu trước đây là tìm hiểu xem apk trên máy chủ có mới hơn so với cái được cài đặt TRƯỚC khi cố tải xuống hoặc cài đặt nó không. Cách dễ nhất để làm điều này là bao gồm số phiên bản trong tên tệp của apk được lưu trữ trên máy chủ, vdmyapp_1.01.apk

Bạn sẽ cần thiết lập tên và số phiên bản của các ứng dụng đã được cài đặt (nếu được cài đặt) để so sánh. Bạn sẽ cần một thiết bị đã root hoặc một phương tiện cài đặt nhị phân aapt và busybox nếu chúng chưa được bao gồm trong rom.

Tập lệnh này sẽ lấy danh sách các ứng dụng từ máy chủ của bạn và so sánh với bất kỳ ứng dụng nào đã cài đặt. Kết quả là một danh sách được gắn cờ để nâng cấp / cài đặt.

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

Như ai đó hỏi làm thế nào để làm điều đó mà không sử dụng aapt. Cũng có thể trích xuất thông tin apk bằng apktool và một chút kịch bản. Cách này chậm hơn và không đơn giản trong Android nhưng sẽ hoạt động trên windows / mac hoặc linux miễn là bạn đã thiết lập 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

Cũng xem xét một thư mục thả trên máy chủ của bạn. Tải apks lên nó và một tác vụ cron đổi tên và di chuyển chúng vào thư mục cập nhật của bạn.

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

Hiện tại, điều này có thể được thực hiện như sau

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

Nói chung, nó sẽ là:

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

5

Bây giờ tôi có thể truy xuất thành công phiên bản của tệp APK từ dữ liệu XML nhị phân của nó.

Chủ đề này là nơi tôi nhận được chìa khóa cho câu trả lời của mình (Tôi cũng đã thêm phiên bản mã Ribo của mình): Cách phân tích tệp AndroidManifest.xml trong gói .apk

Ngoài ra, đây là mã phân tích cú pháp XML mà tôi đã viết, đặc biệt để tìm nạp phiên bản:

Phân tích cú pháp 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;
}

So sánh phiên bản (được xem là SystemPackageTools.compareVersions trong đoạn trích trước) LƯU Ý: Mã này được lấy cảm hứng từ chủ đề sau: Cách hiệu quả để so sánh các chuỗi phiên bản trong 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;
}

Tôi hi vọng cái này giúp được.


Bạn đã nâng cấp lên Gradle hoặc Android Studio mới chưa? Điều này vẫn còn hoạt động? Mã Robio không hoạt động để giải nén XML cho tôi nữa.
GR Envoy

4

Đối với kịch bản nâng cấp cụ thể, một cách tiếp cận khác có thể là có một dịch vụ web cung cấp số phiên bản hiện tại và kiểm tra xem thay vì tải xuống toàn bộ apk chỉ để kiểm tra phiên bản của nó. Nó sẽ tiết kiệm một số băng thông, hiệu suất cao hơn một chút (tải xuống nhanh hơn nhiều so với apk nếu toàn bộ apk không cần thiết hầu hết thời gian) và đơn giản hơn nhiều để thực hiện.

Ở dạng đơn giản nhất, bạn có thể có một tệp văn bản đơn giản trên máy chủ của mình ... http://some-place.com/current-app-version.txt

Bên trong tệp văn bản đó có nội dung như

3.1.4

và sau đó tải xuống tệp đó và kiểm tra phiên bản hiện được cài đặt.

Xây dựng một giải pháp tiên tiến hơn cho điều đó sẽ là triển khai một dịch vụ web phù hợp và có một cuộc gọi api khi khởi chạy có thể trả về một số json, tức là http://api.some-place.com/versionCheck:

{
    "current_version": "3.1.4"
}

Bạn đang đi đúng hướng - không nên tải xuống tệp, chỉ để có phiên bản của nó. Tôi chỉ muốn đề cập rằng tôi sẽ không khuyên bạn nên tạo tệp văn bản MANUALLY - quá dễ để nó không khớp với tệp apk thực tế. Thay vào đó, hãy viết một tập lệnh (sử dụng một trong những câu trả lời khác) để xem bên trong apk để xem phiên bản của nó là gì, lưu trữ tập tin đó trong tệp hoặc cơ sở dữ liệu, để được truy xuất bằng phương tiện bạn thảo luận.
ToolmakerSteve

Vâng trong kịch bản này, trường hợp không phải là nó phù hợp với APK, chỉ là số phiên bản mới nhất có thể truy cập công khai.
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
Đây không phải là một câu trả lời cho câu hỏi. Đây là mã, bên trong một ứng dụng đang chạy, lấy một số thông tin phiên bản và hiển thị nó. Câu hỏi là làm thế nào để có được thông tin này từ tệp APK mà không cần cài đặt ứng dụng .
ToolmakerSteve

Điều này chỉ dành cho ứng dụng đã được cài đặt không dành cho tệp .apk.
Rhusfer
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.