【问题标题】:How to write build time stamp into apk如何将构建时间戳写入apk
【发布时间】:2011-11-28 06:15:49
【问题描述】:
  1. 对 Android 联系人包进行一些更改
  2. 使用mm (make) 命令构建此应用程序

因为我必须一次又一次地更改和构建这个应用程序,所以我想在Contacts.apk 中添加一个构建时间戳来检查我们在手机中运行它时的构建时间。

我们知道,当我们运行mm 命令时,Contacts 包中的Android.mk (makefile) 会被调用。

现在,我们可以使用date-macro 获取构建时间。

但是我们如何将这个构建时间戳写入我们的应用程序可以在运行时读取的文件中呢?

有什么建议吗?

【问题讨论】:

  • 您想将构建时间戳添加到 apk 本身吗?所以它变成了MyApp-201109302359.apk?
  • 谢谢,我已经做到了。作为 MyApp-201109302359.apk,但如您所知,我必须多次构建此应用程序。如果我这样做了,*system/app 文件夹中会有很多 MyApp-*.apk,这不是我想要的。
  • 我想将构建时间戳写入我的 apk 可以读取的文件中,并在运行时将其显示在 Activity 中。
  • 我在这里发布了一种使用 Android Studio 和 gradle 的方法:stackoverflow.com/a/22649533/3035127

标签: android build makefile


【解决方案1】:

如果您使用 Gradle,您可以添加 buildConfigField,并在构建时更新时间戳。

android {
    defaultConfig {
        buildConfigField "long", "TIMESTAMP", System.currentTimeMillis() + "L"
    }
}

然后在运行时读取它。

Date buildDate = new Date(BuildConfig.TIMESTAMP);

【讨论】:

  • 如果你使用 Gradle,我发现这是最好的选择
  • 谢谢,我一直在寻找这么简单的东西
  • 这应该被认为是最好的答案。
  • 什么是 BuildConfig?。你应该添加类定义。
  • 这会减慢您的构建时间。避免将其用于开发人员构建
【解决方案2】:

检查 classes.dex 上次修改日期的方法,这意味着您的应用程序代码的上次构建时间:

  try{
     ApplicationInfo ai = getPackageManager().getApplicationInfo(getPackageName(), 0);
     ZipFile zf = new ZipFile(ai.sourceDir);
     ZipEntry ze = zf.getEntry("classes.dex");
     long time = ze.getTime();
     String s = SimpleDateFormat.getInstance().format(new java.util.Date(time));
     zf.close();
  }catch(Exception e){
  }

经过测试,工作正常,即使应用程序安装在 SD 卡上。

【讨论】:

  • 是的,现在我也在关于对话框中使用它:)
  • 如何将“String s”添加到 .apk 名称中?
  • 嗯,在将 Android Studio 切换为使用 Instant Run 之后,我今天使用这个 sn-p 得到了 NullPointerException。使用 Instant Run 构建时,从 ApplicationInfo 获取的 ZipFile 不包含“classes.dex”条目,从而导致 NPE。我切换到“AndroidManifest.xml”,它似乎已经解决了我的项目中的问题。
  • 对于 Gradle 2.2.1,上面的确切代码为 classes.dex(以及 APK 中的所有其他文件)返回 11/30/1979 12:00 AM。有人解决这个问题吗?编辑:忽略。根据下面的评论,这是此处提到的问题:code.google.com/p/android/issues/detail?id=220039
【解决方案3】:

从 API 版本 9 开始:

PackageInfo.lastUpdateTime

应用上次更新的时间。

try {
    PackageInfo packageInfo = getPackageManager().getPackageInfo(getPackageName(), 0);
    //TODO use packageInfo.lastUpdateTime
} catch (PackageManager.NameNotFoundException e) {
    e.printStackTrace();
}

在较低的 API 版本上,您必须自己安排构建时间。例如将文件放入包含日期的资产文件夹中。或者在本机代码中使用 __ DATE__ 宏。或者检查 classes.dex 的构建日期(APK 中的文件日期)。

