我正在寻找一种方法,在运行时以编程方式将jar文件中的一些类和其他文件加载到android应用程序中。
这样做的目的是,在jar文件中包含的类和其他文件的帮助下,可以增加应用程序的功能。我曾尝试使用java.net.urlclassloader,但由于android dalvik vm只能加载包含“classes.dex”文件的jar文件,所以没有成功。这些特殊的jar文件将由dexclassloader加载,如本线程所述。
但是,我正在寻找一种解决方案,可以通过编程方式而不是手动创建classes.dex文件并将其添加到jar文件中。到目前为止,出于测试目的,我已经尝试在操作系统(windows10)的cli中手动执行此过程,但也没有成功。以下是我的cli的输出:
C:\Users\%USERNAME%\AppData\Local\Android\Sdk\build-tools\29.0.2>dx --dex --output=C:\Users\%USERNAME%\Downloads\classes.dex C:\Users\%USERNAME%\Downloads\Weather.jar
-Djava.ext.dirs=C:\Users\%USERNAME%\AppData\Local\Android\Sdk\build-tools\29.0.2\lib is not supported. Use -classpath instead.
Error: Could not create the Java Virtual Machine.
Error: A fatal exception has occurred. Program will exit.
注意:“%username%”已替换为有效的用户名。只是出于安全原因被审查了一下。
下面是我当前的类加载解决方案的代码,它在常规jvm(jdk/jre 9.0.4)上运行得非常好,但在android应用程序中却不行:
package chrtopf.ddns.net.smarthomeapp.plugins;
import android.util.Log;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.GenericSignatureFormatError;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.MalformedParameterizedTypeException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import chrtopf.ddns.net.smarthomeapi.android.Plugin;
/**
* This class is responsible for executing the highly unstable process of getting a new instance
* of a plugins main class. There is a total number of 11 exceptions to be thrown during this
* procedure. Every exception get printed with its stack trace and a custom designed fatal error
* message. The one and only method this class makes use of is loadPlugin(). The plugins are
* loaded through the URIClassLoader. ATTENTION: If the package name of a class to be loaded
* is the same as one of the package names from the application in which this PluginLoader class
* is used a conflict between the two same named packages is created. In this conflict the same
* named package of your application is always going to be chosen first INSTEAD of the package
* from a different .jar archive file.
*
* @author ChrTopf
*
*/
public class PluginLoader {
private static final String TAG = "PluginLoader";
private PluginInterface app;
/**
* Initializes a new plugin loader object.
* @param app The plugin interface which is going to be used in the constructor of the plugins
* main class. This interface delivers access to important methods to the plugin. (PluginInterface)
*/
public PluginLoader(PluginInterface app) {
this.app = app;
}
/**
* This method loads the main class of a plugin from a specific package of a specific .jar archive file.
* @param plugin_name The name of the plugin (only for GUI and debug, but important). (String)
* @param jar_file The existing .java archive file of the plugin to be loaded. (File)
* @param main_path The package path to the plugins main class. (String)
* @return returns a new Instance of the plugins main class. (Plugin)
*/
public Plugin loadPlugin(String plugin_name, File jar_file, String main_path) {
try {
//get the url e.g. the path to the .jar file of the plugin
URL file_path = jar_file.toURI().toURL();
//try to load the .jar archive
URLClassLoader classloader = new URLClassLoader(new URL[] {file_path});
//load the main class from the archive as specified in the .properties file
Class<?> plugin = classloader.loadClass(main_path);
//get the constructor of the main plugin class
Constructor<?> plugin_const = plugin.getDeclaredConstructor(PluginInterface.class);
//prepare the constructor
plugin_const.setAccessible(true);
//get a new instance of the main plugin class using the prepared constructor
Plugin new_plugin = (Plugin) plugin_const.newInstance(this.app);
//close the classloader
classloader.close();
//return the new instance of the plugin
return new_plugin;
} catch (MalformedURLException e) {
e.printStackTrace();
Log.e(TAG, "The configuration file of the plugin with the name " + plugin_name + " has an incorrect path to the .jar archive!");
} catch (ClassNotFoundException e) {
e.printStackTrace();
Log.e(TAG, "The configuration file of the plugin with the name " + plugin_name + " has an incorrect path to the main class! Or the plugin is made for a different smarthome server version.");
} catch (TypeNotPresentException e) {
e.printStackTrace();
Log.e(TAG, "The main class of the plugin with the name " + plugin_name + " has no superclass, but it needs to be plugin.Plugin!");
} catch (MalformedParameterizedTypeException | GenericSignatureFormatError e) {
e.printStackTrace();
Log.e(TAG, "This should definitely not happen. Please contact the author of the smarthome server application or verfiy that you used java version 13.0.1 for your plugin with the name " + plugin_name);
} catch (InstantiationException e) {
e.printStackTrace();
Log.e(TAG, "A new instance of the main class could somehow not be constructed from the plugin " + plugin_name);
} catch (IllegalAccessException e) {
e.printStackTrace();
Log.e(TAG, "A new instance of the main class could not be constructed from the plugin " + plugin_name + " because it is unknown to the smarthome server.");
} catch (IllegalArgumentException e) {
e.printStackTrace();
Log.e(TAG, "A new instance of the main class could not be constructed from the plugin " + plugin_name + " due to illegal Arguments.");
} catch (InvocationTargetException e) {
e.printStackTrace();
Log.e(TAG, "A new instance of the main class could not be constructed from the plugin " + plugin_name + " due to the constructor of the main class throwing an exception.");
} catch (NoSuchMethodException e) {
e.printStackTrace();
Log.e(TAG, "A new instance of the main class could not be constructed from the plugin " + plugin_name + " because a specific method could not be found in the main plugin class.");
} catch (SecurityException e) {
e.printStackTrace();
Log.e(TAG, "A new instance of the main class could not be constructed from the plugin " + plugin_name + " due to missing file system rights.");
} catch (IOException e) {
e.printStackTrace();
Log.e(TAG, "The classloader could not be closed successfully.");
}
return null;
}
}
注意:loadplugin()方法从jar文件返回一个类,它是plugin类的接口。
由于dalvik vm无法从这种jar文件中加载类,我在logcat中得到以下错误:
2020-12-05 13:36:26.648 15121-15121/chrtopf.ddns.net.smarthomeapp2 E/AndroidRuntime: FATAL EXCEPTION: main
Process: chrtopf.ddns.net.smarthomeapp2, PID: 15121
java.lang.RuntimeException: Unable to start activity ComponentInfo{chrtopf.ddns.net.smarthomeapp2/chrtopf.ddns.net.smarthomeapp.main.MainActivity}: java.lang.UnsupportedOperationException: can't load this type of class file
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3375)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3514)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:83)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2110)
at android.os.Handler.dispatchMessage(Handler.java:107)
at android.os.Looper.loop(Looper.java:214)
at android.app.ActivityThread.main(ActivityThread.java:7697)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:516)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:950)
Caused by: java.lang.UnsupportedOperationException: can't load this type of class file
at java.lang.ClassLoader.defineClass(ClassLoader.java:591)
at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
at java.net.URLClassLoader.defineClass(URLClassLoader.java:469)
at java.net.URLClassLoader.access$100(URLClassLoader.java:73)
at java.net.URLClassLoader$1.run(URLClassLoader.java:368)
at java.net.URLClassLoader$1.run(URLClassLoader.java:362)
at java.security.AccessController.doPrivileged(AccessController.java:69)
at java.security.AccessController.doPrivileged(AccessController.java:94)
at java.net.URLClassLoader.findClass(URLClassLoader.java:361)
at java.lang.ClassLoader.loadClass(ClassLoader.java:379)
at java.lang.ClassLoader.loadClass(ClassLoader.java:312)
at chrtopf.ddns.net.smarthomeapp.plugins.PluginLoader.loadPlugin(PluginLoader.java:59)
at chrtopf.ddns.net.smarthomeapp.plugins.PluginWrap.load(PluginWrap.java:82)
at chrtopf.ddns.net.smarthomeapp.plugins.PluginManager.loadPlugin(PluginManager.java:178)
at chrtopf.ddns.net.smarthomeapp.plugins.PluginManager.startExec(PluginManager.java:131)
at chrtopf.ddns.net.smarthomeapp.main.MainActivity.onCreate(MainActivity.java:105)
at android.app.Activity.performCreate(Activity.java:7815)
at android.app.Activity.performCreate(Activity.java:7804)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1325)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3350)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3514)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:83)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2110)
at android.os.Handler.dispatchMessage(Handler.java:107)
at android.os.Looper.loop(Looper.java:214)
at android.app.ActivityThread.main(ActivityThread.java:7697)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:516)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:950)
我使用的是AndroidAPI level 29和AndroidStudio 4.0.1
提前感谢您的支持!
暂无答案!
目前还没有任何答案,快来回答吧!