summaryrefslogtreecommitdiffstats
path: root/src/java/jogamp
diff options
context:
space:
mode:
authorSven Gothel <[email protected]>2012-03-17 21:15:49 +0100
committerSven Gothel <[email protected]>2012-03-17 21:15:49 +0100
commit235f8b1cbff8ed13071d5c19c0be492c0b25cb78 (patch)
tree659845e16bd69372bc7ddc3a42b3aa7130d18df5 /src/java/jogamp
parent0cfc7847c58b51c9a26b50d905b592d1fc4c8578 (diff)
Add 'asset' URLConnection; IOUtil uses URLConnection / incr. effeciency; Android ClassLoaderUtil cleanup;
- Add 'asset' URLConnection - Please read API doc 'PiggybackURLConnection' and 'AssetURLConnection' - Solves generic resource handling where platform locations may differ, ie ClassLoader lookup on Android in the 'assets/' subfolder. - New Android 'AssetDexClassLoader' uses 'assets/' folder for findResource(..) - aapt.signed (our APK ant task) - uses 'assets/' folder - adds the 'assetsdir' attribute allowing to copy other assets into the APK - IOUtil uses URLConnection / incr. effeciency - using URLConnection on all getResource(..) since URL is connected anyways for validation and URLConnection can be used by caller right away - String getRelativeOf(URL, String) -> URL getRelativeOf(URL, String) - preserves scheme, authority, etc - simple parentOf handling, more efficient - reusing new 'asset' protocol impl. - Android ClassLoaderUtil cleanup; - Use createClassLoader(..) impl for build-in static jogamp and user APKs, which removes code redundancy Tests: New code path, especially 'assets' are covered by new unit tests, no regressions on Linux.
Diffstat (limited to 'src/java/jogamp')
-rw-r--r--src/java/jogamp/android/launcher/ActivityLauncher.java11
-rw-r--r--src/java/jogamp/android/launcher/AssetDexClassLoader.java29
-rw-r--r--src/java/jogamp/android/launcher/ClassLoaderUtil.java140
-rw-r--r--src/java/jogamp/android/launcher/LauncherMain.java2
-rw-r--r--src/java/jogamp/android/launcher/TraceDexClassLoader.java27
-rw-r--r--src/java/jogamp/common/os/android/GluegenVersionActivity.java5
-rw-r--r--src/java/jogamp/common/os/android/StaticContext.java14
7 files changed, 144 insertions, 84 deletions
diff --git a/src/java/jogamp/android/launcher/ActivityLauncher.java b/src/java/jogamp/android/launcher/ActivityLauncher.java
index 04c898e..7e2d86d 100644
--- a/src/java/jogamp/android/launcher/ActivityLauncher.java
+++ b/src/java/jogamp/android/launcher/ActivityLauncher.java
@@ -32,7 +32,6 @@ import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import android.app.Activity;
-import android.content.Context;
import android.net.Uri;
import android.os.Bundle;
import android.widget.TextView;
@@ -42,7 +41,7 @@ public class ActivityLauncher extends Activity {
static final String TAG = "NEWTLauncherActivity";
TextView tv = null;
Method mOnCreate, mOnDestroy, mOnPause, mOnRestart, mOnResume,
- mOnStart, mOnStop, mSetIsInvokedByExternalActivity;
+ mOnStart, mOnStop, mSetRootActivity;
Class<?> activityClazz = null;
Object activityObject = null;
@@ -55,7 +54,7 @@ public class ActivityLauncher extends Activity {
final LauncherUtil.DataSet data = LauncherUtil.DataSet.create(uri);
data.setSystemProperties();
- ClassLoader cl = ClassLoaderUtil.createJogampClassLoaderSingleton(this, data.getPackages());
+ ClassLoader cl = ClassLoaderUtil.createClassLoader(this, data.getPackages(), false);
if(null != cl) {
try {
activityClazz = Class.forName(data.getActivityName(), true, cl);
@@ -69,7 +68,7 @@ public class ActivityLauncher extends Activity {
mOnResume = activityClazz.getMethod("onResume");
mOnStart = activityClazz.getMethod("onStart");
mOnStop = activityClazz.getMethod("onStop");
- mSetIsInvokedByExternalActivity = activityClazz.getMethod("setIsInvokedByExternalActivity", Activity.class);
+ mSetRootActivity = activityClazz.getMethod("setRootActivity", Activity.class);
} catch (Exception e) {
Log.d(TAG, "error: "+e, e);
throw new RuntimeException(e);
@@ -78,13 +77,13 @@ public class ActivityLauncher extends Activity {
if( null == mOnCreate || null == mOnDestroy || null == mOnPause ||
null == mOnRestart || null == mOnResume ||
- null == mSetIsInvokedByExternalActivity ) {
+ null == mSetRootActivity ) {
RuntimeException e = new RuntimeException("XXX - incomplete method set");
Log.d(TAG, "error: "+e, e);
throw e;
}
- callMethod(activityObject, mSetIsInvokedByExternalActivity, this);
+ callMethod(activityObject, mSetRootActivity, this);
callMethod(activityObject, mOnCreate, savedInstanceState);
Log.d(TAG, "onCreate - X");
diff --git a/src/java/jogamp/android/launcher/AssetDexClassLoader.java b/src/java/jogamp/android/launcher/AssetDexClassLoader.java
new file mode 100644
index 0000000..ec3ae6c
--- /dev/null
+++ b/src/java/jogamp/android/launcher/AssetDexClassLoader.java
@@ -0,0 +1,29 @@
+package jogamp.android.launcher;
+
+import java.net.URL;
+
+import android.util.Log;
+
+import dalvik.system.DexClassLoader;
+
+public class AssetDexClassLoader extends DexClassLoader {
+ private static final boolean DEBUG = false;
+ private static final String assets_folder = "assets/";
+
+ public AssetDexClassLoader(String dexPath, String dexOutputDir, String libPath, ClassLoader parent) {
+ super(dexPath, dexOutputDir, libPath, parent);
+ if(DEBUG) {
+ Log.d(AssetDexClassLoader.class.getSimpleName(), "ctor: dexPath " + dexPath + ", dexOutputDir " + dexOutputDir + ", libPath " + libPath + ", parent " + parent);
+ }
+ }
+
+ @Override
+ public URL findResource(String name) {
+ final String assetName = name.startsWith(assets_folder) ? name : assets_folder + name ;
+ URL url = super.findResource(assetName);
+ if(DEBUG) {
+ Log.d(AssetDexClassLoader.class.getSimpleName(), "findResource: " + name + " -> " + assetName + " -> " + url);
+ }
+ return url;
+ }
+}
diff --git a/src/java/jogamp/android/launcher/ClassLoaderUtil.java b/src/java/jogamp/android/launcher/ClassLoaderUtil.java
index 319a0fd..85c0b94 100644
--- a/src/java/jogamp/android/launcher/ClassLoaderUtil.java
+++ b/src/java/jogamp/android/launcher/ClassLoaderUtil.java
@@ -29,89 +29,83 @@
package jogamp.android.launcher;
import java.io.File;
-import java.lang.ref.WeakReference;
-import java.lang.reflect.Field;
-import java.util.HashMap;
+import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
-import android.app.Activity;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.util.Log;
-import dalvik.system.DexClassLoader;
public class ClassLoaderUtil {
private static final String TAG = "JogampClassLoader";
- public static final String packageGlueGen = "com.jogamp.common";
- public static final String packageJogl = "javax.media.opengl"; // FIXME: a 'performance' hack
+ // FIXME: Need to generalize this .. (Note: native lib resources must be cached!)
+ private static final String[] packagesJogAmp = { "com.jogamp.common", "javax.media.opengl" };
+ private static ClassLoader jogAmpClassLoader = null;
- public static final String dexPathName= "jogampDex";
+ // location where optimized dex files will be written
+ private static final String dexPathName= "jogampDex";
+ private static File dexPath = null;
private static LauncherTempFileCache tmpFileCache = null;
- private static ClassLoader jogAmpClassLoader = null;
- /**
- *
- * @param ctx
- * @param userPackageNames list of user package names, the last entry shall reflect the Activity
- * @return
- */
- public static synchronized ClassLoader createJogampClassLoaderSingleton(Context ctx, List<String> userPackageNames) {
- if(null==jogAmpClassLoader) {
- if(null!=tmpFileCache) {
- throw new InternalError("XXX0");
- }
+ private static final String PATH_SEP = "/";
+ private static final String ELEM_SEP = ":";
+
+ private static synchronized void init(Context ctx) {
+ if(null == tmpFileCache) {
if(!LauncherTempFileCache.initSingleton(ctx)) {
throw new InternalError("TempFileCache initialization error");
}
tmpFileCache = new LauncherTempFileCache();
if(!tmpFileCache.isValid()) {
throw new InternalError("TempFileCache instantiation error");
- }
- final ApplicationInfo ai = ctx.getApplicationInfo();
- Log.d(TAG, "S: userPackageName: "+userPackageNames+", dataDir: "+ai.dataDir+", nativeLibraryDir: "+ai.nativeLibraryDir);
-
- final String appDir = new File(ai.dataDir).getParent();
- final String libSub = ai.nativeLibraryDir.substring(ai.nativeLibraryDir.lastIndexOf('/')+1);
- Log.d(TAG, "S: appDir: "+appDir+", libSub: "+libSub);
-
- final String libPathName = appDir + "/" + packageGlueGen + "/" + libSub + "/:" +
- appDir + "/" + packageJogl + "/" + libSub + "/" ;
- Log.d(TAG, "S: libPath: "+libPathName);
-
- String apkGlueGen = null;
- String apkJogl = null;
-
- try {
- apkGlueGen = ctx.getPackageManager().getApplicationInfo(packageGlueGen,0).sourceDir;
- apkJogl = ctx.getPackageManager().getApplicationInfo(packageJogl,0).sourceDir;
- } catch (PackageManager.NameNotFoundException e) {
- Log.d(TAG, "error: "+e, e);
- }
- if(null == apkGlueGen || null == apkJogl) {
- Log.d(TAG, "not found: gluegen <"+apkGlueGen+">, jogl <"+apkJogl+">");
- return null;
- }
-
- final String cp = apkGlueGen + ":" + apkJogl ;
- Log.d(TAG, "jogamp cp: " + cp);
-
- final File dexPath = new File(tmpFileCache.getTempDir(), dexPathName);
+ }
+ dexPath = new File(tmpFileCache.getTempDir(), dexPathName);
Log.d(TAG, "jogamp dexPath: " + dexPath.getAbsolutePath());
dexPath.mkdir();
- jogAmpClassLoader = new DexClassLoader(cp, dexPath.getAbsolutePath(), libPathName, ctx.getClassLoader());
- } else {
- if(null==tmpFileCache) {
- throw new InternalError("XXX1");
- }
+ }
+ }
+
+ public static synchronized ClassLoader createClassLoader(Context ctx, List<String> userPackageNames, boolean addLibPath) {
+ return createClassLoader(ctx, userPackageNames, addLibPath, null);
+ }
+
+ public static synchronized ClassLoader createClassLoader(Context ctx, List<String> userPackageNames,
+ boolean addLibPath, ClassLoader parent) {
+ init(ctx);
+
+ if(null==jogAmpClassLoader) {
+ jogAmpClassLoader = createClassLoaderImpl(ctx, Arrays.asList(packagesJogAmp), true,
+ (null != parent ) ? parent : ctx.getClassLoader());
}
+ parent = jogAmpClassLoader;
- StringBuilder userAPKs = new StringBuilder();
- int numUserAPKs = 0;
+ return createClassLoaderImpl(ctx, userPackageNames, addLibPath, jogAmpClassLoader);
+ }
+
+ /**
+ *
+ * @param ctx
+ * @param userPackageNames list of user package names, the last entry shall reflect the Activity
+ * @return
+ */
+ private static synchronized ClassLoader createClassLoaderImpl(Context ctx, List<String> userPackageNames,
+ boolean addLibPath, ClassLoader parent) {
+
+
+ final ApplicationInfo appInfo = ctx.getApplicationInfo();
+ final String appDir = new File(appInfo.dataDir).getParent();
+ final String libSub = appInfo.nativeLibraryDir.substring(appInfo.nativeLibraryDir.lastIndexOf('/')+1);
+ Log.d(TAG, "S: userPackageName: "+userPackageNames+"; appName "+appInfo.name+", appDir "+appDir+", nativeLibraryDir: "+appInfo.nativeLibraryDir+"; dataDir: "+appInfo.dataDir+", libSub "+libSub);
+
+ StringBuilder apks = new StringBuilder();
+ StringBuilder libs = new StringBuilder();
+ int apkCount = 0;
String lastUserPackageName = null; // the very last one reflects the Activity
+
for(Iterator<String> i=userPackageNames.iterator(); i.hasNext(); ) {
lastUserPackageName = i.next();
String userAPK = null;
@@ -121,32 +115,33 @@ public class ClassLoaderUtil {
Log.d(TAG, "error: "+e, e);
}
if(null != userAPK) {
- numUserAPKs++;
- if(numUserAPKs>0) {
- userAPKs.append(":");
+ if(apkCount>0) {
+ apks.append(ELEM_SEP);
+ if(addLibPath) {
+ libs.append(ELEM_SEP);
+ }
+ }
+ apks.append(userAPK);
+ if(addLibPath) {
+ libs.append(appDir).append(PATH_SEP).append(lastUserPackageName).append(PATH_SEP).append(libSub).append(PATH_SEP);
}
- userAPKs.append(userAPK);
Log.d(TAG, "APK found: <"+lastUserPackageName+"> -> <"+userAPK+">");
+ apkCount++;
} else {
Log.d(TAG, "APK not found: <"+lastUserPackageName+">");
}
}
- if( userPackageNames.size()!=numUserAPKs ) {
+ if( userPackageNames.size()!=apkCount ) {
Log.d(TAG, "APKs incomplete, abort");
return null;
}
- final String userAPKs_s = userAPKs.toString();
- Log.d(TAG, "user cp: " + userAPKs_s);
- final File dexPath = new File(tmpFileCache.getTempDir(), lastUserPackageName);
- Log.d(TAG, "user dexPath: " + dexPath.getAbsolutePath());
- dexPath.mkdir();
- ClassLoader cl = new DexClassLoader(userAPKs_s, dexPath.getAbsolutePath(), null, jogAmpClassLoader);
- Log.d(TAG, "cl: " + cl);
-
- return cl;
+ // return new TraceDexClassLoader(apks.toString(), dexPath.getAbsolutePath(), libs.toString(), parent);
+ return new AssetDexClassLoader(apks.toString(), dexPath.getAbsolutePath(), libs.toString(), parent);
}
+ /***
+ *
public boolean setAPKClassLoader(String activityPackageName, ClassLoader classLoader)
{
try {
@@ -188,6 +183,7 @@ public class ClassLoaderUtil {
}
}
return null;
- }
+ }
+ */
}
diff --git a/src/java/jogamp/android/launcher/LauncherMain.java b/src/java/jogamp/android/launcher/LauncherMain.java
index bbdee1d..eb56385 100644
--- a/src/java/jogamp/android/launcher/LauncherMain.java
+++ b/src/java/jogamp/android/launcher/LauncherMain.java
@@ -51,7 +51,7 @@ public class LauncherMain {
userPackageNames.add("com.jogamp.opengl.test");
Looper.prepareMainLooper();
ActivityGroup activityGroup = new ActivityGroup(true);
- ClassLoader cl = ClassLoaderUtil.createJogampClassLoaderSingleton(activityGroup, getUserPackageNames());
+ ClassLoader cl = ClassLoaderUtil.createClassLoader(activityGroup, getUserPackageNames(), false);
if(null != cl) {
Class<?> activityClazz = Class.forName(getUserActivityName(), true, cl);
Intent intent = new Intent(activityGroup, activityClazz);
diff --git a/src/java/jogamp/android/launcher/TraceDexClassLoader.java b/src/java/jogamp/android/launcher/TraceDexClassLoader.java
new file mode 100644
index 0000000..0b00489
--- /dev/null
+++ b/src/java/jogamp/android/launcher/TraceDexClassLoader.java
@@ -0,0 +1,27 @@
+package jogamp.android.launcher;
+
+import java.net.URL;
+
+import android.util.Log;
+
+import dalvik.system.DexClassLoader;
+
+public class TraceDexClassLoader extends DexClassLoader {
+ private static final boolean DEBUG = false;
+
+ public TraceDexClassLoader(String dexPath, String dexOutputDir, String libPath, ClassLoader parent) {
+ super(dexPath, dexOutputDir, libPath, parent);
+ if(DEBUG) {
+ Log.d(TraceDexClassLoader.class.getSimpleName(), "ctor: dexPath " + dexPath + ", dexOutputDir " + dexOutputDir + ", libPath " + libPath + ", parent " + parent);
+ }
+ }
+
+ @Override
+ public URL findResource(String name) {
+ final URL url = super.findResource(name);
+ if(DEBUG) {
+ Log.d(TraceDexClassLoader.class.getSimpleName(), "findResource: " + name + " -> " + url);
+ }
+ return url;
+ }
+}
diff --git a/src/java/jogamp/common/os/android/GluegenVersionActivity.java b/src/java/jogamp/common/os/android/GluegenVersionActivity.java
index 8ea10e2..5923e66 100644
--- a/src/java/jogamp/common/os/android/GluegenVersionActivity.java
+++ b/src/java/jogamp/common/os/android/GluegenVersionActivity.java
@@ -27,6 +27,7 @@
*/
package jogamp.common.os.android;
+
import com.jogamp.common.GlueGenVersion;
import com.jogamp.common.os.Platform;
import com.jogamp.common.util.VersionUtil;
@@ -43,7 +44,7 @@ public class GluegenVersionActivity extends Activity {
public void onCreate(Bundle savedInstanceState) {
Log.d(MD.TAG, "onCreate - S");
super.onCreate(savedInstanceState);
- StaticContext.setContext(this.getApplicationContext());
+ StaticContext.init(this.getApplicationContext());
tv = new TextView(this);
tv.setText(VersionUtil.getPlatformInfo()+Platform.NEWLINE+GlueGenVersion.getInstance()+Platform.NEWLINE+Platform.NEWLINE);
setContentView(tv);
@@ -109,7 +110,7 @@ public class GluegenVersionActivity extends Activity {
tv.append("> destroyed"+Platform.NEWLINE);
}
Log.d(MD.TAG, "onDestroy - x");
- StaticContext.setContext(null);
+ StaticContext.clear();
super.onDestroy();
Log.d(MD.TAG, "onDestroy - X");
}
diff --git a/src/java/jogamp/common/os/android/StaticContext.java b/src/java/jogamp/common/os/android/StaticContext.java
index 56f8f13..cb55e65 100644
--- a/src/java/jogamp/common/os/android/StaticContext.java
+++ b/src/java/jogamp/common/os/android/StaticContext.java
@@ -32,13 +32,21 @@ import android.util.Log;
public class StaticContext {
private static Context context = null;
+
private static boolean DEBUG = false;
- public static final synchronized void setContext(Context ctx) {
- if(DEBUG) Log.d(MD.TAG, "setContext("+ctx+")");
+ public static final synchronized void init(Context ctx) {
+ if(null != context) {
+ throw new RuntimeException("Context already set");
+ }
+ if(DEBUG) { Log.d(MD.TAG, "init("+ctx+")"); }
context = ctx;
}
+ public static final synchronized void clear() {
+ if(DEBUG) { Log.d(MD.TAG, "clear()"); }
+ context = null;
+ }
public static final synchronized Context getContext() {
return context;
- }
+ }
}