Android - 处理作为解决方案一部分的图像和下载的图像

Android - Handling images that are part of solution and images that are downloaded

我不清楚 Android 在缓存方面如何处理图像。

比方说,我有一个应用程序 这是 3 层深(因此,它有 3 个活动被推到 彼此)。

如果我的 3 个活动中的每一个都在所有 3 个活动的工具栏中显示 3 个图像,我的理解是相同的 3 个图像将被加载到内存中 3 次。这导致 9 张图像占用内存,尽管实际上只有 3 张图像,但它们只是 多次加载。

此外,假设我的主 activity 有一个 ListView,显示有关通过网络检索的文档的信息。

每个列表视图项都自定义为显示 3 条信息:

如果上面的 ListView 加载了数百万个项目(每个项目代表一个文档)并假设所有项目都是一个 word 文档,那么 .docx 类型的所有数百万个图像都会被下载,但它实际上只是我需要的一个图像。这似乎是对网络带宽和内存的明显浪费,我宁愿只下载一张图像并使用它一百万次,而不是下载一百万张图像并每张图像只使用一次。

所以,在这种情况下,我不知道我需要什么样的图像,因此图像资产中没有它,而是依赖下载的图像来显示文档类型,是否有某种缓存策略可以我可以用来避免下载相同的图像一百万次吗?

Android已经提供了吗?

更新

在我上面的工具栏图像示例中,这些是 Android 资源图像(因此,可绘制对象)。那么,对于这些,我相信它们是默认缓存的?

但是,ListView 图像是从网络上下载的图像,因此在我上面的示例中,自定义 ListViewItem 由 3 个视图(一个图像和 2 个标签)组成,图像将同时下载 ListView被渲染。我提供了一个只有 3 个视图的简单 ListView 项目,但它可能是 10 个视图(比如 4 个图像和 6 个标签)。重点是在ListView的渲染过程中,一张图片是从网上下载的。由于该图像可能会重复,如何避免下载相同的图像而使用缓存的图像?

首先关于项目列表

ListView 不会以您假设的这种低效方式工作。但是如果你真的关心效率和处理非常大的数据列表,建议使用 RecyclerView。两者的比较here有很好的讨论和答案。

如果您计划使用某些有限的文档类型图标集合来处理包含数千个文档的列表,最佳做法是使用位图的 HashMap,您将 link 到特定的列表项 (无论您选择使用什么:RecyclerViewListView).

这里有一个简单的例子(不正确java,只是为了提供一个想法)与 ListViews 的适配器 class:

public class DocumentsListAdapter extends ArrayAdapter<String> {
    private final HashMap<String, Bitmap> docIcons = HashMap<String, Bitmap>()
    private final Activity mContext;
    private final List<String> mItems;

    public DocumentsListAdapter(Activity context, List<String> items) {
         docIcons.put("docx", loadDocBitmapFromDisk());
         docIcons.put("xls", loadXlsBitmapFromDisk());
         docIcons.put("pdf", loadPdfBitmapFromDisk());
         // and so on...
         mItems = items;
         mContext = context;

    }

    @Override
    public View getView(int position, View view, ViewGroup parent) {
        LayoutInflater inflater = context.getLayoutInflater();
        View rowView= inflater.inflate(R.layout.list_single, null, true);

        TextView txtTitle = (TextView) rowView.findViewById(R.id.txt);
        ImageView imageView = (ImageView) rowView.findViewById(R.id.img);

        txtTitle.setText(mItems.get(position));

        if(items.get(position).endsWith(".docx")) {
            imageView.setImageBitmap(docIcons.get("docx"))

        } // and so on for other types...
    }
}

这个例子不包括你如何获得你的图标。您可以从互联网上下载它们,将它们存储在磁盘上,当应用程序需要这些图标时——您可以将它们作为位图从磁盘加载并在内存中仅存储一次每个位图并在视图中重用它们。将此逻辑与 RecyclerView 一起实施,您将获得相当高效的应用程序,它不会浪费内存并且响应速度很快。

您不必在适配器中存储 HashMap 和位图,但如果您只在一个地方使用它,这是一个不错的选择。如果您需要在不同的活动中使用它,请考虑将此 HashMap 保留在其他地方,这样每次用户在使用类似 ListViews.

的不同活动之间切换时不会重新加载它

处理具有相同图像的多个活动

关于活动,这取决于您如何存储图像。如果它们也从 Internet 下载并缓存在磁盘上,您可以按照此答案的 ListView 部分中的描述进行操作。只需将已加载到内存中的图像列表保存在某处并使用

imageView.setImageBitmap(someBitmap)

需要在activity的特定部分显示时。

如果您使用的是应用程序资源,那就更简单了,因为您可以这样做:

Drawable dr = context.resources.getDrawable(R.drawable.my_favourite_image);
imageView.setImageDrawable(dr);

所有与内存高效使用有关的工作都是在后台完成的。

如何避免下载相同的图像但使用缓存的图像

这取决于您将如何跟踪每张图像的独特性。例如。如果每个图像都有唯一的 URL ,最基本的解决方案是使用以图像的 URL 作为键的 HashMap。

更复杂但更灵活和正确的决定是使用 Guava caching tools. This is not android specific and very broad topic to cover in this answer, but you can take a look here.

尽管如此,如果您打算弄乱图像的复杂布局并关心性能,请检查 Ankothis benchmark。这与内容缓存无关,但是布局渲染性能可以提升3倍以上