【讨论】:

  • 非常感谢,您的建议非常有价值。 ^_^ 我正在检查使用 PackageInfo.lastUpdateTime 的方法...
  • 如何在我自己的应用程序的java代码中检查我自己的classes.dex的最后修改日期?更糟糕的是,当我重建应用程序时,我发现 classes.dex 没有更新。
  • PackageInfo.lastUpdate 对我不起作用,因为它的值始终是“2000-01-01”。
  • 现在我明白了,我不能使用 PackageInfo.lastUpdateTime,因为它取决于目标手机的时间。每次安装或更新 apk 时,PackageInfo.lastUpdateTime 将通过读取最终用户的本地时间来更新。而需要的是 apk 的构建时间。所以,我想我必须在本机代码中使用 DATE 宏。
  • PackageInfo.lastUpdate 安装日期时间。它与构建日期时间不同。
【解决方案4】:

编辑:我的答案不再起作用,因为选项 keepTimestampsInApk 已被删除。 2020 年的工作是https://stackoverflow.com/a/26372474/6937282(也可以https://stackoverflow.com/a/22649533/6937282 了解更多详情)

原答案:

解决方案“classes.dex 文件的最后修改时间”的提示以及较新的 AndroidStudio 版本: 在默认配置中,时间戳不再写入 apk 文件中的文件。时间戳始终为“1979 年 11 月 30 日”。

您可以通过将此行添加到文件来更改此行为

%userdir%/.gradle/gradle.properties(如果不存在则创建)

android.keepTimestampsInApk = true

Issue 220039

(必须在userdir中,项目构建目录中的gradle.properties似乎不起作用)

