Как записать метку времени сборки в apk - PullRequest
53 голосов
/ 30 сентября 2011
  1. Внесение некоторых изменений в пакет контактов Android
  2. Использование команды mm (make) для создания этого приложения

Поскольку мне нужно изменить и собрать это приложение сноваи снова, поэтому я хочу добавить метку времени сборки в Contacts.apk, чтобы проверить время сборки, когда мы запускаем его в телефоне.

Как мы знаем, когда мы запускаем команду mm, Android.mk (makefile) будет вызываться в пакете контактов.

И теперь мы можем получить время сборки, используя date -macro.

Но как мы можем записать эту метку времени сборки в файл, который наше приложение может прочитать во время выполнения?

Есть предложения?

Ответы [ 9 ]

121 голосов
/ 15 октября 2014

Если вы используете Gradle, вы можете добавить buildConfigField с отметкой времени, обновленной во время сборки.

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

Затем прочитать ее во время выполнения.

Date buildDate = new Date(BuildConfig.TIMESTAMP);
94 голосов
/ 30 сентября 2011

Метод, который проверяет дату последней модификации 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-карту.

24 голосов
/ 30 сентября 2011

Начиная с версии 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).

12 голосов
/ 07 октября 2016

Подсказка для решения «время последнего изменения файла classes.dex» для более новых версий AndroidStudio: в конфигурации по умолчанию метка времени больше не записывается в файлы в файле apk.Отметка времени всегда «30 ноября 1979 года».

Вы можете изменить это поведение, добавив эту строку в файл

% userdir% /. Gradle / gradle.properties (создать, если не существует)

android.keepTimestampsInApk = true

См. Выпуск 220039

(Должно быть в userdir, gradle.properties в каталоге сборки проекта, похоже, не работает)

6 голосов
/ 05 октября 2016

в вашем 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 голосов
/ 19 февраля 2013
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 голосов
/ 02 июня 2014

Я использую ту же стратегию, что и 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){
  }
4 голосов
/ 06 января 2014

Я знаю, что это действительно старо, но вот как я это сделал, используя ant в eclipse:

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.

2 голосов
/ 02 декабря 2018

Итак 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")
    }
  }
}
...

В коде вашего приложения вы можете получить доступ к свойствам следующим образом:

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

Я включил это здесь, поскольку все другие решения, по-видимому, были сделаны до официального примера.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...