I tried really hard, but always get a Class Not Found exception, from reading this answer
https://stackoverflow.com/a/3024261
I took my jar run the
dx --dex --output=C:\classes.dex C:\MyAndroidLib.jar
and got a dex file
then I run the
apt add C:\MyLib.jar C:\classes.dex
to create a jar with the dex file.
then I wrote the following code.
DexClassLoader classLoader = new DexClassLoader(
destPath, dirPath +"/" , null, getClass().getClassLoader());
Class<?> classToLoad = classLoader.loadClass("ClassImpl");
on debug I can see that the dex is inside the classLoader(under the mDexs member)
and the ClassImpl is the only class I got inside.
but I keep getting the class not found exception.
Anyone got a working sample of dynamic class loading from external jar ?
Someone knows whats my problem?
I did not use aapt... Only dex. And I got my class loaded, and my methods called.
Use this code for see inside the dex file:
DexFile dexfile = DexFile.loadDex(url_jar_path,
File.createTempFile("opt", "dex", context.getCacheDir()).getPath(), 0);
// Print all classes in the DexFile
Enumeration<String> classNames = dexfile.entries();
String classname = "";
while (true) {
if (! classNames.hasMoreElements()) {
break;
}
classname = classNames.nextElement();
}
Use this code for see inside the class:
Class class = dexclassloader.loadClass(ruta_clase_en_jar);
String name;
Method[] method_array = clase.getMethods();
for (Method i: method_array) {
name = i.getName ();
}
Related
I have developed a SDK for android applications.We have many clients using this SDK in there applications.Now i have updated my SDK.I am looking for a way that these changes can reflect in there application without updating there app on play store.Urgent help needed.Any help will be appreciated.Thanks in advance.
Well you can dynamically load a jar file from your SD card using DexLoader class...which you can update when ever you want..on your storage... below is working code..
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("com.test.android.MainActivity");
final Object myInstance = classToLoad.newInstance();
final Method doSomething = classToLoad.getMethod("doSomething");
doSomething.invoke(myInstance);
and in your library file code can be like this
public class MainActivity {
public void doSomething() {
Log.e(MainActivity .class.getName(), "MainActivity : doSomething() called.");
}}
tell me if you need any assistance
there is no such way for your situation. But there is one thing you can do to enable it for next update. Android can dynamically load compiled code with DexClassLoader. So you compile a new DEX file, and then force your SDK to download and use it.
// Internal storage where the DexClassLoader writes the optimized dex file to
final File optimizedDexOutputPath = getDir("outdex", Context.MODE_PRIVATE);
DexClassLoader cl = new DexClassLoader(dexInternalStoragePath.getAbsolutePath(),
optimizedDexOutputPath.getAbsolutePath(),
null,
getClassLoader());
Class libProviderClazz = null;
try {
// Load the library.
libProviderClazz =
cl.loadClass("com.example.dex.lib.LibraryProvider");
// Cast the return object to the library interface so that the
// caller can directly invoke methods in the interface.
// Alternatively, the caller can invoke methods through reflection,
// which is more verbose.
LibraryInterface lib = (LibraryInterface) libProviderClazz.newInstance();
lib.showAwesomeToast(this, "hello");
} catch (Exception e) { ... }
I'm trying to load classes from external JAR file placed on sdcard. Many people used DexClassLoader successfuly.
My steps:
1) Create classes.dex file from jar file:
dx --dex --output=classes.dex file.jar
2) Add generated classes.dex file to jar
3) Create DexClassLoader:
ClassLoader classLoader = new DexClassLoader(context.getDir("dex",
Context.MODE_PRIVATE).getAbsolutePath(), jarfile.getAbsolutePath(), null,
context.getClassLoader());
4) When I see what's in dex inside:
try {
DexFile dx = DexFile.loadDex(jarFile.getAbsolutePath(), File.createTempFile("opt", "dex",
context.getCacheDir()).getPath(), 0);
// Print all classes in the DexFile
for(Enumeration<String> classNames = dx.entries(); classNames.hasMoreElements();) {
String className = classNames.nextElement();
System.out.println("class: " + className);
}
} catch (IOException e) {
e.printStackTrace();
}
DexOpt: --- BEGIN 'file.jar' (bootstrap=0) ---
DexOpt: --- END 'file.jar' (success) ---
DEX prep '/mnt/sdcard/tmp/file.jar': unzip in 0ms, rewrite 83ms
class: com.test.TestClass
5) Load class:
Class<?> class = classLoader.loadClass("com.test.TestClass");
Here I get exception!:
java.lang.ClassNotFoundException: Didn't find class "com.test.TestClass" on path: /data/data/com.myapp/cache/app_dex
I see that it creates app_dex directory but it's empty.
Please, help!!!
The only way I could load classes from jar is by DexFile class:
DexFile dexFile = DexFile.loadDex(jar.getAbsolutePath(),
File.createTempFile("opt", "dex", context.getCacheDir()).getPath(), 0);
....
Class<?> currClass = dexFile.loadClass(className, context.getClassLoader());
Very interesting why I can't do this with DexClassLoader.
Does anybody use it in Android 4.2.2 or 4.4.2 ?
Thanks!
It seems you set incorrect order
ClassLoader classLoader = new DexClassLoader(context.getDir("dex",
Context.MODE_PRIVATE).getAbsolutePath(), jarfile.getAbsolutePath(), null,
context.getClassLoader());
replace first parameter with second
More info https://developer.android.com/reference/dalvik/system/DexClassLoader#public-constructors_1
I made and compiled a Android Library, containing a simple class and a simple static function:
package moo;
public class MyTestClass {
public static String Foo(){
return "Foo from Moo";
}
}
I placed the .jar in my Assets/Plugins/Android Folder. Then In Unity:
void OnGUI () {
string somestring = "foooooooooooOOooo";
AndroidJavaClass testClass = new AndroidJavaClass("moo.MyTestClass");
somestring = testClass.CallStatic<string>("Foo");
GUI.Label (new Rect (20, 20, 100, 20), somestring);
}
And I get an error:
JNI: Unable to find method id for 'Foo' (static)
UnityEngine.AndroidJavaObject:CallStatic(String, Object[])
Am I missing something to call my static method?
Thanks!
there are 2 problem as far as I can see:
you have to put your jar package to Assets/Plugins/Android/bin;
you will always get this error on your windows/mac editor, you have to run this on your android device;
I have a problem with this line of code. Want to create sqlite database on the device.
string dbPath = System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal) + "\\test.db";
if (!System.IO.File.Exists(dbPath))
using (System.IO.Stream sr = ***Assets***.Open("test.db"))
{
using (System.IO.Stream srTo = System.IO.File.Create(dbPath))
{
sr.CopyTo(srTo);
}
}
This message gives the:
The name 'Assets' does not exist in the current context
have a similar project, but more comprehensive than big. There is no error. They Assets Where I'm following definition defines a cs file in c drive gives reference to Android.Content.ContextWrapper.
is not on The project the path.
[How the file was added application?
If you aren't doing this in an activity you need a reference to the Activity/Context. You will need to pass this in to your helper class in the constructor.
public yourClass(Activity context.......){
context.Assets.Open("your.db");
}
You can do like this:
using (System.IO.Stream sr = Android.App.Application.Context.Assets.Open("test.db"))
{
using (System.IO.Stream srTo = System.IO.File.Create(dbPath))
{
sr.CopyTo(srTo);
}
}
I currently have a compiled jar file that I would like to use on an android device. The code outputs to the command line using System.out.println().
How would I create a wrapper to grab the stdout and put it in a text view on an android device? Would I need to make any changes to the jar (I do have all the source code) to allow the wrapper?
Thanks in advance.
I think you'll need to make some changes. You can set standart output by calling
System.setOut(PrintStream out)
// Reassigns the "standard" output stream.
Where out is your own class that will print data to text view. See swing solution. Just set appending to text view and you can use this code.
Or just create one method
void log(String message);
where you appending text to your view. Then change all println() calls to this.
First you should consider that Android has a specific Java VM called Dalvik and not any jar can be ran under it.
If there's one point in your jar where output occurs, the best option would be to create a usual application with a TextView, include your jar to it's build path and replace a call to println() with output to it:
public void print(String msg) {
mTextView.setText(msg);
}
If there're many sources of output you could run you jar using java.lang.Process and use it's getInputStream() method to read printed messages:
public static final String XBOOT_CLASS_PATH = "-Xbootclasspath:/system/framework/core.jar"
public static final String CLASS_PATH = "-classpath /path/to/your/file.jar com.your.package.name"
...
Process p = new ProcessBuilder("dalvikvm", XBOOT_CLASS_PATH, CLASS_PATH).start();
BufferedReader reader = new BufferedReader (new InputStreamReader(p.getInputStream()));
String msg = reader.readLine();
if (msg != null) {
mTextView.setText(msg);
}
// Cleanup omitted for simplicity
If it's an executable jar file here is a working example
Add this simple executable HelloWorld jar file to your Android Project's build path
If the jar file doesn't have a package, then you will have to use Reflection to invoke methods in it.Other wise you can just import the class files and invoke the main method directly.(This example jar has a package "psae")
eg:
TextView tv = (TextView)findViewById(R.id.textv);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
PrintStream ps = new PrintStream(baos);
System.setOut(ps);
String[] params = {"Aneesh","Joseph"};
psae.HelloWorld.main(params);
String output = baos.toString();
tv.setText(output)
If the jar file just has a default package, then you won't be able to import class files from that jar, and hence you will have to use Reflection to invoke the method.
TextView tv = (TextView)findViewById(R.id.textv);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
PrintStream ps = new PrintStream(baos);
System.setOut(ps);
try {
//pick the entry class from the jar Manifest
//Main-Class: psae.HelloWorld
Class myClass = Class.forName("psae.HelloWorld");
//since this has a package, there is no need of reflection.This is just an example
//If the jar file had just a default package, the it would have been something like the below line (and this is where it would be useful)
//Class myClass = Class.forName("Main");
Method myMethod = myClass.getMethod("main", String[].class);
//parameters to the main method
String[] params = {"Aneesh","Joseph"};
myMethod.invoke(null, (Object) params);
String output = baos.toString();
tv.setText(output);
}
catch(Exception d)
{
tv.setText(d.toString());
}