如何定义 gluon 应用程序版本

How to define gluon application version

我想把应用程序版本放到我的胶子应用程序中。但我不知道该怎么做。我需要为此创建服务吗?

有一个非常简单的方法,不需要服务,但它可能会导致错误:

您可以在 AndroidManifest:

中为 Android 维护版本号 code/version
<?xml version="1.0" encoding="UTF-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android" 
              package="your.package" 
              android:versionCode="1.1.0" 
              android:versionName="1.1.0">

在 iOS 上相同,在 Default-Info.plist 中:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
        <key>CFBundleIdentifier</key>
        <string>your.bundle.identifier</string>
        <key>CFBundleVersion</key>
        <string>1.1.0</string>

最后,您可以在代码中保留一个静态字段:

public static final String VERSION_NAME = "1.1.0";

例如,您可以从 NavigationDrawer 访问。

但是,每次发布新版本时都需要手动更新。

为避免错误,您可以设置一些 CI 作业来为您完成。例如,请参阅此 commit,它是移动应用程序发布工作的一部分。

魅力羽绒服

但是,避免发布的版本与代码中的变量不匹配的更明确的方法是添加直接读取这些版本的服务。

像这样:

VersionService.java

package com.gluonhq.charm.down.plugins;

public interface VersionService {

    String getVersionName();

}

VersionServiceFactory.java

package com.gluonhq.charm.down.plugins;

import com.gluonhq.charm.down.DefaultServiceFactory;

public class VersionServiceFactory extends DefaultServiceFactory<VersionService> {

    public VersionServiceFactory() {
        super(VersionService.class);
    }

}

Android 包,在 src/android/java:

AndroidVersionService.java

package com.gluonhq.charm.down.plugins.android;

import android.content.pm.PackageManager;
import com.gluonhq.charm.down.plugins.VersionService;
import javafxports.android.FXActivity;

public class AndroidVersionService implements VersionService {

    @Override
    public String getVersionName() {
        try {
            return FXActivity.getInstance().getPackageManager()
                    .getPackageInfo(FXActivity.getInstance().getPackageName(), 0)
                    .versionName;
        } catch (PackageManager.NameNotFoundException ex) { }
        return "";
    }

}

IOS 包,在 src/ios/java:

IOSVersionService.java

package com.gluonhq.charm.down.plugins.ios;

import com.gluonhq.charm.down.plugins.VersionService;

public class IOSVersionService implements VersionService {

    static {
        System.loadLibrary("Version");
    }

    @Override
    public String getVersionName() {
        return getNativeVersion();
    }

    private static native String getNativeVersion();

}

src/ios/native下:

Version.m

#import <UIKit/UIKit.h>
#include "jni.h"

JNIEXPORT jint JNICALL
JNI_OnLoad_Version(JavaVM *vm, void *reserved)
{
#ifdef JNI_VERSION_1_8
    //min. returned JNI_VERSION required by JDK8 for builtin libraries
    JNIEnv *env;
    if ((*vm)->GetEnv(vm, (void **)&env, JNI_VERSION_1_8) != JNI_OK) {
        return JNI_VERSION_1_4;
    }
    return JNI_VERSION_1_8;
#else
    return JNI_VERSION_1_4;
#endif
}

JNIEXPORT jstring JNICALL Java_com_gluonhq_charm_down_plugins_ios_IOSVersionService_getNativeVersion
(JNIEnv *env, jclass jClass)
{
    NSString *version = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleVersion"];
    return (*env)->NewStringUTF(env, [version cStringUsingEncoding:NSASCIIStringEncoding]);
}

在根级别构建文件:

ios-build.gradle

if (System.getProperty('os.name').toLowerCase().contains("mac")) {
    new ByteArrayOutputStream().withStream { os ->
        exec {
            args '-version', '-sdk', 'iphoneos', 'SDKVersion'
            executable 'xcodebuild'
            standardOutput = os
        }
        ext.IOS_VERSION = os.toString().trim()
    }
} else {
    ext.IOS_VERSION = ""
}
ext.IS_DEBUG_NATIVE = Boolean.parseBoolean(System.getProperty("IS_DEBUG_NATIVE", "false"))

def sdkPath(String platform) {
    return "/Applications/Xcode.app/Contents/Developer/Platforms/${platform}.platform/Developer/SDKs/${platform}${IOS_VERSION}.sdk";
}

