Interaction with a dynamically loaded library using DexClassLoader

I load a jar file at runtime from an SD card using the DexClassLoader class

final String libPath = Environment.getExternalStorageDirectory() + "/test.jar"; final File tmpDir = getDir("dex", 0); final DexClassLoader classloader = new DexClassLoader(libPath, tmpDir.getAbsolutePath(), null, this.getClass().getClassLoader()); final Class<Object> classToLoad = (Class<Object>) classloader.loadClass("org.shlublu.android.sandbox.MyClass"); final Object myInstance = classToLoad.newInstance(); final Method doSomething = classToLoad.getMethod("doSomething"); doSomething.invoke(myInstance); 

In my jar file, I print several logs that work fine. Now I am trying to print Toast from a jar file. With this exception, I get this java.lang.reflect.InvocationTargetException. I know why we get this exception and the reason behind this is the nullpointer exception in the context that uses it when printing the toast. So this is not a duplicate

What can cause java.lang.reflect.InvocationTargetException?

the reason is hers

  java.lang.NullPointerException: Attempt to invoke virtual method 'android.content.Context android.content.Context.getApplicationContext()' on a null object reference 

code in jar file

 public class MyClass extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { // TODO Auto-generated method stub super.onCreate(savedInstanceState); } public void doSomething() { Toast.makeText(getApplicationContext(), "MyClass: doSomething() called.", Toast.LENGTH_LONG).show(); Log.e(MyClass.class.getName(), "MyClass: doSomething() called."); }} 

Can anyone help me achieve this. Any help would be greatly appreciated.

Edit: what I'm trying to do is to have my own library that many of my clients use ... I want to download my library from the user's SD card ... and I want to update the library when I want without user knowledge and without any version updates. The library contains several interfaces, fragments, and actions. so now I can load my library from the SD card and call the main functions. Now the main task is to implement interfaces from the library and call functions that use the context in them.

All examples or tips related to such operations will be very helpful.

+1
android
Oct 19 '15 at 10:56
source share
1 answer

I see two ways to resolve this issue, depending on what you are ready to do:

  • If MyClass should be Activity

An Activity should be started correctly using startActivity() or any option. It must also be declared in your manifest. Thus, the following only works if all of your MyClass variants have the same signature.

I assume your startup code is in Activity . Right after loading classToLoad you can do the following:

 final File tmpDir = getDir("dex", 0); final String libPath = Environment.getExternalStorageDirectory() + "/test.jar"; final DexClassLoader classloader = new DexClassLoader(libPath, tmpDir.getAbsolutePath(), null, this.getClass().getClassLoader()); try { final Class<Object> classToLoad = (Class<Object>) classloader.loadClass("org.shlublu.android.sandbox.MyClass"); // CHANGED: THIS STARTS PROPERLY YOUR ACTIVITY ONCE THE CLASS LOADED final Intent intent = new Intent(this, classToLoad); startActivity(intent); } catch (ClassNotFoundException e) { // handle that Exception properly here } 

Now change doSomething() so that it uses the base Context your new Activity instead of getApplicationContext() . Then call it from MyClass.onCreate() and see if it works:

 public class MyClass extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); doSomething(); // CHANGED: just as an example } private void doSomething() { // CHANGED: now the following line uses 'this' instead of `getApplicationContext()` Toast.makeText(this, "MyClass: doSomething() called.", Toast.LENGTH_LONG).show(); Log.d(MyClass.class.getName(), "MyClass: doSomething() called."); } } 

The rest is when to call doSomething() , why, etc. - depends on what you are ready to do.

  • If MyClass just needs to display Toast

There is no need to create another Activity in this case. doSomething() just needs to get the correct Context to display Toast .

Modify MyClass as follows:

 public class MyClass { private void doSomething(Context ctx) { Toast.makeText(ctx, "MyClass: doSomething() called.", Toast.LENGTH_LONG).show(); Log.d(MyClass.class.getName(), "MyClass: doSomething() called."); } } 

And change your startup code to pass this to doSomething() if it is running with Activity :

 final File tmpDir = getDir("dex", 0); final String libPath = Environment.getExternalStorageDirectory() + "/test.jar"; final DexClassLoader classloader = new DexClassLoader(libPath, tmpDir.getAbsolutePath(), null, this.getClass().getClassLoader()); try { final Class<Object> classToLoad = (Class<Object>) classloader.loadClass("org.shlublu.android.sandbox.MyClass"); // CHANGED: LOADS THE METHOD doSomething(Context). EXECUTES IT WITH this AS AN ARGUMENT final Class[] args = new Class[1]; args[0] = Context.class; final Method doSomething = classToLoad.getMethod("doSomething", args); final Object myInstance = classToLoad.newInstance(); doSomething.invoke(myInstance, this); } catch (ClassNotFoundException e) { // handle that Exception properly here } 
+1
Oct 19 '15 at 12:33
source share



All Articles