如何将构建时间戳写入apk

时间:2011-09-30 07:21:47

标签: android build makefile

  1. 在Android通讯录包中进行一些更改
  2. 使用mm(make)命令构建此应用程序
  3. 因为我必须一次又一次地更改和构建这个应用程序,所以我想在Contacts.apk中添加一个构建时间戳,以检查我们在手机中运行它时的构建时间。

    众所周知,当我们运行mm命令时,将调用Contacts包中的Android.mk(makefile)。

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

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

    有什么建议吗?

10 个答案:

答案 0 :(得分:118)

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

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

然后在运行时阅读。

Date buildDate = new Date(BuildConfig.TIMESTAMP);

答案 1 :(得分:94)

检查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卡上安装了应用,也能正常工作。

答案 2 :(得分:24)

从API版本9开始:

<强> PackageInfo.lastUpdateTime

上次更新应用的时间。

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

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

答案 3 :(得分:12)

提示解决方案&#34; classes.dex文件的最后修改时间&#34;更新的AndroidStudio版本: 在默认配置中,时间戳不再写入apk文件中的文件。时间戳总是&#34; 1979年11月30日&#34;。

您可以通过将此行添加到文件

来更改此行为

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

android.keepTimestampsInApk = true

请参阅Issue 220039

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

答案 4 :(得分:6)

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;
        }

    }
}

答案 5 :(得分:6)

你的build.gradle中的

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

答案 6 :(得分:5)

我使用与Pointer Null相同的策略,除了我更喜欢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){
  }

答案 7 :(得分:4)

我知道这真的很老了,但这就是我在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中拥有可用的有效构建日期。

答案 8 :(得分:2)

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

\documentclass{memoir}

\usepackage{blindtext,xcolor}

\setsecheadstyle{\sffamily\color{blue}}% Set \section style
\setsubsecheadstyle{\sffamily\color{green}}% Set \subsection style

\begin{document}

\blinddocument

\end{document}

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

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")
    }
  }
}
...

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

答案 9 :(得分:1)

对于时间戳和版本控制,请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会导致错误(可能是整数溢出)