From b47d0d92dd222999bf38633de1cec8de6a7ad369 Mon Sep 17 00:00:00 2001
From: Sven Gothel <sgothel@jausoft.com>
Date: Thu, 31 Jan 2013 21:15:32 +0100
Subject: 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.
---
 .../jogamp/android/launcher/ClassLoaderUtil.java   | 70 ++++++++++++++--------
 1 file changed, 46 insertions(+), 24 deletions(-)

(limited to 'src/java/jogamp/android/launcher/ClassLoaderUtil.java')

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;
            }
-- 
cgit v1.2.3