- Grab these 3 classes (slightly modified) from the book “Java 1.4 Game Programming”:
// File: BaseClock.java
// Description: Define a clock’s capabilities
// From: Chapter 12, “Java 1.4 Game Programming”
// by Mulholland & Murphy, ISBN 1-55622-963-1
public abstract class BaseClock
{
public abstract long getTime();
public abstract int getDefaultUnit();
public abstract void stampTime();
public abstract long getElapsedTime();
public abstract long getElapsedTime(int unit);
public long stampTime;
public static final int UNIT_SECONDS = 1; // 10^0
public static final int UNIT_MILLIS = 1000; // 10^3
public static final int UNIT_MICROS = 1000000; // 10^6
public static final int UNIT_NANOS = 1000000000; // 10^9
}
// File: StandardClock.java
// Description: A standard pure Java clock
// From: Chapter 12, “Java 1.4 Game Programming”
// by Mulholland & Murphy, ISBN 1-55622-963-1
public class StandardClock extends BaseClock
{
public long getTime()
{
return System.currentTimeMillis();
}
public int getDefaultUnit()
{
return UNIT_MILLIS;
}
public void stampTime()
{
stampTime = System.currentTimeMillis();
}
public long getElapsedTime()
{
return System.currentTimeMillis() - stampTime;
}
public long getElapsedTime(int unit)
{
return ((System.currentTimeMillis() - stampTime) * unit) / UNIT_MILLIS;
}
}
// File: HiresTimeExample.java
// Description: Test hi-rez native clocks
// From: Chapter 12, “Java 1.4 Game Programming”
// by Mulholland & Murphy, ISBN 1-55622-963-1
public class HiresTimeExample
{
public static void main(String[] args)
{
BaseClock clock = null;
// if ( NativeWinClock.isAvailable() )
if ( NativeMacClock.isAvailable() )
{
System.out.println(“Using native clock”);
// clock = new NativeWinClock();
clock = new NativeMacClock();
}
else
{
System.out.println(“Using standard clock”);
clock = new StandardClock();
}
// Constant velocity vector (u,v) = (dx/dt, dy/dt)
// is defined in pixels per microsecond.
// Magnitudes 160 and 240 represent pixels per second.
double u = (double)160 / BaseClock.UNIT_MICROS;
double v = (double)240 / BaseClock.UNIT_MICROS;
// current position to update with movement
double x = 0;
double y = 0;
long dt; // elapsed time
long counter = 0L;
int secondsCounter = 0;
clock.stampTime();
while (secondsCounter<10) // if not passed 10 seconds
{
// get elasped time
dt = clock.getElapsedTime(BaseClock.UNIT_MICROS);
// stamp clock
clock.stampTime();
// increase elasped time counter
counter += dt;
// update world (move sprite)
x += (u * dt);
y += (v * dt);
if (counter >= BaseClock.UNIT_MICROS) // if 1 second has elapsed
{
System.out.println("Time (seconds): " + clock.getTime());
System.out.println("Elapsed time counter = " + counter);
counter -= BaseClock.UNIT_MICROS;
System.out.println("Pos = " + x + ", " + y);
secondsCounter++;
}
// Note: sleep() is not high-resolution,
// it is a source of timing error.
try
{
Thread.sleep(5);
}
catch (Exception e) {}
}
}
}
- Write a Java program ‘NativeMacClock.java’ (similar to ‘NativeWinClock.java’) which calls the native timer:
// File: NativeMacClock.java
// Author: Craig A. Mattocks
// Date: March 2003
// Description: Native hi-rez Apple Macintosh clock
// Reference: Chapter 12, “Java 1.4 Game Programming”
// by Mulholland & Murphy, ISBN 1-55622-963-1
public class NativeMacClock extends BaseClock
{
// JNI timer method. Keyword ‘native’ --> C function.
// Method declaration only, implementation is in file ‘MacClock.c’
private native long getCounter();
// Global class instance vars
private long frequency;
private static boolean available;
// Constructor
public NativeMacClock()
{
frequency = UNIT_NANOS; // Macintosh clock has nanosecond precision
}
// Class methods
public long getTime()
{
return getCounter() / frequency; // Time returned in seconds
}
public int getDefaultUnit()
{
return UNIT_SECONDS;
}
public void stampTime()
{
stampTime = getCounter();
// System.out.println("Time stamp: " + stampTime); // Verify precision
}
public long getElapsedTime()
{
return ((getCounter() - stampTime) * UNIT_MILLIS) / frequency;
}
public long getElapsedTime(int unit)
{
return ((getCounter() - stampTime) * unit) / frequency;
}
public static boolean isAvailable()
{
return available;
}
// Static initializer executes once, when
// class is loaded by runtime system.
// Loads compiled C shared library: libMacClock.jnilib (Mac OS X)
static
{
try
{
System.loadLibrary("MacClock");
available = true;
}
catch (UnsatisfiedLinkError ulp) {}
catch (SecurityException sex) {}
}
}
- Compile the Java class that calls the native method:
javac -O NativeMacClock.java
- Generate a header file for the native method using javah with the native interface flag -jni:
javah -jni NativeMacClock
- Examine the header file and grab the JNIEXPORT signature for the native method:
more NativeMacClock.h
/* DO NOT EDIT THIS FILE - it is machine generated /
#include <jni.h>
/ Header for class NativeMacClock */
#ifndef _Included_NativeMacClock
#define _Included_NativeMacClock
#ifdef __cplusplus
extern “C” {
#endif
#undef NativeMacClock_UNIT_SECONDS
#define NativeMacClock_UNIT_SECONDS 1L
#undef NativeMacClock_UNIT_MILLIS
#define NativeMacClock_UNIT_MILLIS 1000L
#undef NativeMacClock_UNIT_MICROS
#define NativeMacClock_UNIT_MICROS 1000000L
#undef NativeMacClock_UNIT_NANOS
#define NativeMacClock_UNIT_NANOS 1000000000L
/* Inaccessible static: available /
/
- Class: NativeMacClock
- Method: getCounter
- Signature: ()J
*/
JNIEXPORT jlong JNICALL Java_NativeMacClock_getCounter
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
- Create the C source file ‘MacClock.c’ which contains the native timer method. Include the header and insert the above JNIEXPORT method signature (with appropriate parameters!):
// File: MacClock.c
// Author: Craig A. Mattocks
// Date: March 2003
// Description: Native hi-rez Apple Macintosh timer
// Returns elapsed time in nanoseconds
// since the machine was last booted.
#include <Carbon/Carbon.h>
#include “NativeMacClock.h”
JNIEXPORT jlong JNICALL Java_NativeMacClock_getCounter(JNIEnv *env, jobject this)
{
union
{
Nanoseconds ns;
UInt64 i;
} time;
time.ns = AbsoluteToNanoseconds( UpTime() );
return time.i;
}
- Compile the header and implementation files, building the JNI library as a bundle:
cc -bundle -I/System/Library/Frameworks/JavaVM.framework/Headers -o libMacClock.jnilib -framework JavaVM -framework CoreServices MacClock.c
- Move the JNI library to an appropriate place (optional):
mv libMacClock.jnilib /Library/Java/Extensions/
then let Java know where it can be found, either by using the:
-Djava.library.path="/Library/Java/Extensions"
option on the command line or in your information property list, or by setting the environment variables:
setenv DYLD_LIBRARY_PATH .:/Library/Java/Extensions:/usr/local/lib
setenv LD_LIBRARY_PATH .:/Library/Java/Extensions
- Compile and run the timer example:
javac -O HiresTimeExample.java
java HiresTimeExample
Hooray, it works!
Craig