What does DexClassLoader expect as input for libraryPath? - android

The DexClassLoader contructor looks like this:
public DexClassLoader (String dexPath, String optimizedDirectory, String libraryPath, ClassLoader parent)
What does DexClassLoader expect to be passed for libraryPath (besides null)?
Is it possible to pass in an external library as a parameter to DexClassLoader? If so does the libraryPath parameter behave similar to java's -classpath cmd line option? I wrote a simple java application that references a jar file as an external library, now I want to replicate the behavior in an android app.
I have a jar with math functions: MyMath.jar - contains a class "Add" and a method "add". In my main program I import the "mymath" package from MyMath.jar, create an instance of the Add class and call its add method.
To build I use:
javac -classpath ../lib/MyMath.jar MainProgram.java
To run I use:
java -classpath .;../lib/MyMath.jar MainProgram
This successfully utilizes the MyMath.jar to access the "Add" class in my main program. Now I need to replicate this in an android app, but I am unable to successfully load MyMath.jar with DexClassLoader, here is my implementation:
DexClassLoader classloader = new DexClassLoader("/storage/sdcard/Download/MainProgram.jar", codeCacheDir.getAbsolutePath(), "/storage/sdcard/Download/MyMath.jar", ClassLoader.getSystemClassLoader());
I can instantiate classes and invoke its methods that do not depend on "MyMath.jar" with classloader, but I cannot invoke methods that do depend on MyMath.jar
Here's the thrown exception when I try to invoke a method that depends on MyMath.jar:
java.lang.reflect.InvocationTargetException at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
...
Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Lcom/mymath/Add;
...
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:899)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:694) 
Caused by: java.lang.ClassNotFoundException: Didn't find class "com.mymath.Add" on path: DexPathList[[zip file "/storage/sdcard/Download/MainProgram.jar"],nativeLibraryDirectories=[/vendor/lib64, /system/lib64]]
How can I link MyMath.jar library when I load MainProgram.jar using DexClassLoader?

This library might help: https://github.com/simpleton/dalvik_patch/blob/master/inject_secondary_dex/src/com/example/dex/DexInjector.java
First, you can turn your jar file to dex file, by
dx --dex --output ./MyMath_dex.jar ./MyMath.jar
And then, you can load your dex file in Android, for more information, you can read the source code in library above.

DexClassLoader's first parameter takes in "list of jar/apk files containing classes and resources, delimited by File.pathSeparator, which defaults to ":" on Android"
The solution was to simply concatenate my MyMath.jar to my MainProgram.jar:
final DexClassLoader classloader = new DexClassLoader(
"/storage/sdcard/Download/MainProgram.jar" + ":" +
"/storage/sdcard/Download/MyMath.jar",
codeCacheDir.getAbsolutePath(),
null,
ClassLoader.getSystemClassLoader());

Related

How to import Guava into Android applications