ext.xcodebuildIOS = {buildDir, projectDir, name ->

    if (!file(sdkPath('iPhoneOS')).exists()) {
        println "Skipping xcodebuild"
        return
    }

    // define statics do being able to configure the input/output files on the task
    // for faster builds if nothing changed
    def buildSystems = ["iPhoneOS+arm64",
                        "iPhoneOS+armv7",
                        "iPhoneSimulator+i386",
                        "iPhoneSimulator+x86_64"]
    def linkerOutputs = []

    def lipoOutput = "$buildDir/native/lib${name}.a"

    def nativeSources = ["$projectDir/src/ios/native/${name}.m"]

    // the actual task action
    buildSystems.each { buildSystem ->

        def (platform, arch) = buildSystem.tokenize("+");

        def compileOutput = "$buildDir/native/$arch"
        def compileOutputs = ["$buildDir/native/$arch/${name}.o"]

        def linkerOutput = "$buildDir/native/$arch/lib${name}.a"

        new File(compileOutput).mkdirs();

        def clangArgs = [
                "-x", "objective-c",
                "-miphoneos-version-min=6.0",
                "-fmessage-length=0",
                "-std=c99",
                "-fno-common",
                "-Wall",
                "-fno-strict-aliasing",
                "-fwrapv",
                "-fpascal-strings",
                "-fobjc-abi-version=2",
                "-fobjc-legacy-dispatch",
                "-I" + System.getenv("JAVA_HOME") + "/include",
                "-I" + System.getenv("JAVA_HOME") + "/include/darwin",
                "-c",
                IS_DEBUG_NATIVE ? ["-O0", "-DDEBUG", "-g"] : ["-O3", "-DNDEBUG"],
                "-arch", arch,
                "-isysroot",
                sdkPath(platform),
                nativeSources].flatten()
        // "-o", compileOutput,

        def linkerArgs = [
                "-static",
                "-framework", "Foundation",
                "-framework", "CoreGraphics",
                "-framework", "CoreBluetooth",
                "-framework", "CoreLocation",
                "-framework", "CoreMotion",
                "-framework", "CoreText",
                "-framework", "UIKit",
                "-framework", "QuartzCore",
                "-framework", "OpenGLES",
                "-framework", "UserNotifications",
                "-arch_only", arch,
                "-syslibroot", sdkPath(platform),
                "-L${sdkPath(platform)}/usr/lib",
                "-o", linkerOutput,
                compileOutputs
        ].flatten()

        // execute compiler
        exec {
            executable "clang"
            args clangArgs
            workingDir compileOutput
        }

        // execute linker
        exec {
            executable "libtool"
            args linkerArgs
            workingDir compileOutput
        }

        linkerOutputs.add(linkerOutput)
    }

    def lipoArgs = [
            "-create",
            linkerOutputs,
            "-o",
            lipoOutput
    ].flatten();

    // execute lipo to combine all linker output in one archive
    exec {
        executable "lipo"
        args lipoArgs
    }
}

build.gradle

buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath 'org.javafxports:jfxmobile-plugin:1.3.16'
    }
}

apply plugin: 'org.javafxports.jfxmobile'
apply from: 'ios-build.gradle'

repositories {
    jcenter()
    maven {
        url 'http://nexus.gluonhq.com/nexus/content/repositories/releases'
    }
}

mainClassName = 'your.main.class'

dependencies {
    compile 'com.gluonhq:charm:5.0.2'
}

jfxmobile {
    javafxportsVersion = '8.60.11'
    downConfig {
        version = '3.8.6'
        // Do not edit the line below. Use Gluon Mobile Settings in your project context menu instead
        plugins 'display', 'lifecycle', 'statusbar', 'storage'
    }
    android {
        manifest = 'src/android/AndroidManifest.xml'
    }
    ios {
        infoPList = file('src/ios/Default-Info.plist')
        forceLinkClasses = [
                'com.gluonhq.**.*',
                'javax.annotations.**.*',
                'javax.inject.**.*',
                'javax.json.**.*',
                'org.glassfish.json.**.*'
        ]
    }
}

task xcodebuild {
    doLast {
        xcodebuildIOS("$project.buildDir","$project.projectDir", "Version")
    }
}

task installNativeLib (type:Copy, dependsOn: xcodebuild) {
    from("$project.buildDir/native")
    into("src/ios/jniLibs")
    include("*.a")
}

在部署到 iOS、运行 之前:

./gradlew installNativeLib

服务使用

最后,您可以在代码中的任何位置使用该服务:

String version = Services.get(VersionService.class)
            .map(VersionService::getVersionName)
            .orElse(""));