diff options
author | Sven Gothel <[email protected]> | 2013-01-31 21:15:32 +0100 |
---|---|---|
committer | Sven Gothel <[email protected]> | 2013-01-31 21:15:32 +0100 |
commit | b47d0d92dd222999bf38633de1cec8de6a7ad369 (patch) | |
tree | 764afc7741b6cb5cf3a11f5d4a8d3c117b3b5f0e | |
parent | c8de8fbf5d080b674b509763fbb7374c21ee705b (diff) |
Android: Cleanup ClassLoaderUtil/LauncherUtil - Using cached parent ClassLoader for SYS-Packages w/ native libs, and non cached child ClassLoader for USR-Packages
Android's Dalvik VM, like a JVM, cannot load a native library from one location by multiple ClassLoader.
Since we don't like to hardcode the system-packages, as it was before, i.e. "com.jogamp.common", "javax.media.opengl",
we need to either copy the libs or use parenting of cached ClassLoader.
The latter is chosen, since it's faster and uses less resources.
- System-packages are passed through from the user 'List<String> LauncherUtil.BaseActivityLauncher::getSysPackages()'
to the ActivityLauncher, which instantiates the ClassLoader.
- No more hard-reference the system-packages in ClassLoaderUtil ("com.jogamp.common", "javax.media.opengl"),
just use the new user provided system-packages.
- The system-packages denominate a hash-key for caching, a new ClassLoader is created and mapped
if it does not yet exist.
- A non-chached user-packages ClassLoader is created using the cached system-packages ClassLoader as it's parent.
-rw-r--r-- | make/scripts/adb-launch-main.sh | 32 | ||||
-rw-r--r-- | src/java/jogamp/android/launcher/ActivityLauncher.java | 14 | ||||
-rw-r--r-- | src/java/jogamp/android/launcher/ClassLoaderUtil.java | 70 | ||||
-rw-r--r-- | src/java/jogamp/android/launcher/LauncherUtil.java | 128 | ||||
-rw-r--r-- | src/java/jogamp/android/launcher/MainLauncher.java | 2 |
5 files changed, 173 insertions, 73 deletions
diff --git a/make/scripts/adb-launch-main.sh b/make/scripts/adb-launch-main.sh index 1a1c977..d032e19 100644 --- a/make/scripts/adb-launch-main.sh +++ b/make/scripts/adb-launch-main.sh @@ -8,10 +8,18 @@ export HOST_RSYNC_ROOT=PROJECTS/JOGL export TARGET_UID=jogamp #export TARGET_IP=panda02 -export TARGET_IP=jautab03 -export TARGET_ADB_PORT=5555 +#export TARGET_IP=jautab03 +export TARGET_IP=C5OKCT139647 +#export TARGET_ADB_PORT=5555 +export TARGET_ADB_PORT= export TARGET_ROOT=jogamp-test +if [ -z "$TARGET_ADB_PORT" ] ; then + export TARGET_IP_PORT=$TARGET_IP +else + export TARGET_IP_PORT=$TARGET_IP:$TARGET_ADB_PORT +fi + export BUILD_DIR=../build-android-armv6 if [ -e /opt-linux-x86/android-sdk-linux_x86 ] ; then @@ -19,16 +27,16 @@ if [ -e /opt-linux-x86/android-sdk-linux_x86 ] ; then export PATH=$ANDROID_HOME/platform-tools:$PATH fi -TSTCLASS=com.jogamp.common.GlueGenVersion -#TSTCLASS=jogamp.android.launcher.LauncherUtil +#TSTCLASS=com.jogamp.common.GlueGenVersion +TSTCLASS=jogamp.android.launcher.LauncherUtil LOGFILE=`basename $0 .sh`.log -#adb -s $TARGET_IP:$TARGET_ADB_PORT uninstall jogamp.android.launcher -#adb -s $TARGET_IP:$TARGET_ADB_PORT install $BUILD_DIR/jogamp-android-launcher.apk +#adb -s $TARGET_IP_PORT uninstall jogamp.android.launcher +#adb -s $TARGET_IP_PORT install $BUILD_DIR/jogamp-android-launcher.apk -#adb -s $TARGET_IP:$TARGET_ADB_PORT uninstall com.jogamp.common -#adb -s $TARGET_IP:$TARGET_ADB_PORT install $BUILD_DIR/gluegen-rt-android-armeabi.apk +#adb -s $TARGET_IP_PORT uninstall com.jogamp.common +#adb -s $TARGET_IP_PORT install $BUILD_DIR/gluegen-rt-android-armeabi.apk SHELL_CMD="\ cd /sdcard ; \ @@ -39,8 +47,8 @@ am kill-all ; \ am start -W -S -a android.intent.action.MAIN -n jogamp.android.launcher/jogamp.android.launcher.MainLauncher -d launch://jogamp.org/$TSTCLASS/?pkg=com.jogamp.common \ " -adb connect $TARGET_IP:$TARGET_ADB_PORT -adb -s $TARGET_IP:$TARGET_ADB_PORT logcat -c -adb -s $TARGET_IP:$TARGET_ADB_PORT shell $SHELL_CMD 2>&1 | tee $LOGFILE -adb -s $TARGET_IP:$TARGET_ADB_PORT logcat -d 2>&1 | tee -a $LOGFILE +adb connect $TARGET_IP_PORT +adb -s $TARGET_IP_PORT logcat -c +adb -s $TARGET_IP_PORT shell $SHELL_CMD 2>&1 | tee $LOGFILE +adb -s $TARGET_IP_PORT logcat -d 2>&1 | tee -a $LOGFILE diff --git a/src/java/jogamp/android/launcher/ActivityLauncher.java b/src/java/jogamp/android/launcher/ActivityLauncher.java index 0ac940e..6e2f261 100644 --- a/src/java/jogamp/android/launcher/ActivityLauncher.java +++ b/src/java/jogamp/android/launcher/ActivityLauncher.java @@ -55,7 +55,7 @@ public class ActivityLauncher extends Activity { data = LauncherUtil.DataSet.create(uri); data.setSystemProperties(); - ClassLoader cl = ClassLoaderUtil.createClassLoader(this, data.getPackages(), false, null); + ClassLoader cl = ClassLoaderUtil.createClassLoader(this, data.getSysPackages(), data.getUsrPackages(), null); if(null != cl) { try { activityClazz = Class.forName(data.getActivityName(), true, cl); @@ -135,8 +135,18 @@ public class ActivityLauncher extends Activity { Log.d(TAG, "onDestroy - S"); callMethod(activityObject, mOnDestroy); if(null != data) { + activityObject=null; + mOnCreate=null; + mOnDestroy=null; + mOnPause=null; + mOnRestart=null; + mOnResume=null; + mOnStart=null; + mOnStop=null; + mSetRootActivity=null; + activityClazz = null; data.clearSystemProperties(); - data = null; + data = null; } super.onDestroy(); Log.d(TAG, "onDestroy - X"); diff --git a/src/java/jogamp/android/launcher/ClassLoaderUtil.java b/src/java/jogamp/android/launcher/ClassLoaderUtil.java index ae2918f..76dbf53 100644 --- a/src/java/jogamp/android/launcher/ClassLoaderUtil.java +++ b/src/java/jogamp/android/launcher/ClassLoaderUtil.java @@ -29,7 +29,7 @@ package jogamp.android.launcher; import java.io.File; -import java.util.Arrays; +import java.util.HashMap; import java.util.Iterator; import java.util.List; @@ -41,15 +41,13 @@ import android.util.Log; public class ClassLoaderUtil { private static final String TAG = "JogampClassLoader"; - // 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; + private static HashMap<String, ClassLoader> cachedClassLoader = new HashMap<String, ClassLoader>(); // 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 LauncherTempFileCache tmpFileCache = null; private static final String PATH_SEP = "/"; private static final String ELEM_SEP = ":"; @@ -69,45 +67,69 @@ public class ClassLoaderUtil { } } - public static synchronized ClassLoader createClassLoader(Context ctx, List<String> userPackageNames, boolean addUserLibPath, - List<String> apkNames) { - return createClassLoader(ctx, userPackageNames, addUserLibPath, apkNames, null); + /** + * @param ctx + * @param cachedPackageName list of package names w/ native libraries for which a ClassLoader shall be cached, i.e. persistence. + * This is usually required for native libraries. + * @param userPackageNames list of user package names w/o native libraries, the last entry shall reflect the Activity. + * @param userAPKNames optional non-cached user APKs + * @return + */ + public static synchronized ClassLoader createClassLoader(Context ctx, List<String> cachedPackageName, + List<String> userPackageNames, List<String> userAPKNames) { + return createClassLoader(ctx, cachedPackageName, userPackageNames, userAPKNames, null); } - public static synchronized ClassLoader createClassLoader(Context ctx, List<String> userPackageNames, boolean addUserLibPath, - List<String> apkNames, ClassLoader parent) { + /** + * @param ctx + * @param cachedPackageNames list of package names w/ native libraries for which a ClassLoader shall be cached, i.e. persistence. + * This is usually required for native libraries. + * @param userPackageNames list of user package names w/o native libraries, the last entry shall reflect the Activity. + * @param userAPKNames optional non-cached user APKs + * @param parent + * @return + */ + public static synchronized ClassLoader createClassLoader(Context ctx, List<String> cachedPackageNames, + List<String> userPackageNames, List<String> userAPKNames, + ClassLoader parent) { init(ctx); - if(null==jogAmpClassLoader) { - jogAmpClassLoader = createClassLoaderImpl(ctx, Arrays.asList(packagesJogAmp), true, null, - (null != parent ) ? parent : ctx.getClassLoader()); + final String cacheKey = cachedPackageNames.toString(); + ClassLoader clCached = cachedClassLoader.get(cacheKey); + if( null == clCached ) { + clCached = createClassLoaderImpl(ctx, cachedPackageNames, true, null, (null != parent ) ? parent : ctx.getClassLoader()); + cachedClassLoader.put(cacheKey, clCached); + Log.d(TAG, "NEW cached-CL: cachedPackageNames: "+cacheKey); + } else { + Log.d(TAG, "REUSE cached-CL: cachedPackageNames: "+cacheKey); } - parent = jogAmpClassLoader; - return createClassLoaderImpl(ctx, userPackageNames, addUserLibPath, apkNames, jogAmpClassLoader); + return createClassLoaderImpl(ctx, userPackageNames, false, userAPKNames, clCached); } /** - * * @param ctx - * @param userPackageNames list of user package names, the last entry shall reflect the Activity + * @param packageNames list of package names + * @param addLibPath + * @param apkNames + * @param parent * @return */ - private static synchronized ClassLoader createClassLoaderImpl(Context ctx, List<String> userPackageNames, boolean addUserLibPath, + private static synchronized ClassLoader createClassLoaderImpl(Context ctx, List<String> packageNames, boolean addLibPath, List<String> apkNames, ClassLoader parent) { final ApplicationInfo appInfoLauncher= ctx.getApplicationInfo(); final String appDirLauncher = new File(appInfoLauncher.dataDir).getParent(); final String libSubDef = "lib"; - Log.d(TAG, "S: userPackageNames: "+userPackageNames+"; Launcher: appDir "+appDirLauncher+", dataDir: "+appInfoLauncher.dataDir+", nativeLibraryDir "+appInfoLauncher.nativeLibraryDir); + Log.d(TAG, "S: userPackageNames: "+packageNames+"; Launcher: appDir "+appDirLauncher+", dataDir: "+appInfoLauncher.dataDir+", nativeLibraryDir "+appInfoLauncher.nativeLibraryDir); final StringBuilder apks = new StringBuilder(); final StringBuilder libs = new StringBuilder(); int apkCount = 0; String lastUserPackageName = null; // the very last one reflects the Activity - if( null != userPackageNames ) { - for(Iterator<String> i=userPackageNames.iterator(); i.hasNext(); ) { + if( null != packageNames ) { + for(Iterator<String> i=packageNames.iterator(); i.hasNext(); ) { lastUserPackageName = i.next(); String userAPK = null; @@ -125,14 +147,14 @@ public class ClassLoaderUtil { if(null != userAPK) { if(apkCount>0) { apks.append(ELEM_SEP); - if(addUserLibPath) { + if(addLibPath) { libs.append(ELEM_SEP); } } apks.append(userAPK); Log.d(TAG, "APK["+apkCount+"] found: <"+lastUserPackageName+"> -> <"+userAPK+">"); Log.d(TAG, "APK["+apkCount+"] apks: <"+apks.toString()+">"); - if(addUserLibPath) { + if(addLibPath) { if(null != nativeLibraryDir && nativeLibraryDir.length()>0 ) { libs.append(nativeLibraryDir).append(PATH_SEP); } else { @@ -145,7 +167,7 @@ public class ClassLoaderUtil { Log.d(TAG, "APK not found: <"+lastUserPackageName+">"); } } - if( userPackageNames.size() != apkCount ) { + if( packageNames.size() != apkCount ) { Log.d(TAG, "User APKs incomplete, abort (1)"); return null; } diff --git a/src/java/jogamp/android/launcher/LauncherUtil.java b/src/java/jogamp/android/launcher/LauncherUtil.java index b68768b..1dfc218 100644 --- a/src/java/jogamp/android/launcher/LauncherUtil.java +++ b/src/java/jogamp/android/launcher/LauncherUtil.java @@ -65,7 +65,9 @@ public class LauncherUtil { /** The host <code>jogamp.org</code> */ public static final String HOST = "jogamp.org"; - static final String PKG = "pkg"; + static final String SYS_PKG = "sys"; + + static final String USR_PKG = "pkg"; static final String ARG = "arg"; @@ -105,8 +107,11 @@ public class LauncherUtil { /** Must return the downstream Activity class name */ public abstract String getActivityName(); - /** Must return a list of required packages, at least one. */ - public abstract List<String> getPackages(); + /** Must return a list of required user packages, at least one containing the activity. */ + public abstract List<String> getUsrPackages(); + + /** Return a list of required system packages w/ native libraries, may return null or a zero sized list. */ + public abstract List<String> getSysPackages(); @Override public void onCreate(Bundle savedInstanceState) { @@ -116,7 +121,8 @@ public class LauncherUtil { final DataSet data = new DataSet(); data.setActivityName(getActivityName()); - data.addAllPackages(getPackages()); + data.addAllSysPackages(getSysPackages()); + data.addAllUsrPackages(getUsrPackages()); data.addAllProperties(props); data.addAllArguments(args); @@ -135,8 +141,11 @@ public class LauncherUtil { ArrayList<String> keyList = new ArrayList<String>(); public final void setProperty(String key, String value) { - if(key.equals(PKG)) { - throw new IllegalArgumentException("Illegal property key, '"+PKG+"' is reserved"); + if(key.equals(SYS_PKG)) { + throw new IllegalArgumentException("Illegal property key, '"+SYS_PKG+"' is reserved"); + } + if(key.equals(USR_PKG)) { + throw new IllegalArgumentException("Illegal property key, '"+USR_PKG+"' is reserved"); } if(key.equals(ARG)) { throw new IllegalArgumentException("Illegal property key, '"+ARG+"' is reserved"); @@ -178,6 +187,17 @@ public class LauncherUtil { public final List<String> getPropertyKeys() { return keyList; } } + /** + * Data set to transfer from and to launch URI consisting out of: + * <ul> + * <li>system packages w/ native libraries used on Android, which may use a cached ClassLoader, see {@link DataSet#getSysPackages()}.</li> + * <li>user packages w/o native libraries used on Android, which do not use a cached ClassLoader, see {@link DataSet#getUsrPackages()}.</li> + * <li>activity name, used to launch an Android activity, see {@link DataSet#getActivityName()}.</li> + * <li>properties, which will be added to the system properties, see {@link DataSet#getProperties()}.</li> + * <li>arguments, used to launch a class main-entry, see {@link DataSet#getArguments()}.</li> + * </ul> + * {@link DataSet#getUri()} returns a URI representation of all components. + */ public static class DataSet { static final char SLASH = '/'; static final char QMARK = '?'; @@ -187,20 +207,29 @@ public class LauncherUtil { static final String EMPTY = ""; String activityName = null; - ArrayList<String> packages = new ArrayList<String>(); + ArrayList<String> sysPackages = new ArrayList<String>(); + ArrayList<String> usrPackages = new ArrayList<String>(); OrderedProperties properties = new OrderedProperties(); ArrayList<String> arguments = new ArrayList<String>(); public final void setActivityName(String name) { activityName = name; } public final String getActivityName() { return activityName; } + + public final void addSysPackage(String p) { + sysPackages.add(p); + } + public final void addAllSysPackages(List<String> plist) { + sysPackages.addAll(plist); + } + public final List<String> getSysPackages() { return sysPackages; } - public final void addPackage(String p) { - packages.add(p); + public final void addUsrPackage(String p) { + usrPackages.add(p); } - public final void addAllPackages(List<String> plist) { - packages.addAll(plist); + public final void addAllUsrPackages(List<String> plist) { + usrPackages.addAll(plist); } - public final List<String> getPackages() { return packages; } + public final List<String> getUsrPackages() { return usrPackages; } public final void setProperty(String key, String value) { properties.setProperty(key, value); @@ -227,33 +256,58 @@ public class LauncherUtil { public final Uri getUri() { StringBuilder sb = new StringBuilder(); sb.append(SCHEME).append(COLSLASH2).append(HOST).append(SLASH).append(getActivityName()); + boolean needsQMark = true; boolean needsSep = false; - if(packages.size()>0) { - sb.append(QMARK); - for(int i=0; i<packages.size(); i++) { + if(sysPackages.size()>0) { + if( needsQMark ) { + sb.append(QMARK); + needsQMark = false; + } + for(int i=0; i<sysPackages.size(); i++) { if(needsSep) { sb.append(AMPER); } - sb.append(PKG).append(ASSIG).append(packages.get(i)); + sb.append(SYS_PKG).append(ASSIG).append(sysPackages.get(i)); needsSep = true; } } - Iterator<String> propKeys = properties.keyList.iterator(); - while(propKeys.hasNext()) { + if(usrPackages.size()>0) { + if( needsQMark ) { + sb.append(QMARK); + needsQMark = false; + } + for(int i=0; i<usrPackages.size(); i++) { if(needsSep) { sb.append(AMPER); } - final String key = propKeys.next(); - sb.append(key).append(ASSIG).append(properties.map.get(key)); + sb.append(USR_PKG).append(ASSIG).append(usrPackages.get(i)); needsSep = true; + } + } + Iterator<String> propKeys = properties.keyList.iterator(); + while(propKeys.hasNext()) { + if( needsQMark ) { + sb.append(QMARK); + needsQMark = false; + } + if(needsSep) { + sb.append(AMPER); + } + final String key = propKeys.next(); + sb.append(key).append(ASSIG).append(properties.map.get(key)); + needsSep = true; } Iterator<String> args = arguments.iterator(); while(args.hasNext()) { - if(needsSep) { - sb.append(AMPER); - } - sb.append(ARG).append(ASSIG).append(args.next()); - needsSep = true; + if( needsQMark ) { + sb.append(QMARK); + needsQMark = false; + } + if(needsSep) { + sb.append(AMPER); + } + sb.append(ARG).append(ASSIG).append(args.next()); + needsSep = true; } return Uri.parse(sb.toString()); } @@ -298,11 +352,16 @@ public class LauncherUtil { // assignment final String k = part.substring(0, assignment); final String v = part.substring(assignment+1); - if(k.equals(PKG)) { + if(k.equals(SYS_PKG)) { + if(v.length()==0) { + throw new IllegalArgumentException("Empty package name: part <"+part+">, query <"+q+"> of "+uri); + } + data.addSysPackage(v); + } else if(k.equals(USR_PKG)) { if(v.length()==0) { throw new IllegalArgumentException("Empty package name: part <"+part+">, query <"+q+"> of "+uri); } - data.addPackage(v); + data.addUsrPackage(v); } else if(k.equals(ARG)) { if(v.length()==0) { throw new IllegalArgumentException("Empty argument name: part <"+part+">, query <"+q+"> of "+uri); @@ -313,7 +372,7 @@ public class LauncherUtil { } } else { // property key only - if( part.equals(PKG) || part.equals(ARG) ) { + if( part.equals(USR_PKG) || part.equals(ARG) ) { throw new IllegalArgumentException("Reserved key <"+part+"> in query <"+q+"> of "+uri); } data.setProperty(part, EMPTY); @@ -338,12 +397,13 @@ public class LauncherUtil { public static void main(String[] args) { if(args.length==0) { args = new String[] { - SCHEME+"://"+HOST+"/com.jogamp.TestActivity?"+PKG+"=jogamp.pack1&"+PKG+"=javax.pack2&"+PKG+"=com.jogamp.pack3&jogamp.common.debug=true&com.jogamp.test=false", - SCHEME+"://"+HOST+"/com.jogamp.TestActivity?"+PKG+"=jogamp.pack1&jogamp.common.debug=true&com.jogamp.test=false", - SCHEME+"://"+HOST+"/com.jogamp.TestActivity?"+PKG+"=jogamp.pack1", - SCHEME+"://"+HOST+"/com.jogamp.TestActivity?"+PKG+"=jogamp.pack1&"+PKG+"=javax.pack2&"+PKG+"=com.jogamp.pack3&jogamp.common.debug=true&com.jogamp.test=false&"+ARG+"=arg1&"+ARG+"=arg2=arg2value&"+ARG+"=arg3", - SCHEME+"://"+HOST+"/com.jogamp.TestActivity?"+PKG+"=jogamp.pack1&jogamp.common.debug=true&com.jogamp.test=false&"+ARG+"=arg1&"+ARG+"=arg2=arg2value&"+ARG+"=arg3", - SCHEME+"://"+HOST+"/com.jogamp.TestActivity?"+PKG+"=jogamp.pack1&"+ARG+"=arg1&"+ARG+"=arg2=arg2value&"+ARG+"=arg3" + SCHEME+"://"+HOST+"/com.jogamp.TestActivity?"+SYS_PKG+"=jogamp.pack1&"+SYS_PKG+"=javax.pack2&"+USR_PKG+"=com.jogamp.pack3&"+USR_PKG+"=com.jogamp.pack4&jogamp.common.debug=true&com.jogamp.test=false", + SCHEME+"://"+HOST+"/com.jogamp.TestActivity?"+SYS_PKG+"=jogamp.pack1&jogamp.common.debug=true&com.jogamp.test=false", + SCHEME+"://"+HOST+"/com.jogamp.TestActivity?"+USR_PKG+"=jogamp.pack1&jogamp.common.debug=true&com.jogamp.test=false", + SCHEME+"://"+HOST+"/com.jogamp.TestActivity?"+USR_PKG+"=jogamp.pack1&"+USR_PKG+"=com.jogamp.pack2", + SCHEME+"://"+HOST+"/com.jogamp.TestActivity?"+USR_PKG+"=jogamp.pack1&"+USR_PKG+"=javax.pack2&"+USR_PKG+"=com.jogamp.pack3&jogamp.common.debug=true&com.jogamp.test=false&"+ARG+"=arg1&"+ARG+"=arg2=arg2value&"+ARG+"=arg3", + SCHEME+"://"+HOST+"/com.jogamp.TestActivity?"+USR_PKG+"=jogamp.pack1&jogamp.common.debug=true&com.jogamp.test=false&"+ARG+"=arg1&"+ARG+"=arg2=arg2value&"+ARG+"=arg3", + SCHEME+"://"+HOST+"/com.jogamp.TestActivity?"+USR_PKG+"=jogamp.pack1&"+ARG+"=arg1&"+ARG+"=arg2=arg2value&"+ARG+"=arg3" }; } int errors = 0; diff --git a/src/java/jogamp/android/launcher/MainLauncher.java b/src/java/jogamp/android/launcher/MainLauncher.java index d4ac4ca..f37fa57 100644 --- a/src/java/jogamp/android/launcher/MainLauncher.java +++ b/src/java/jogamp/android/launcher/MainLauncher.java @@ -64,7 +64,7 @@ public class MainLauncher extends Activity { data = LauncherUtil.DataSet.create(uri); data.setSystemProperties(); - ClassLoader cl = ClassLoaderUtil.createClassLoader(this, data.getPackages(), false, Arrays.asList(frameworkAPKs)); + ClassLoader cl = ClassLoaderUtil.createClassLoader(this, data.getSysPackages(), data.getUsrPackages(), Arrays.asList(frameworkAPKs)); if(null != cl) { try { staticContextClazz = Class.forName("jogamp.common.os.android.StaticContext", true, cl); |