mm
(make)コマンドを使用してこのアプリケーションをビルドするこのアプリを何度も変更してビルドする必要があるため、Contacts.apk
にビルドタイムスタンプを追加して、ハンドセットで実行したときにビルド時間を確認したいと思います。
知っているように、mm
コマンドを実行すると、ContactsパッケージのAndroid.mk
(makefile)が呼び出されます。
そして今、date
- macroを使用してビルド時間を取得できます。
しかし、アプリケーションが実行時に読み取ることができるファイルにこのビルドタイムスタンプを書き込むにはどうすればよいですか?
助言がありますか?
Gradleを使用する場合は、ビルド時にタイムスタンプが更新されたbuildConfigField
を追加できます。
Android {
defaultConfig {
buildConfigField "long", "TIMESTAMP", System.currentTimeMillis() + "L"
}
}
次に、実行時にそれを読み取ります。
Date buildDate = new Date(BuildConfig.TIMESTAMP);
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カードにインストールされていても、テストされ、正常に動作します。
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のファイルの日付)を確認します。
ソリューション「classes.dexファイルの最終変更時間」の新しいAndroidStudioバージョンのヒント:デフォルトの構成では、タイムスタンプはapkファイル内のファイルに書き込まれなくなりました。タイムスタンプは常に「1979年11月30日」です。
この動作を変更するには、この行をファイルに追加します
%userdir%/。gradle/gradle.properties(存在しない場合は作成)
Android.keepTimestampsInApk = true
問題220039 を参照してください
(ユーザーディレクトリにある必要があり、プロジェクトビルドディレクトリのgradle.propertiesは機能しないようです)
あなたの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))'
}
}
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;
}
}
}
MANIFEST.MFファイルを好むこと以外は、Pointer Nullと同じ戦略を使用します。これは、レイアウトが変更された場合でも再生成されます(classes.dexの場合はそうではありません)。また、端末とサーバーTZの混乱を避けるために、日付をGMTでフォーマットすることを強制します(比較を行う必要がある場合は、最新バージョンを確認します)。
次のコードになります。
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){
}
これは本当に古いことはわかっていますが、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="(<string name="application_build_date">)\d+(</string>)"
replace="<string name="application_build_date">${DSTAMP}</string>" />
</target>
</project>
次に、strings.xmlにapplication_build_date文字列を追加します
<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内で常に有効なビルド日を利用できるようにします。
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));
他のすべてのソリューションは公式の例の前にあるように見えるので、ここにこれを含めます。