diff options
author | Sven Gothel <[email protected]> | 2012-03-17 21:15:49 +0100 |
---|---|---|
committer | Sven Gothel <[email protected]> | 2012-03-17 21:15:49 +0100 |
commit | 235f8b1cbff8ed13071d5c19c0be492c0b25cb78 (patch) | |
tree | 659845e16bd69372bc7ddc3a42b3aa7130d18df5 /src/java/jogamp/android/launcher | |
parent | 0cfc7847c58b51c9a26b50d905b592d1fc4c8578 (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/android/launcher')
5 files changed, 130 insertions, 79 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; + } +} |