ダウンロードした後、インストールせずに、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は、SDKのbuild-tools
サブフォルダーにあります。例えば:
<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();
使用している場合 バージョン2.2以降 の Android Studio その後、Android Studioで使用します Build → Analyze APK 次に、AndroidManifest.xmlファイルを選択します。
これで、バイナリXMLデータからAPKファイルのバージョンを正常に取得できます。
このトピックは、答えの鍵を得た場所です(Riboのコードのバージョンも追加しました): 。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: 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;
}
バージョン比較(前のスニペットでSystemPackageTools.compareVersionsとして表示)注:このコードは、次のトピックから着想を得ています。 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;
}
これがお役に立てば幸いです。
aapt dump badging test.apk | grep "VersionName" | sed -e "s/.*versionName='//" -e "s/' .*//"
これは、結果としてバージョン番号のみを返すことで質問に答えます。しかしながら......
前述の目標は、サーバー上のapkがダウンロードまたはインストールを試行する前にインストールされているapkよりも新しいかどうかを確認することです。これを行う最も簡単な方法は、サーバーでホストされているapkのファイル名にバージョン番号を含めることです(例:myapp_1.01.apk
)
比較を行うには、既にインストールされているアプリの名前とバージョン番号を設定する必要があります(インストールされている場合)。ルート化されたデバイス、またはaaptバイナリとbusyboxがまだromに含まれていない場合、それらをインストールする手段が必要になります。
このスクリプトは、サーバーからアプリのリストを取得し、インストールされているアプリと比較します。その結果、アップグレード/インストールのフラグが付けられたリストが作成されます。
#/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
誰かがaaptを使用せずにそれを行う方法を尋ねたので。 apktoolと少しのスクリプトを使用してapk情報を抽出することもできます。この方法はAndroidでは遅く、単純ではありませんが、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
アップグレードシナリオの場合、特に別のアプローチは、バージョンを確認するためだけにapk全体をダウンロードする代わりに、現在のバージョン番号を配信し、それを確認するWebサービスを使用することです。帯域幅をいくらか節約し、パフォーマンスが少し向上し(apk全体がほとんどの場合必要ない場合、apkよりもダウンロードがはるかに高速になります)、実装がはるかに簡単になります。
最も単純な形式では、サーバー上に単純なテキストファイルを作成できます... http://some-place.com/current-app-version.txt
そのテキストファイルの中には、次のようなものがあります
3.1.4
そのファイルをダウンロードして、現在インストールされているバージョンと照合します。
より高度なソリューションを構築するには、適切なWebサービスを実装し、起動時にAPI呼び出しを行い、jsonを返す可能性があります。つまり、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();
}
現時点では、これは次のように行うことができます
$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