0

I have a library that loads classes from it's jar dynamically but has to do so with the ClassLoader provided by the "context" object for the application it is packaged with and being called from. Is there a way to get the context or class loader from within a process from the library code with no direct reference passed in to the library call?

I guess what I am looking to do is this:

class SomeLibraryClass {

    static final boolean isAndroid_;

    static {
        if (System.getProperty("java.vm.name").equalsIgnoreCase("Dalvik")) {
            isAndroid_ = true;
        } else {
            isAndroid_ = false;
        }
    }

    static ClassLoader getClassLoader() {
         if(isAndroid_) {
             return(getClassLoaderForVMProcessAppContext());
         }    
         return(getClassLoaderForUnixWindowsMacJavaProcess());
    }

    private static ClassLoader getClassLoaderForVMProcessAppContext() {
        /* do something here that requires no static linkage, imports
         * etc to any android specific classes or jars to get 
         * class loader for the current process' application context. 
         * so this library can be used on all platforms
         */
    }
}
peterk
  • 5,136
  • 6
  • 33
  • 47
  • There are several ugly ones: http://stackoverflow.com/questions/2002288/static-way-to-get-context-on-android – zapl Aug 28 '15 at 23:37
  • What class loader is used inside your library? – Raffaele Aug 28 '15 at 23:43
  • any class loader that will load any class available to the application context available in the current process. Only the class name is available to the library until it is loaded. The proper loader seems to be the one available from the application/package context. – peterk Aug 29 '15 at 08:06
  • What is the point of dynamically loading classes from a library's own jar? – Kevin Krumwiede Aug 29 '15 at 19:52
  • partly to keep them out of ram if not needed and also to allow loading of classes from the other jars included in the application bundle. The purpose is for it to dynamically load special sister or inner classes that may or may not be present for other classes in the system on demend for special operations - the name is synthesized from the class name of the related class and extracted from a special package. To enable these "extended" features related to an object one creates this special sister class and puts it in the app. The point here of course is how not why :) – peterk Aug 29 '15 at 23:37
  • The "why" often suggests a better approach. Given these details, I still don't see why you specifically need the loader from the application `Context`. A normal `ClassLoader` delegates to its parent before trying to load a class itself, meaning that you can obtain a loader from *any* class and use it with the same or better results than you'd get from any of its ancestors. – Kevin Krumwiede Aug 30 '15 at 02:44
  • the normal class loader will not load the classes from packages jar files, If I create and use a "PathLoader" it will load the subclass class but if the class extends a base class that is imported at load time, it is imported with the application's loader so when attempting to cast it to the local base class (from the application's loader) it throws a ClassCastException. These loaders operate in different "class spaces" Theloader that is needed is the one retrieved by Context.getClassLoader(); It seems to be what the android OS requires. The android security/VM/Process model is based on it. – peterk Aug 30 '15 at 03:47
  • Note I tried getting the loader from a class in the same jar file in the same package as the one to be loaded, and when attempting to load an unreferenced class from the jar file in the same package as the one I referenced to get the loader it fails. The one from the application context however succeeds in all the cases. – peterk Aug 30 '15 at 03:53
  • note #2 I believe the application context class loader is a child of the "normal" class loader and not the other way around so the application context one is NOT a parent of the "normal" one. – peterk Aug 30 '15 at 03:57
  • Ok - I think I have sussed it out. There are some objects in the system loaded by parent class loaders of the primary package class loader. if I use one of those loaders the load fails.- however if I use a class in the library itself to retrieve the loader in my multi-library muiti-process application I am able to load classes from anywhere in the package.and be able to cast them to their superclasses which are imported directly. This still doesn't answer the question of how to get the Application or Service context in a process in a multi-process package globally but solved my need. – peterk Aug 30 '15 at 11:33

1 Answers1

1

You can extend Application:

public class MyApplication extends Application {
    private static MyApplication instance;
    public MyApplication() {
        instance = this;
    } 
    public static Context getContext() {
        return instance;
    } 
}

So you can use it else where

Context context=MyApplication.getContext();

Don't forget declaring MyApplication in Manifest file!

<application 
    android:name="com.myapp....MyApplication">
  ....
</application>
makata
  • 2,188
  • 2
  • 28
  • 23
  • Thanks. Though doesn't really do the job. The idea is to make the library able to use the class loader and all it needs is the fact that it exists and is running in a valid process with access to a context. I figure the best design would be to have have some way to determine the class loader for the current VM/Process to load any class that is within reach of the current process. I will try this. I will be curious it the appication has more than one process (ie: a Service in the app running as a separate process), if one instance of MyApplication will be initialized in both? – peterk Aug 29 '15 at 07:55
  • what may work here - is to have an application instance where it's constructor will initialize a static "ClassLoader" in the requiring class so that class itself needs no android linkage specific code in it - the library BTW is used on both the client and server side and the server is not android. – peterk Aug 29 '15 at 08:01
  • 1
    @peterk Every `Object` has a reference to its own `Class`, which has a reference to the `ClassLoader` that loaded it. But from the limited information you've given us, what you're trying to do doesn't make any sense. And yes, if an app uses more than one process, each process will not only have a completely independent `Application` instance, it will have completely independent copies of all classes and static fields. – Kevin Krumwiede Aug 29 '15 at 21:51
  • @KevinKrumwiede as I assume with android why having one instance of an "Application" will not work here. – peterk Aug 29 '15 at 23:39
  • 1
    note the application works fine if I explicitly initialize a "LoaderLoader" interface in the library in each process. It is just incovenient when someone is to use the library and since all imports have to come in from the loader the loader is present at load time for all code in the package - just inaccessable. – peterk Aug 29 '15 at 23:45
  • In some cases you may need to enforce users to register your library in their **Application** class to use it. For example, to use Parse API users have to do `void onCreate() {Parse.initialize(this, APP_ID, CLIENT_KEY);` So you may enforce something similar. – makata Aug 30 '15 at 04:12
  • @makata exactly what I am doing with my "explicit initialization" – peterk Aug 30 '15 at 11:41