What is the proper way to import Guava into an Android project? Every time I try to use it I get a NoClassDefFoundError.
This is what I'm doing to generate the crash. I'm using Android Studio 3.0 Canary 7.
Create an new project File > New > New Project, target API 26.0, using the Empty Activity template.
Add to app/build.gradle in the dependencies section
implementation "com.google.guava:guava:20.0"
Add this to the onCreate method in MainActivity.java
ImmutableList<String> foo = ImmutableList.of("A", "B", "C");
Log.d("MainActivity", foo.get(0));
Run the App and open up Logcat to see this exception:
FATAL EXCEPTION: main
Process: com.letsdoit.guavaissue, PID: 14366
java.lang.NoClassDefFoundError: Failed resolution of: Lcom/google/common/collect/ImmutableList;
at com.letsdoit.guavaissue.MainActivity.onCreate(MainActivity.java:20)
at android.app.Activity.performCreate(Activity.java:6679)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1118)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2618)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2726)
at android.app.ActivityThread.-wrap12(ActivityThread.java)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1477)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6119)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:886)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:776)
Caused by: java.lang.ClassNotFoundException: Didn't find class "com.google.common.collect.ImmutableList" on path: DexPathList[[zip file "/data/app/com.letsdoit.guavaissue-1/base.apk", zip file "/data/app/com.letsdoit.guavaissue-1/split_lib_dependencies_apk.apk", zip file "/data/app/com.letsdoit.guavaissue-1/split_lib_slice_0_apk.apk", zip file "/data/app/com.letsdoit.guavaissue-1/split_lib_slice_1_apk.apk", zip file "/data/app/com.letsdoit.guavaissue-1/split_lib_slice_2_apk.apk", zip file "/data/app/com.letsdoit.guavaissue-1/split_lib_slice_3_apk.apk", zip file "/data/app/com.letsdoit.guavaissue-1/split_lib_slice_4_apk.apk", zip file "/data/app/com.letsdoit.guavaissue-1/split_lib_slice_5_apk.apk", zip file "/data/app/com.letsdoit.guavaissue-1/split_lib_slice_6_apk.apk", zip file "/data/app/com.letsdoit.guavaissue-1/split_lib_slice_7_apk.apk", zip file "/data/app/com.letsdoit.guavaissue-1/split_lib_slice_8_apk.apk", zip file "/data/app/com.letsdoit.guavaissue-1/split_lib_slice_9_apk.apk"],nativeLibraryDirectories=[/data/app/com.letsdoit.guavaissue-1/lib/x86, /system/lib, /vendor/lib]]
at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:56)
at java.lang.ClassLoader.loadClass(ClassLoader.java:380)
at java.lang.ClassLoader.loadClass(ClassLoader.java:312)
at com.letsdoit.guavaissue.MainActivity.onCreate(MainActivity.java:20) 
at android.app.Activity.performCreate(Activity.java:6679) 
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1118) 
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2618) 
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2726) 
at android.app.ActivityThread.-wrap12(ActivityThread.java) 
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1477) 
at android.os.Handler.dispatchMessage(Handler.java:102) 
at android.os.Looper.loop(Looper.java:154) 
at android.app.ActivityThread.main(ActivityThread.java:6119) 
at java.lang.reflect.Method.invoke(Native Method) 
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:886) 
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:776)
I'm almost certain it has to do with Gauva being large and not playing well with multidex, but am not sure what to do about it. These are some of the note worthy things I've tried to no avail:
Enabling multidex and specifying ImmutableList in the multiDexKeepFile.
Disabling instant run.
Pulled the APKs from the device and verified the Guava classes are in the APKs.
Following the recommendations in this stack overflow question.
TL;DR
Use guava version 22.0-android and up. Make sure to use the -android flavor, otherwise you'll run into the NoClassDefFoundError.
Explanation
I learned after posting the question how to manually clean the project and uninstall apks from the emulator. It turns out that version 20.0 actually does work, but I had tried version 21.0 right before then and failed to clean.
The non-android flavors of guava as of version 21.0 are using Java 8. The android flavors and versions before 21.0 use Java 7. This is described in these release notes for version 22.0.
I tested these flavors and versions:
20.0 (Java 7) - works
21.0 (Java 8) - doesn't work
22.0 (Java 8) - doesn't work
22.0-android (Java 7) - works
When using version 21.0 or 22.0 (no -android) the ImmutableList class is getting referenced but not compiled into the dex files (since it's in italics). This was causing the NoClassDefFoundError.
APK with dangling references to ImmutableList
As the android developer docs explain
In the tree view, italicized nodes are references that do not have a
definition in the selected DEX file.
It further explains that
A DEX file can reference methods and fields that are defined in a
different a file. For example System.out.println() is a reference to
the println() method in the Android framework.
But in this case, there is no other file that these methods and class definition should end up in. It's just failing to add them.
Contrast that to using 20.0 or 22.0-android, where the ImmutableList class actually gets compiled in.
APK with ImmutableList defined
And the app starts up as expected.

Using an imported Jar file that accesses its own R$string values

I have a supplied .jar library that is under a different package and it attempts to access string resources in its own package. While the strings.xml is not inside the jar file how can I get com.itspackagename.R to work inside the .jar file when the service starts?
I am getting the following type of error.
04-03 11:31:41.798 1791-1791/? E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.mypackage, PID: 1791
java.lang.NoClassDefFoundError: Failed resolution of: Lcom/otherpackagename/R$string;
and
Caused by: java.lang.ClassNotFoundException: Didn't find class "com.otherpackagename.R$string" on path:
To me it looks like it is trying to access its own .R which is failing. I don't know if/how to fix this if I can simply put the string resources into my strings.xml?, place them inside the jar,or provide another strings.xml in another location, or if its an issue with it resolving its own resources (R) ?

Android Wear - java.lang.ClassNotFoundException

I'm developing an Android Wear extension to an existing app. While working on Android Studio I'm getting the following every time I'm running the wear module AFTER I run the main app, and the wear app crashes on start:
Caused by: java.lang.ClassNotFoundException: Didn't find class "com.example.mobile.activities.MainActivity" on path: DexPathList[[zip file "/data/app/com.example.mobile-2/base.apk"],nativeLibraryDirectories=[/data/app/com.example.mobile-2/lib/x86, /vendor/lib, /system/lib]]
at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:56)
at java.lang.ClassLoader.loadClass(ClassLoader.java:511)
at java.lang.ClassLoader.loadClass(ClassLoader.java:469)
at android.app.Instrumentation.newActivity(Instrumentation.java:1067)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2317)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2476) 
at android.app.ActivityThread.-wrap11(ActivityThread.java) 
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1344) 
at android.os.Handler.dispatchMessage(Handler.java:102) 
at android.os.Looper.loop(Looper.java:148) 
at android.app.ActivityThread.main(ActivityThread.java:5422)
The only solution I found for this is File > Invalidate Caches/Restart and then Build > Clean project.
After that I can run the wear module (until the next main run).
This makes my work very slow. Can someone help me here? 
ClassNotFoundException was thrown when an application tries to load in a class through its string name using:
The forName method in class Class.
The findSystemClass method in class ClassLoader .
The [loadClass](https://developer.android.com/reference/java/lang/ClassLoader.html#loadClass(java.lang.String, boolean)) method in class ClassLoader.
but no definition for the class with the specified name could be found.
As of release 1.4, this exception has been retrofitted to conform to the general purpose exception-chaining mechanism. The "optional exception that was raised while loading the class" that may be provided at construction time and accessed via the getException() method is now known as the cause, and may be accessed via the getCause() method, as well as the aforementioned "legacy method."
Found this SO related ticket, issue was resolved by doing steps below:
Right click on your project and select Properties.
Select Java Build Path from the menu on the left.
Select the Order and Export tab.
From the list make sure the libraries or external jars you added to your project are checked.
Finally, clean your project & run.
You may also check this SO ticket: Android Activity ClassNotFoundException - tried everything

Unable to load class using DexClassLoader, URLClassLoader or PathClassLoader

I'm trying to load a class file from a server and instatiante it using
different class loaders, but without success.
I tried DexClassLoader, URLClassLoader and PathClassLoader. Whatever I do, the only thing I get is a ClassNotFoundException from loadClass method.
First thing I do is to download:
jar file that contains the class file and the sex file
jar file that contains the class file but no sex file
the naked class file
And store it to the apps private storage folder. Then I try to load the class with one of the ClassLoader implementations mentioned above causing in:
Error: java.lang.ClassNotFoundException: Didn't find class
"FormTestView" on path: DexPathList[[zip file
"/data/data/[PACKAGE_NAME]/app_test_folder/test.jar"],nativeLibraryDirectories=[/vendor/lib,
/system/lib]]
Any idea?
Did you convert your .jar file using dx tool in android-sdks/build-tools?
If you did not try that, use this command:
./dx --dex --output yourJarDexConverted.jar yourJar.jar
The resulting yourJarDexConverted.jar file contains classes.dex which is an encrypted file including all the java classes needed by the application distributed as jar.

javah not able to find android classes

When calling javah on a .java file in my android project, javah was unable to find the android classes (specifically android.graphics.Bitmap).
here's the terminal output:
thomas#THOMASDESKLINUX:~$ javah -classpath .:/home/thomas/Documents/LinuxProgramming/EclipseWorkspace/RenderScene/bin org.me.renderscene.Billboard
javadoc: error - In doclet class com.sun.tools.javah.MainDoclet, method start has thrown an exception java.lang.reflect.InvocationTargetException
com.sun.tools.javac.code.Symbol$CompletionFailure: class file for android.graphics.Bitmap not found
1 error
help appreciated!
you need to add {android-sdk-path}\platforms\android-8\android.jar also to the classpath

Categories

Resources