Comment obtenir par programme le code de version ou le nom de version de mon apk à partir du fichier AndroidManifest.xml après l'avoir téléchargé et sans l'avoir installé.
<manifest xmlns:Android="http://schemas.Android.com/apk/res/Android"
package="xxx.xx.xxx"
Android:versionCode="1"
Android:versionName="1.1" >
Par exemple, je veux vérifier si une nouvelle version est téléchargée sur mon service IIS, après l'avoir installée sur le périphérique. S'il ne s'agit pas d'une nouvelle version, je ne souhaite pas l'installer.
La suite a fonctionné pour moi de la ligne de commande:
aapt dump badging myapp.apk
REMARQUE: aapt.exe se trouve dans un sous-dossier build-tools
du SDK. Par exemple:
<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();
Si vous utilisez version 2.2 et supérieure de Studio Android puis dans Android utilisation de Studio Build → Analyze APK puis sélectionnez le fichier AndroidManifest.xml.
Je peux maintenant récupérer avec succès la version d'un fichier APK à partir de ses données XML binaires.
C'est dans cette rubrique que j'ai obtenu la clé de ma réponse (j'ai également ajouté ma version du code de Ribo): Comment analyser le fichier AndroidManifest.xml dans un package .apk
De plus, voici le code d'analyse XML que j'ai écrit, spécialement pour extraire la version:
Analyse 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: 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'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;
}
Comparaison de version (vue sous SystemPackageTools.compareVersions dans un extrait de code précédent) REMARQUE: ce code est inspiré du sujet suivant: Méthode efficace pour comparer les chaînes de version en Java
/**
* Compare 2 version strings and tell if the first is higher, equal or lower
* Source: https://stackoverflow.com/questions/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;
}
J'espère que ça aide.
aapt dump badging test.apk | grep "VersionName" | sed -e "s/.*versionName='//" -e "s/' .*//"
Cela répond à la question en renvoyant uniquement le numéro de version. Pourtant......
Comme indiqué précédemment, l'objectif devrait être de savoir si l'apk sur le serveur est plus récent que celui installé AVANT de tenter de le télécharger ou de l'installer. Le moyen le plus simple consiste à inclure le numéro de version dans le nom du fichier de l'apk hébergé sur le serveur, par exemple myapp_1.01.apk
.
Vous devrez établir le nom et le numéro de version des applications déjà installées (si elles sont installées) pour pouvoir comparer. Vous aurez besoin d'un périphérique enraciné ou d'un moyen d'installer le fichier binaire aapt et busybox s'ils ne sont pas déjà inclus dans la rom.
Ce script obtiendra la liste des applications de votre serveur et se comparera à toutes les applications installées. Le résultat est une liste marquée pour la mise à niveau/l'installation.
#/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
Comme quelqu'un a demandé comment faire sans utiliser aapt. Il est également possible d'extraire des informations d'apk avec apktool et un peu de script. Cette façon de faire est plus lente et pas simple sous Android, mais fonctionnera sous Windows/Mac ou Linux tant que vous avez la configuration apktool qui fonctionne.
#!/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
Considérez également un dossier de dépôt sur votre serveur. Téléchargez des apks et une tâche périodique renomme et les déplace dans votre dossier de mise à jour.
#!/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
Pour le scénario de mise à niveau, une autre solution pourrait consister à créer un service Web fournissant le numéro de version actuel et à le vérifier au lieu de télécharger l'intégralité de l'apk uniquement pour vérifier sa version. Cela permettrait d'économiser de la bande passante, d'être un peu plus performant (beaucoup plus rapide à télécharger qu'un apk si l'apk n'est pas nécessaire la plupart du temps) et beaucoup plus simple à mettre en œuvre.
Dans sa forme la plus simple, vous pourriez avoir un simple fichier texte sur votre serveur ... http://some-place.com/current-app-version.txt
À l'intérieur de ce fichier texte ont quelque chose comme
3.1.4
puis téléchargez ce fichier et vérifiez par rapport à la version actuellement installée.
Construire une solution plus avancée à ce problème consisterait à mettre en place un service Web approprié et à avoir un appel d’API au lancement qui pourrait renvoyer un json, c’est-à-dire http://api.some-place.com/versionCheck
:
{
"current_version": "3.1.4"
}
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();
}
Pour le moment, cela peut être fait comme suit
$Android_HOME/build-tools/28.0.3/aapt dump badging /<path to>/<app name>.apk
En général, ce sera:
$Android_HOME/build-tools/<version_of_build_tools>/aapt dump badging /<path to>/<app name>.apk