android动态加载库,关于android:使用DexClassLoader与动态加载的库进行交互

我正在使用DexClassLoader类从SD卡在运行时加载jar文件

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 classToLoad = (Class) classloader.loadClass("org.shlublu.android.sandbox.MyClass");

final Object myInstance  = classToLoad.newInstance();

final Method doSomething = classToLoad.getMethod("doSomething");

doSomething.invoke(myInstance);

在我的jar文件中,我正在打印一些运行良好的日志。现在我要执行的操作是从jar文件中打印Toast。在执行此操作时,我得到的异常是java.lang.reflect.InvocationTargetException。我知道为什么我们得到这个异常,其背后的原因是在打印吐司时使用它的上下文中的空指针异常。所以它不是重复的

什么会导致java.lang.reflect.InvocationTargetException?

原因是

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

jar文件中的代码是

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.");

}}

任何人都可以帮助我实现这一目标。任何帮助将不胜感激。

编辑:

我想做的是我有一个自己的图书馆,很多客户正在使用...我想要的是从用户的SD卡中加载我的图书馆。我想在需要时更新该图书馆没有用户的知识,也没有任何版本更新。库包含很少的接口,片段和活动。所以现在我能够从sd卡加载我的库并可以调用基本功能。现在的主要挑战是从库中实现接口并调用在其中使用了上下文的函数。

涉及此类操作的任何示例或提示将大有帮助。

可能重复的原因是什么会导致java.lang.reflect.InvocationTargetException?

@Boss异常不是我的问题。我知道其原因。我需要协助以实现所需的东西..因此,它不是该java.lang.reflect.InvocationTargetException问题的重复项

...是的,它是重复的...您需要解开此异常以了解是什么原因引起的...

@selvin请仔细阅读我已编辑过的问题,我已经知道了原因。我只是问什么解决方法。谢谢

好的...错误是显而易见的...您的类正在扩展活动或服务...,并且您自己创建了它的实例...然后在doSomething中调用getApplicationContext() ...

@selvin ..是的,它可以扩展活动,然后我该如何实现..请检查我编辑过的问题..

没有办法... ...如果您知道android的基础知识,显然创建有效Activity实例的唯一方法是调用Contenxt.startIntent ... ...但必须在manifest中声明这种活动。 ..解决方案是改用Fragments

@selvin但是那是我的要求...我有一个可以随时随地更改的android库..所以您知道一种更好的方法... ??如果您能帮助您,那就太好了... :)

@selvin带片段,您的意思是我应该在jar文件中使用片段来代替活动。但是您不认为使用偶数片段会涉及一项活动吗?

哦,拜托...然后您将有一个通用活动,由于意图参数采用了指向jar文件和片段类名称的路径...创建片段取决于您(不确定配置更改或其他位置)操作系统本身就是在创建片段的实例)...如果您不太了解android框架,那么此任务超出了您的可能...认真地,您需要非常了解android组件的生命周期

@selvin是的,我确定您真的很擅长android框架..我可以在您的评论中嗅它。.请您帮我做一下...

@Selvin,您不能对OP这么说:"如果您不太了解android框架,那么此任务将超出您的可能……认真地,您需要非常了解android组件的生命周期"。我们所有人在这里都是互相帮助,而不是像这样。如果您发现此任务对您来说很容易,为什么不发布工作代码或至少暗示一下。

@TGMCians我可以给他一个错误的代码(准备复制并粘贴)...但是,基于问题中的例外情况,他将无法正确使用它... gist.github.com/SelvinPL/eefa88add4fa33f41ca8 ...从此代码加载的jar包含仅具有1类Fragment1 @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { TextView textView = new TextView(getActivity()); textView.setText("This is fragment loaded dynamically"); return textView; }的dex。

@ Selvin,请参阅我编辑的问题。

我看到两种解决方法,具体取决于您愿意做什么:

如果MyClass必须是Activity

必须使用startActivity()或任何变体正确启动Activity。它也必须在清单中声明。因此,仅当您所有的MyClass变体都具有相同的签名时,以下内容才有效。

我假设您的启动代码位于Activity中。加载classToLoad之后,您可以执行以下操作:

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 classToLoad = (Class) 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

}

现在,以使用新Activity的基础Context而不是getApplicationContext()的方式更改doSomething()。然后例如从MyClass.onCreate()调用它,然后看它是否起作用:

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.");

}

}

其余的内容-什么时候调用doSomething(),为什么调用等...-取决于您愿意做什么。

如果MyClass仅需显示Toast

在这种情况下,无需创建另一个Activity。 doSomething()仅需要接收适当的Context即可显示Toast。

更改MyClass,如下所示:

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.");

}

}

并假设您从Activity运行启动代码,以将this传递给doSomething():

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 classToLoad = (Class) 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

}

