1. 程式人生 > >How to read version (and other) information from Android and iOS apps using Java

How to read version (and other) information from Android and iOS apps using Java

How to read version (and other) information from Android and iOS apps using Java

https://medium.com/@mart.schneider/how-to-read-version-and-other-information-from-android-and-ios-apps-using-java-3be7cf067f79

Many times, especially when running automated tests against mobile apps, it is important to verify that the correct version of an app is used. This article describes easy ways to query the version information from both Android and iOS app packages using either command line tools or Java libraries. Both approaches can be easily integrated into a build pipeline.

The command-line way

For Android, to parse meta information from an APK file one approach is to use aapt like this:

aapt dump badging /path/test.apk

This prints a lot of information so we need to trim the output to what we are interested in:

aapt dump badging test.apk | grep -o 'versionName=[^,]*' | cut -d'=' -f 2 | cut -d ' ' -f 1 | tr -d "'"

Similarly, we can also query the version code (or any other information for that matter):

aapt dump badging /path/test.apk | grep -o ‘versionCode=[^,]*’ | cut -d’=’ -f 2 | cut -d ‘ ‘ -f 1 | tr -d "'"

If we have the app installed on a device (physical or simulator) which is connected via adb

 we can also query the version like so:

adb shell dumpsys package package.of.the.app | grep versionName | cut -d'=' -f 2 | cut -d ' ' -f 1
adb shell dumpsys package package.of.the.app | grep versionCode | cut -d'=' -f 2 | cut -d ' ' -f 1

For iOS apps, let’s consider APP packages (used for simulators) first. If we are on Mac OSX, we can use defaults (note that the path needs to be absolute):

defaults read /path/test.app/Info CFBundleShortVersionString
defaults read /path/test.app/Info CFBundleVersion

On Linux, we need plistutil and an XML parser like xmllint:

plistutil -i /path/test.app/Info | xmllint --xpath "//key[text()='CFBundleShortVersionString']/following-sibling::string[1]/text()" -
plistutil -i /path/test.app/Info | xmllint --xpath "//key[text()='CFBundleVersion']/following-sibling::string[1]/text()"

If we want to retrieve the version information from an IPA file (used for physical devices) we must unpack it first. Then, we can run the same commands as above:

# Unpack IPA file
unzip -q /path/test.ipa -d /tmp/ipa
# Mac OS
defaults read /tmp/ipa/Payload/test.app/Info CFBundleShortVersionString
defaults read /tmp/ipa/Payload/test.app/Info CFBundleVersion
# Linux
plistutil -i /tmp/ipa/Payload/test.app/Info | xmllint --xpath "//key[text()='CFBundleShortVersionString']/following-sibling::string[1]/text()" -
plistutil -i /tmp/ipa/Payload/test.app/Info | xmllint --xpath "//key[text()='CFBundleVersion']/following-sibling::string[1]/text()"
# Clean-up
rm -rf /tmp/ipa

The Java way

One way to retrieve the version information from within a Java program is to use Runtime or ProcessBuilder to execute the above shell commands. However, this is platform-dependent and a bit brittle. It requires that the tools used (aaptplistutilxmllint etc.) are available on the machine running the Java code.

Therefore, a native Java solution is preferable. Luckily, there are open source libraries for all our needs:

  • apk-parser: This library allows us to read information from APK files.
  • dd-plist: This library can process plist files used to store iOS app meta information.
  • zip4j: This library is one (of many) options to handle zip files in Java.

Using Maven, we can include them in our project:

<properties>
  <dd.plist.version>1.21</dd.plist.version>
  <apk.parser.version>2.6.4</apk.parser.version>
  <zip4j.version>1.3.2</zip4j.version>
</properties>
<dependencies>
  <dependency>
    <groupId>com.googlecode.plist</groupId>
    <artifactId>dd-plist</artifactId>
    <version>${dd.plist.version}</version>
  </dependency>
  <dependency>
    <groupId>net.dongliu</groupId>
    <artifactId>apk-parser</artifactId>
    <version>${apk.parser.version}</version>
  </dependency>
  <dependency>
    <groupId>net.lingala.zip4j</groupId>
    <artifactId>zip4j</artifactId>
    <version>${zip4j.version}</version>
  </dependency>
</depdencies>

For Android, we can then simply fetch the version information like this:

String appPath = "/path/test.apk";
ApkFile apkFile = new ApkFile(new File(appPath));
ApkMeta apkMeta = apkFile.getApkMeta();
String versionName = apkMeta.getVersionName();
String versionCode = apkMeta.getVersionCode().toString();

For iOS, the code looks as follows for an APP package:

String appPath = "/path/test.app";
NSDictionary dictionary = (NSDictionary) PropertyListParser.parse(path);
String versionName = dictionary.objectForKey(“CFBundleShortVersionString”).toString();
String versionCode = dictionary.objectForKey(“CFBundleVersion”).toString();

If we are dealing with an IPA file instead:

String appPath = "/path/test.ipa";
# Unpack IPA file
String tmpFolder = File.separator + "tmp" + File.separator + UUID.randomUUID().toString();
ZipFile zipFile = new ZipFile(appPath);
zipFile.extractAll(tmpFolder);
# Fetch version information
NSDictionary dictionary = (NSDictionary) PropertyListParser.parse(new File(new File(tmpFolder + File.separator + "Payload").listFiles()[0].getAbsolutePath() + File.separator + "Info.plist"));
String versionName = dictionary.objectForKey(“CFBundleShortVersionString”).toString();
String versionCode = dictionary.objectForKey(“CFBundleVersion”).toString();
# Clean-up
Files.walk(Paths.get(tmpFolder)).sorted(Comparator.reverseOrder()).map(Path::toFile).forEach(File::delete);

For simplicity, the exception handling has been removed from the above examples.

The full code is available here.

All-in-one library

The demo above is part of the justtestlah test framework. You can use the mobile-tools JAR (which comes with minimum additional dependencies) to query version (and other meta) information from both Android and iOS apps. Import it as follows:

<properties>
  <justtestlah.version>1.3.2</justtestlah.version>
</properties>
<dependencies>
  <dependency>
    <groupId>io.github.martinschneider</groupId>
    <artifactId>justtestlah-mobile-tools</artifactId>
    <version>${justtestlah.version}</version>
  </dependency>
</depdencies>

Then, simply use it like this:

ApplicationInfo appInfo = new ApplicationInfoService().getAppInfo(appPath);
String versionName = appInfo.getVersionName();
String versionCode = appInfo.getVersionCode();
String applicationName = appInfo.getApplicationName();

appPath can point to any APK, IPA or APP file.