【讨论】:

    【解决方案5】:
    Install time : packageInfo.lastUpdateTime
    build time   : zf.getEntry("classes.dex").getTime()
    

    两者都是不同的时间。 您可以使用下面的代码进行检查。

    public class BuildInfoActivity extends Activity {
    
        private static final String TAG = BuildInfoActivity.class.getSimpleName();
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
    
            try {
    
                PackageManager pm = getPackageManager();
                PackageInfo packageInfo = null;
                try {
                    packageInfo = pm.getPackageInfo(getPackageName(), 0);
                } catch (NameNotFoundException e) {
                    e.printStackTrace();
                }
    
                // install datetime
                String appInstallDate = DateUtils.getDate(
                        "yyyy/MM/dd hh:mm:ss.SSS", packageInfo.lastUpdateTime);
    
                // build datetime
                String appBuildDate = DateUtils.getDate("yyyy/MM/dd hh:mm:ss.SSS",
                        DateUtils.getBuildDate(this));
    
                Log.i(TAG, "appBuildDate = " + appBuildDate);
                Log.i(TAG, "appInstallDate = " + appInstallDate);
    
            } catch (Exception e) {
            }
    
        }
    
        static class DateUtils {
    
            public static String getDate(String dateFormat) {
                Calendar calendar = Calendar.getInstance();
                return new SimpleDateFormat(dateFormat, Locale.getDefault())
                        .format(calendar.getTime());
            }
    
            public static String getDate(String dateFormat, long currenttimemillis) {
                return new SimpleDateFormat(dateFormat, Locale.getDefault())
                        .format(currenttimemillis);
            }
    
            public static long getBuildDate(Context context) {
    
                try {
                    ApplicationInfo ai = context.getPackageManager()
                            .getApplicationInfo(context.getPackageName(), 0);
                    ZipFile zf = new ZipFile(ai.sourceDir);
                    ZipEntry ze = zf.getEntry("classes.dex");
                    long time = ze.getTime();
    
                    return time;
    
                } catch (Exception e) {
                }
    
                return 0l;
            }
    
        }
    }
    

    【讨论】:

      【解决方案6】:

      在你的 build.gradle 中:

      android {
          defaultConfig {
              buildConfigField 'String', 'BUILD_TIME', 'new java.text.SimpleDateFormat("MM.dd.yy HH:mm", java.util.Locale.getDefault()).format(new java.util.Date(' + System.currentTimeMillis() +'L))'
          }
      }
      

      【讨论】:

      • 只是System.currentTimeMillis() 似乎更好更简单
      【解决方案7】:

      我使用与空指针相同的策略,但我更喜欢 MANIFEST.MF 文件。 即使修改了布局,也会重新生成这个(classes.dex 不是这种情况)。 我还强制将日期格式设置为 GMT 以避免终端和服务器 TZ 之间的混淆(如果必须进行比较,例如:检查最新版本)。

      结果如下:

        try{
           ApplicationInfo ai = getPackageManager().getApplicationInfo(getPackageName(), 0);
           ZipFile zf = new ZipFile(ai.sourceDir);
           ZipEntry ze = zf.getEntry("META-INF/MANIFEST.MF");
           long time = ze.getTime();
           SimpleDateFormat formatter = (SimpleDateFormat) SimpleDateFormat.getInstance();
           formatter.setTimeZone(TimeZone.getTimeZone("gmt"));
           String s = formatter.format(new java.util.Date(time));
           zf.close();
        }catch(Exception e){
        }
      

      【讨论】:

        【解决方案8】:

        我知道这真的很老了,但这是我在 Eclipse 中使用 ant 的方法:

        项目根目录中的build.xml

        <project name="set_strings_application_build_date" default="set_build_date" basedir=".">
            <description>
                This ant script updates strings.xml application_build_date to the current date
            </description>
        
            <!-- set global properties for this build -->
            <property name="strings.xml"  location="./res/values/strings.xml"/>
        
            <target name="init">
                <!-- Create the time stamp -->
                <tstamp/>
            </target>
        
            <target name="set_build_date" depends="init" description="sets the build date" >
        
                <replaceregexp file="${strings.xml}"
                    match="(&lt;string name=&quot;application_build_date&quot;&gt;)\d+(&lt;/string&gt;)"
                    replace="&lt;string name=&quot;application_build_date&quot;&gt;${DSTAMP}&lt;/string&gt;" />
        
            </target>
        </project>
        

        然后将 application_build_date 字符串添加到您的 strings.xml

        <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
            <string name="app_name">your app name</string>
            <string name="application_build_date">20140101</string>
            ...
        </resources>
        

        确保 ant 脚本作为预构建活动执行,并且您将始终在 R.string.application_build_date 中获得有效的构建日期。

        【讨论】:

        • 可能有点老了,但是很有用。你能帮我用gradle怎么做吗?
        • 所以我实际上只是在 gradle 中实现了相同的脚本,但事后看来我应该使用@AleksejsMjaliks 解决方案。他的方法会将其直接集成到 BuildConfig 中,这更加酷,因为您已经将构建类型、风格、版本文本和版本代码隐藏在其中。
        • 但要回答您的问题,请将其放在项目文件夹的根目录中,更新 strings.xml 的路径(需要为 location="./src/main/res/values/strings.xml ) xml”在 Android Studio 中),然后使用 gradle.build 文件顶部的 ant.importBuild 'build.xml' 将其导入等级。然后,您可以将其作为依赖任务添加到您的装配任务中。
        【解决方案9】:

        所以Android Developer - Android Studio User Guide - Gradle Tips and Recipes - Simplify App Development 实际上记录了要添加的内容,以便为您的应用提供可用的发布时间戳:

        android {
          ...
          buildTypes {
            release {
              // These values are defined only for the release build, which
              // is typically used for full builds and continuous builds.
              buildConfigField("String", "BUILD_TIME", "\"${minutesSinceEpoch}\"")
              resValue("string", "build_time", "${minutesSinceEpoch}")
              ...
            }
            debug {
              // Use static values for incremental builds to ensure that
              // resource files and BuildConfig aren't rebuilt with each run.
              // If they were dynamic, they would prevent certain benefits of
              // Instant Run as well as Gradle UP-TO-DATE checks.
              buildConfigField("String", "BUILD_TIME", "\"0\"")
              resValue("string", "build_time", "0")
            }
          }
        }
        ...
        

        在您的应用代码中,您可以按如下方式访问属性:

        ...
        Log.i(TAG, BuildConfig.BUILD_TIME);
        Log.i(TAG, getString(R.string.build_time));
        

        我在这里包括这个,因为所有其他解决方案似乎都来自官方示例之前。

        【讨论】:

        • 感谢您的提示和链接。实际上,我以一种更适合我们特定构建环境的不同方式解决了这个问题。
        【解决方案10】:

        对于时间戳和版本控制,build.gradle/android/defaultConfig:

        def buildDateStamp = new Date().format("yyyyMMdd").toInteger()
        versionCode buildDateStamp
        versionName "$buildDateStamp"
        buildConfigField "String", "BUILD_DATE_STAMP", "\"$buildDateStamp\""
        

        代码中的用法:BuildConfig.BUILD_DATE_STAMP

        resValue "string", "build_date_stamp", "$buildDateStamp"
        

        xml 中的用法:"@string/build_date_stamp"

        警告:添加HHmm 会导致错误(可能是整数溢出)

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2022-11-17
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-02-09
          • 2019-03-14
          • 2023-03-05
          • 2014-07-19
          相关资源
          最近更新 更多