@shublu非常感谢您的答复。第一种方法行不通,因为如果我们使用Intent,它将在当前项目中找到该活动。请告诉我们如何将其作为参数传递给doSomething()。我的意思是在classToLoad.getMethod(" doSomething");中怎么可能;

正如我认为的第一种方法是抛出ActivityNotFoundException ... :(

Method.invoke()可以接收您需要的所有参数:developer.android.com/reference/java/lang/reflect/,java.lang.Object ...)我相应地更新了答案。

显然(如我在评论中所写),它将不起作用,因为活动未在清单中注册。

@Selvin是的,没错!我将其添加到清单中时未在回答中说明!在我的测试中,我总是加载具有相同类声明的插件。

@Selvin是的,我们知道这将无法工作...您能提供一些可以工作的内容吗...谢谢

//更改:现在,以下行使用了它,而不是getApplicationContext() <=这确实是不必要的...对我来说,似乎是通过置换编程...应用上下文对于烤面包非常有效

@Hardeep它确实有效,但是必须声明它。

@shublu我的意思是..但是这里的情况有所不同,我无法在我当前的项目清单中声明它。非常感谢您的回答。

@selvin应用程序上下文对于敬酒非常有效:谢谢。随意更改答案。我倾向于尽可能少地使用getApplicationContext(),这可能是一个坏习惯。

是的,在每个地方都使用getApplicationContext()是个坏习惯...为什么要对Toast这样的小任务使用完整的应用程序引用...使用它会更好...

如果我使用您的第二种方法,即将上下文作为参数传递,则会得到异常java.lang.NoSuchMethodException:最终方法的doSomething [] doSomething = classToLoad.getMethod(" doSomething");

@Shublu请参阅我编辑的问题。

@Hardeep getMethod()应??该收到您方法的签名:args和类型的数量。 developer.android.com/reference/java/lang/,java.lang.Class ...)

@shublu好的,我将通过此链接...请查看我编辑过的问题...您不知道我想在MyClass中做什么。.i在那儿提到了... :)感谢您的答复和帮助。 。

@shublu我以您说的方式更改了代码,但仍然收到相同的错误... java.lang.NoSuchMethodException:doSomething [class android.content.Context] ...

让我们继续聊天中的讨论。

你为什么不使用界面呢? ...在我的示例中,我只是将类从ddownloaded jar转换为Fragment ...您可以仅创建带有合约的库,并在主应用程序和" plugin"中使用它...在" plugin"库中,您可以从实现接口这个库和在主应用程序中只是创建类的实例,将其强制转换为接口并使用它...

@Selvin我没有,因为这不是OP要求的。他的问题实际上是另一个问题的跟进:stackoverflow.com/questions/6857807/

@Shlublu,这对您来说不是... :)您正在尽最大努力帮助他...但似乎您会失败(当然不是您的错,您的例子还可以):)

@selvin您的评论是什么意思……"您只能使用合约制作库,并在主应用程序和"插件"中使用它?

@Selvin是的,他将失败。.您几乎没有在这里添加任何有用的评论...,但您一直在劝阻和批评事情..请参阅shublu提供的答案质量stackoverflow.com/questions/6857807/is-it-possible -在运行时从Android应用程序动态加载库6860579#6860579每一件事都得到了很好的解释...他的工作说得比您的评论要响亮...甚至其他人也可以理解的代码

@shublu我仍然收到该错误,尽管我已经使用上下文参数更新了我的函数。java.lang.NoSuchMethodException:doSomething [class android.content.Context] ..

@ Hardeep听起来很奇怪。有空聊天吗?

@shublu是的,我已经在那儿给您发送了一条消息。

你好shlublu ..希望你还好..对不起,这个项目被搁置了几天... m返回到此..m,很抱歉,我在两者之间离开了对话...我们可以再讨论一次...谢谢提前...

您好@shlublu,我在项目中取得了一些进展。.请您帮我从动态加载的jar文件实现接口。谢谢

@Shlublu您是否有示例代码在动态库中调用Activity类? (第1点),当我使用Intent时,出现错误ClassNotFound,导致其不在主项目中

@KDoX好吧,我大概做了,但是自2015年10月以来我没有再对此进行深入研究。但是,如果您遇到ClassNotFoundError,则意味着该类尚未加载。如果问题出在可下载的动态库侧,则可能必须使用模型类或某种反射方式才能正确编译它。另外,您是否看过stackoverflow.com/questions/6857807/?

@Shlublu是指singhatiwana的答案吗?我目前正在测试他的项目github.com/singwhatiwanna/dynamic-load-apk/blob/master/

@KDoX是的,对此和一般而言,此Q / A。好!

@Shlublu thanx进行确认。 #敬礼

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值