是否可以在运行时从Android应用程序动态加载库?
有没有办法让Android应用程序在运行时下载并使用Java库 以下是一个例子: 假设应用程序需要根据输入值进行一些计算。应用程序要求输入这些值,然后检查所需的是否可以在运行时从Android应用程序动态加载库?,android,reflection,classloader,dalvik,android-library,Android,Reflection,Classloader,Dalvik,Android Library,有没有办法让Android应用程序在运行时下载并使用Java库 以下是一个例子: 假设应用程序需要根据输入值进行一些计算。应用程序要求输入这些值,然后检查所需的Classes或方法是否可用 如果没有,它将连接到服务器,下载所需的库,并在运行时加载它,以使用反射技术调用所需的方法。实现可能会根据不同的条件而变化,例如下载库的用户。我不确定是否可以通过动态加载java代码来实现这一点。可能你可以尝试嵌入一个脚本引擎,比如rhino,它可以执行java脚本,可以动态下载和更新。对不起,我迟到了,这个问
Classe
s或方法是否可用
如果没有,它将连接到服务器,下载所需的库,并在运行时加载它,以使用反射技术调用所需的方法。实现可能会根据不同的条件而变化,例如下载库的用户。我不确定是否可以通过动态加载java代码来实现这一点。可能你可以尝试嵌入一个脚本引擎,比如rhino,它可以执行java脚本,可以动态下载和更新。对不起,我迟到了,这个问题已经有了公认的答案,但是是的,你可以下载并执行外部库。我是这样做的:
我想知道这是否可行,所以我写了以下课程:
package org.shlublu.android.sandbox;
import android.util.Log;
public class MyClass {
public MyClass() {
Log.d(MyClass.class.getName(), "MyClass: constructor called.");
}
public void doSomething() {
Log.d(MyClass.class.getName(), "MyClass: doSomething() called.");
}
}
我把它打包成一个DEX文件,保存在我设备的SD卡上,名为/sdcard/shlublu.jar
在从Eclipse项目中删除并清理了MyClass
之后,我编写了下面的“愚蠢程序”:
public class Main extends Activity {
@SuppressWarnings("unchecked")
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
try {
final String libPath = Environment.getExternalStorageDirectory() + "/shlublu.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);
} catch (Exception e) {
e.printStackTrace();
}
}
}
public类主扩展活动{
@抑制警告(“未选中”)
@凌驾
创建时受保护的void(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
试一试{
最后一个字符串libPath=Environment.getExternalStorageDirectory()+“/shlublu.jar”;
最终文件tmpDir=getDir(“dex”,0);
final DexClassLoader classloader=新的DexClassLoader(libPath,tmpDir.getAbsolutePath(),null,this.getClass().getClassLoader());
最终类classToLoad=(类)classloader.loadClass(“org.shlublu.android.sandbox.MyClass”);
最终对象myInstance=classToLoad.newInstance();
最终方法doSomething=classToLoad.getMethod(“doSomething”);
调用(myInstance);
}捕获(例外e){
e、 printStackTrace();
}
}
}
它基本上是这样加载类MyClass
:
- 创建一个
- 使用它从
“/sdcard/shlublu.jar”
- 并将此类存储到应用程序的
“dex”
专用目录(手机的内部存储)
然后,它创建一个MyClass
的实例,并对所创建的实例调用doSomething()
而且它是有效的。。。我在日志猫中看到了MyClass
中定义的跟踪:
我已经在emulator 2.1和我的物理HTC手机(运行Android 2.2,没有根目录)上进行了尝试
这意味着您可以为应用程序创建外部DEX文件以下载和执行它们。在这里,这是一个艰难的过程(丑陋的对象
强制转换,方法.invoke()
丑陋的调用…),但必须能够使用接口
使某些东西更干净
哇。我是第一个感到惊讶的人。我希望出现安全异常
以下事实有助于进一步调查:
- 我的DEX shlublu.jar已签名,但我的应用程序未签名
- 我的应用程序是通过Eclipse/USB连接执行的。这是一个在调试模式下编译的无符号APK
Shlublu的anwser非常好。不过有些小事情对初学者会有帮助:
- 对于库文件“MyClass”,创建一个单独的Android应用程序项目,该项目将MyClass文件作为src文件夹中的唯一文件(其他内容,如project.properties、manifest、res等也应存在)
- 在库项目清单中,确保您具有:
(“.NotExecutable”不是一个保留字,只是我必须在这里放一些东西)
- 要生成.dex文件,只需将库项目作为android应用程序运行(用于编译),并从项目的bin文件夹中找到.apk文件
- 将.apk文件复制到您的手机上,并将其重命名为shlublu.jar文件(不过,apk实际上是jar的一种特殊化)
其他步骤与Shlublu所述相同
- 非常感谢Shlublu的合作
当然,这是可能的。未安装的apk可以被宿主android应用程序调用。通常,解析资源和活动的生命周期,然后动态加载jar或apk。
详细信息,请参阅我对github的开源研究:
此外,还需要DexClassLoader和反射,现在来看一些关键代码:
/**
* Load a apk. Before start a plugin Activity, we should do this first.<br/>
* NOTE : will only be called by host apk.
* @param dexPath
*/
public DLPluginPackage loadApk(String dexPath) {
// when loadApk is called by host apk, we assume that plugin is invoked by host.
mFrom = DLConstants.FROM_EXTERNAL;
PackageInfo packageInfo = mContext.getPackageManager().
getPackageArchiveInfo(dexPath, PackageManager.GET_ACTIVITIES);
if (packageInfo == null)
return null;
final String packageName = packageInfo.packageName;
DLPluginPackage pluginPackage = mPackagesHolder.get(packageName);
if (pluginPackage == null) {
DexClassLoader dexClassLoader = createDexClassLoader(dexPath);
AssetManager assetManager = createAssetManager(dexPath);
Resources resources = createResources(assetManager);
pluginPackage = new DLPluginPackage(packageName, dexPath, dexClassLoader, assetManager,
resources, packageInfo);
mPackagesHolder.put(packageName, pluginPackage);
}
return pluginPackage;
}
/**
*加载apk。在开始插件活动之前,我们应该先这样做。
*注意:将仅由主机apk调用。
*@param dexPath
*/
公共DLPluginPackage loadApk(字符串dexPath){
//当主机apk调用loadApk时,我们假设插件由主机调用。
mFrom=DLConstants.FROM_EXTERNAL;
PackageInfo PackageInfo=mContext.getPackageManager()。
getPackageGearChiveInfo(dexPath,PackageManager.GET_活动);
如果(packageInfo==null)
返回null;
最后一个字符串packageName=packageInfo.packageName;
DLPluginPackage pluginPackage=mPackagesHolder.get(packageName);
if(pluginPackage==null){
DexClassLoader DexClassLoader=createDexClassLoader(dexPath);
AssetManager AssetManager=createAssetManager(dexPath);
Resources=createResources(assetManager);
pluginPackage=新的DLPluginPackage(packageName、dexPath、dexClassLoader、assetManager、,
资源,packageInfo);
mPackagesHolder.put(packageName,pluginPackage);
}
返回插件包;
}
你的数字高程模型
<manifest ...>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="18" />
...
</manifest>
public class MyFragment extends Fragment {
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup
container, @Nullable Bundle savedInstanceState)
{
super.onCreateView(inflater, container, savedInstanceState);
LinearLayout layout = new LinearLayout(getActivity());
layout.setLayoutParams(new
LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.MATCH_PARENT));
Button button = new Button(getActivity());
button.setText("Invoke host method");
layout.addView(button, LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT);
return layout;
}
}