正确使用 Classloader(尤其是在 Android 中)

Posted

技术标签:

【中文标题】正确使用 Classloader(尤其是在 Android 中)【英文标题】:Correct use of Classloader (especially in Android) 【发布时间】:2010-05-09 21:23:33 【问题描述】:

我阅读了一些关于类加载器的文档,但我仍然不确定在哪里以及为什么需要它们。 android API 说:

从 存储库。一个或多个类加载器 在运行时安装。这些都是 每当运行时系统咨询 需要一个尚未完成的特定课程 内存中可用。

所以如果我理解正确的话,可能会有很多类加载器负责加载新类。但是系统如何决定使用哪个?在什么情况下开发者应该实例化一个新的类加载器?

在 Android API for Intent 中有一个方法

public void  setExtrasClassLoader  (ClassLoader  loader)

描述说:

设置将使用的 ClassLoader 解组任何 Parcelable 时 来自此 Intent 的附加值。

那么我可以在那里定义一个特殊的类加载器,以便我可以传递带有未在接收活动中定义的 Intent 的对象吗?一个例子:

如果位于项目 A(在 Eclipse 中)中的活动 A 定义了一个我想使用 Intent 对象的 putExtra 发送到项目 B 中的活动 B 的对象。如果未定义通过 Intent 发送的此对象(项目 B 中的源代码),则会出现 NoClassDefFoundException。那么我可以使用 setExtraClassloader 方法来避免这个异常吗?如果是,我如何决定我必须通过哪个类加载器对象?以及如何正确实例化它?

【问题讨论】:

【参考方案1】:

我阅读了一些关于 类加载器,但我仍然不确定 需要它们的地方和原因。

一般来说,你不需要接触类加载器系统。

在什么情况下应该 开发者实例化一个新的 类加载器?

拥有大约十年的 Java 编程经验。 :-)

如果活动 A 位于 项目 A(在 Eclipse 中)定义了一个 我要发送到的对象 项目 B 中的活动 B 使用 putExtra Intent 对象。如果这个对象 通过 Intent 发送的不是 已定义(项目 B 中的源代码), 然后有一个 NoClassDefFoundException。那我可以用吗 方法 setExtraClassloader 到 避免这个异常?

不可以,因为项目 A 和项目 B 不能共享代码。将您需要的课程放入两个项目中。或者使用带有 AIDL 的远程服务接口,而不是 Intents 和附加功能。或者不使用自定义类,而是将对象视为数据结构(例如,使用简单的 HashMapStrings 或其他东西)。

【讨论】:

但是如果这不可能,这个方法的目的是什么? 自定义类加载器通常会产生与解决问题一样多的问题,因此在尝试解决问题时,它们不是一个好的起点。如果您真的想感到困惑,请尝试弄清楚 Thread.setContextClassLoader 的作用。 :-) @CommonsWare No, because Project A and Project B cannot share code... Eeeh... 但是我在这里使用的createPackageContext 方法呢:***.com/questions/5743485/… @Idolon:虽然这种技术显然有效,但我不推荐它。您无法强制两个项目使用相同的代码库,除非您打算用枪指着所有用户的脑袋。您需要能够应对独立更新的两个项目。这需要一个设计为在面对版本变化时保持稳健的 API,我怀疑通过从其他项目中加载类来实现这一点。 @CommonsWare 是的,你对 2 个应用程序共享的代码是完全正确的——一旦你发布了它们,公共 API 需要为所有未来版本冻结。但我想这种技术是在不同包之间使用ResultReceiver 的唯一可能方法。你怎么看? (当然有远程服务接口,但有时 ResultReceiver 只是更方便)。【参考方案2】:

这是一个迟到的答案,但希望它可以帮助其他人。

类加载器通常用于在运行时加载可执行的 Java 代码。一个很好的例子就是从互联网上下载的插件。您可以从类文件中获取二进制数据,加载它,并根据需要调用其中的函数。当然,您需要使用调用程序已知的接口或抽象类,以便它知道如何使用该类。

当二进制类数据在典型庄园中不可访问时,使用自定义类加载器。例如,如果您的蓝牙设备包含一个类文件,其中包含实现接口的代码,则需要编写一个自定义类加载器来通过蓝牙接口加载类数据。

您可能想要编写自定义类加载器的另一个原因是,如果您想要更改加载的类访问其他类的方式。您可以限制加载的类可以访问哪些内部类,甚至可以编写自己的类,从而改变内部类的行为。例如,如果加载的类使用 Java.io.File 类,您可能需要强制它使用内部类以不同的方式访问文件。

简而言之,当您编写自定义类加载器时,您会更改类的加载方式,以及加载的类将如何加载所有其他类。

【讨论】:

【参考方案3】:

类加载器并不难理解,至少在现有的 Java 空间中是这样。 (我可以在 90 分钟内教你 ClassLoader 系统——我在 No Fluff Just Stuff 节目中一直这样做。)也就是说,大多数时候你不需要创建自定义 ClassLoader——如果你愿意的话在输入字节码的过程中,java.lang.instrument 是你的朋友。如果要从 URL 加载代码,请查看 java.net.URLClassLoader。在这两者之间,对自定义 ClassLoader 的需求完全为零。

【讨论】:

以上是关于正确使用 Classloader(尤其是在 Android 中)的主要内容,如果未能解决你的问题,请参考以下文章

Java classes and class loading

session and cookie

在 macOS 上测试文件名是不是相等,尤其是在 HFS+ 和 APFS 上

(转)理解classloader

类加载器(ClassLoader)

Parquet and ORC