javafx - 如何序列化包含 Image 对象的地图?
问题描述
我正在创建一个图片库,其中包含相册,每个相册都包含图片。这个 irems 像这样存储在 HashMap 中
HashMap<Album, ArrayList<Picture>> albums=new HashMap<>();
当尝试序列化地图时,问题就开始了,因为每个 Picture 对象都包含一个 Image 对象,所以我可以选择这个 Image 并为我的应用程序更轻松地创建一个 ImageView,Picture 构造函数如下所示:
public Picture(String name,String place, String description, Image image)
我总是得到这个异常:java.io.NotSerializableException: javafx.scene.image.Image
有没有办法让我的图片可序列化?
解决方案
您需要自定义Picture
. 要自定义对象的序列化,请使用以下两种方法:
void readObject(ObjectInputStream) throws ClassNotFoundException, IOException
void writeObject(ObjectOutputStream) throws IOException
这些方法可以有任何访问修饰符,但通常是 (?) private
。
如果您有以下课程:
public class Picture implements Serializable {
private final String name;
private final String place;
private final String description;
private transient Image image;
public Picture(String name, String place, String description, Image image) {
this.name = name;
this.place = place;
this.description = description;
this.image = image;
}
public String getName() {
return name;
}
public String getPlace() {
return place;
}
public String getDescription() {
return description;
}
public Image getImage() {
return image;
}
}
关于Image
.
序列化图像的位置。
private void readObject(ObjectInputStream in) throws ClassNotFoundException, IOException { in.defaultReadObject(); String url = (String) in.readObject(); if (url != null) { image = new Image(url); } } private void writeObject(ObjectOutputStream out) throws IOException { out.defaultWriteObjet(); out.writeObject(image == null ? null : image.getUrl()); }
该
Image#getUrl()
方法是在 JavaFX 9 中添加的。Image
序列化过孔的像素数据PixelReader
。反序列化时,您将使用PixelWriter
.private void readObject(ObjectInputStream in) throws ClassNotFoundException, IOException { in.defaultReadObject(); if (in.readBoolean()) { int w = in.readInt(); int h = in.readInt(); byte[] b = new byte[w * h * 4]; in.readFully(b); WritableImage wImage = new WritableImage(w, h); wImage.getPixelWriter().setPixels(0, 0, w, h, PixelFormat.getByteBgraInstance(), b, 0, w * 4); image = wImage; } } private void writeObject(ObjectOutputStream out) throws IOException { out.defaultWriteObject(); out.writeBoolean(image != null); if (image != null) { int w = (int) image.getWidth(); int h = (int) image.getHeight(); byte[] b = new byte[w * h * 4]; image.getPixelReader().getPixels(0, 0, w, h, PixelFormat.getByteBgraInstance(), b, 0, w * 4); out.writeInt(w); out.writeInt(h); out.write(b); } }
警告:以这种方式序列化像素数据是以未压缩格式保存图像。图像可能非常大,使用此方法时可能会给您带来问题。
这取决于
PixelReader
是否可用,但情况并非总是如此。如果您阅读文档,Image#getPixelReader()
您会看到(强调我的):PixelReader
如果图像可读,则此方法返回提供读取图像像素的访问权限。如果此方法返回 null,则此图像此时不支持读取。如果图像正在从源加载并且仍然不完整(进度仍然 <1.0)或出现错误,则此方法将返回 null 。对于某些格式不支持读取和写入像素的图像,此方法也可能返回 null 。在静态加载和错误之外,一些非详尽的测试表明动画 GIF 没有关联的
PixelReader
.序列化实际的图像文件(我不推荐这个,原因如下)。
private void readObject(ObjectInputStream in) throws ClassNotFoundException, IOException { in.defaultReadObject(); if (in.readBoolean()) { byte[] bytes = new byte[in.readInt()]; in.readFully(bytes); image = new Image(new ByteArrayInputStream(bytes)); } } private void writeObject(ObjectOutputStream out) throws IOException { out.defaultWriteObject(); out.writeBoolean(image != null); if (image != null) { byte[] bytes; try (InputStream is = new URL(image.getUrl()).openStream()) { bytes = is.readAllBytes(); } out.writeInt(bytes.length); out.write(bytes); } }
这假设
Image
不是从资源加载的(或者至少 URL 有一个方案)。但是,正如我所说,我不推荐这种方法。一方面,它只允许您对
Picture
实例进行一次序列化,因为反序列化后原始 URL 会丢失。您可以通过单独存储位置来解决此问题,但您也可以使用选项#1。还有一个事实是你InputStream
在序列化过程中打开并读取它;Picture
对于序列化实例的开发人员来说,这可能是非常出乎意料的行为。
一些注意事项:
上面的代码可能还有优化的空间。
选项 #1 和 #3 不考虑请求的图像宽度和高度。这可能会导致反序列化后内存中的图像更大。您可以修改代码来解决此问题。
你的
Picture
课看起来像一个模型课。如果是这种情况,最好将图像的位置简单地存储在字段中而不是Image
本身中(这也可以使自定义序列化变得不必要);然后让其他代码负责Image
根据存储在Picture
. 要么,要么允许Image
在Picture
实例中延迟加载。关键是避免
Image
在不需要时加载 s 。例如,如果您的部分 UI 只想按名称显示可用图片列表怎么办。如果您有数千个Picture
s,您将希望避免加载数千个Image
s,因为您很容易耗尽内存(即使只有几十张图像)。
推荐阅读
- maven - 如何在本地机器上管理 CodeArtifact AUTH 令牌
- azure-devops - 如何打印出管道或阶段的参数?
- c++17 - 使用可变参数调用后续方法
- typescript - 如何声明 Typescript 函数返回命名属性的类型?
- android - 如何从Android中的文件选择器中获取选定的文件夹路径
- azure - 我们能否在 Azure 资源组模板中获取 Application Insights 的连接字符串?
- spring-boot - 如何在 Apache Camel 3.XX 路由上设置标头?
- unix - unix将文件更新为可执行的jar文件不起作用
- python - 为什么python不能由多人或长期构建和维护?
- javascript - 无法将 ejs 文件的 url 重定向到在 js 文件中定义的具有两个参数的路由器