如何将构建时间戳写入apk

Fal*_*ain 55 android makefile build

  1. 在Android Contacts包中进行一些更改
  2. 使用mm(make)命令构建此应用程序

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

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

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

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

有什么建议?

Ale*_*iks 123

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

android {
    defaultConfig {
        buildConfigField "long", "TIMESTAMP", System.currentTimeMillis() + "L"
    }
}
Run Code Online (Sandbox Code Playgroud)

然后在运行时读取它.

Date buildDate = new Date(BuildConfig.TIMESTAMP);
Run Code Online (Sandbox Code Playgroud)

  • 这应被视为最佳答案. (3认同)
  • 如果您使用Gradle,我发现这是最好的选择 (2认同)
  • 这会减慢你的构建时间。避免将其用于开发人员构建 (2认同)

Poi*_*ull 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){
  }
Run Code Online (Sandbox Code Playgroud)

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

  • 但是在阅读之后别忘了打电话给**zf.close()** (11认同)
  • 使用Gradle 2.2.1,上面的确切代码将返回11/30/1979 12:00 AM的classes.dex(以及APK中的所有其他文件).任何人都可以解决这个问题?编辑:忽略.根据下面的评论,这是一个问题:https://code.google.com/p/android/issues/detail?id=220039 (7认同)
  • 是的,现在我也在“关于”对话框中使用它:) (2认同)
  • 嗯,在将Android Studio切换为使用Instant Run之后,我今天刚刚使用此代码段获得了NullPointerException.使用Instant Run构建时,从ApplicationInfo获取的ZipFile不包含"classes.dex"条目,从而导致NPE.我切换到"AndroidManifest.xml",它似乎解决了我的项目中的问题. (2认同)

Poi*_*ull 24

从API版本9开始,有:

PackageInfo.lastUpdateTime

应用上次更新的时间.

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

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

  • PackageInfo.lastUpdate安装了datetime.它与构建日期时间不同. (9认同)
  • 现在我看,我不能使用PackageInfo.lastUpdateTime,因为它取决于目标手机的时间.每次安装或更新apk时,都会通过读取最终用户的本地时间来更新PackageInfo.lastUpdateTime.需要的是apk的构建时间.所以,我认为我必须在本机代码中使用__DATE__宏. (7认同)

all*_*mex 13

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

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

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

android.keepTimestampsInApk = true
Run Code Online (Sandbox Code Playgroud)

问题220039

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


Mar*_*itz 7

在你的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))'
    }
}
Run Code Online (Sandbox Code Playgroud)


pre*_*ela 6

Install time : packageInfo.lastUpdateTime
build time   : zf.getEntry("classes.dex").getTime()
Run Code Online (Sandbox Code Playgroud)

两者都是不同的时间.您可以查看以下代码.

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

    }
}
Run Code Online (Sandbox Code Playgroud)


小智 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){
  }
Run Code Online (Sandbox Code Playgroud)


Mor*_*ang 5

因此,Android Developer - Android Studio 用户指南 - Gradle 提示和食谱 - 简化应用程序开发实际上记录了要添加的内容,以便为您的应用程序提供可用的发布时间戳:

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")
    }
  }
}
...
Run Code Online (Sandbox Code Playgroud)

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

...
Log.i(TAG, BuildConfig.BUILD_TIME);
Log.i(TAG, getString(R.string.build_time));
Run Code Online (Sandbox Code Playgroud)

我将其包含在此处是因为所有其他解决方案似乎都来自官方示例之前。