【问题标题】:Optimization proposals for ListView with data from multiple "tables"来自多个“表”的数据的 ListView 优化建议
【发布时间】:2011-03-31 11:58:36
【问题描述】:

我刚遇到以下情况。我有一个 Android 应用程序,我猜它可能会在多个应用程序中发生。它是关于标记/标签/分类,随心所欲地调用它。我在SQLite DB中基本上有以下关系

 --------                 --------------              ---------
|  Tags  |               |  DeviceTags  |            | Devices |
|--------|               |--------------|            |---------|
| ID     | 1 ------ *    | ID           | * ------ 1 | ID      |
| NAME   |               | TAGS_ID      |            | NAME    |
 --------                | DEVICE_ID    |            | ...     |
                          --------------              ---------

所有内容都通过我编写的 ContentProvider 公开。到目前为止一切都很好。

在 UI 部分,我有一个 ListActivity 显示所有存储的设备(来自 Devices 表),为了进一步自定义 UI,我创建了自定义行项,根据设备类型等在前面显示一个小图像.

我现在想要实现的是在该列表上为每个设备显示相关标签。现在我的问题来了。对于简单的设备列表,我创建了一个自定义 ResourceCursorAdapter,我在 bindView 方法中设置了相应的信息

@Override
public void bindView(final View view, final Context context, final Cursor cursor) {
  final int objectId = cursor.getInt(cursor.getColumnIndex(Devices._ID));

  TextView deviceName = (TextView) view.findViewById(R.id.deviceName);
  deviceName.setText(...); //set it from the cursor
  ...
  TextView associatedTagsView = (TextView)...;
  associatedTagsView.setText(...); //<<<???? This would need a call to a different table
  ...
}

如您所见,为了能够知道我的设备关联了哪种标签,我需要查询 DeviceTags。所以我做了:

@Override
public void bindView(final View view, final Context context, final Cursor cursor) {
   ...
   TextView associatedTagsView = (TextView)view.findViewById(R.id.deviceTags);
   String tagsString = retrieveTagsString(view.getContext().getContentResolver(), objectId);
   ...
}

private String retrieveTagsString(ContentResolver contentResolver, int objectId) {
    Cursor tagForDeviceCursor =  contentResolver.query(DroidSenseProviderMetaData.TABLE_JOINS.TAG_DEVICETAG,...);
    if(tagForDeviceCursor != null && tagForDeviceCursor.moveToFirst()){
        StringBuffer result = new StringBuffer();

        boolean isFirst = true;
        do{
            if(!isFirst)
                result.append(", ");
            else
                isFirst = false;

            result.append(retrieve name from cursor column...);
        }while(tagForDeviceCursor.moveToNext());

        return result.toString();
    }       
    return null;
}

我对此进行了测试,它实际上工作得很好,但老实说,我觉得这样做并不好。不知怎的,我觉得很奇怪......

有没有更好的办法解决这个问题??

//编辑:

在 CommonsWare 的反馈之后,这里做了一点澄清。我对在 CursorAdapter 中对数据库进行第二次查询感到很奇怪,基本上这将导致每行一个查询,我担心这会严重影响我的性能(我仍然需要在具有大量的真实设备上对其进行测试数据,看看这有多大影响)。

我的问题因此是关于在给定我的数据模型的情况下是否有一些策略来避免这种情况,或者我是否必须基本上“忍受”它:)

【问题讨论】:

    标签: android optimization listview


    【解决方案1】:

    您在 Twitter 上抱怨您没有收到任何反馈。我并不感到非常惊讶,因为虽然您的问题具有堪称典范的详细程度,但它存在两个主要缺陷:

    1. 最后,您的问题含糊不清
    2. 您的问题取决于您自己对“这个”是什么的解释,我们不知道这一点

    因此,我不清楚你认为什么是“奇怪的”。 do...while() 循环? :-)

    我要冒个险,我猜你关心的是对ListView 中的每一行进行第二次查询和一轮字符串连接。这完全取决于您是否认为“以逗号分隔的标签列表”是数据模型的一部分或表示的一部分。如果它是演示文稿的一部分,我看不出你如何能避免你在这里做的事情。

    如果您真的认为它是您的数据模型的一部分,请调整您的 ContentProvider 以提供逗号分隔的标签列表作为您首先查询的任何内容的一部分(未显示)。

    在任何一种情况下,性能都是一个可能的问题。每行执行一次查询,尤其是在滚动时,可能会很糟糕。毕竟,Romain Guy 反复强调您需要在适配器中回收行,而且我不得不想象数据库查询比膨胀布局要贵一点点。不幸的是,您使用的数据模型强调随时间变化的空间,因此您将不得不在沿线的某个地方向吹笛者付款。

    【讨论】:

    • 抱歉我的问题有点含糊。没注意到,现在修好了。但是你完美地匹配了我的问题(+1):) 性能就是问题所在。但是考虑到 1 个设备可能有多个标签的要求,我将不得不使用上面显示的 DB 设计......当然我可以做的是以某种方式“合并”DeviceTags 和标签,但我想这会变得非常难看...也许我最好加入 ContentProvider 级别的所有内容我已经准备好数据...如果我发现当前解决方案严重影响性能,我会尝试这样做。
    • 我也会尽量避免适配器中的第二个查询。只需以列表中所需的所有数据在向用户显示的那一刻出现的方式构建您的内容提供程序。
    • @Juri:“但是考虑到 1 个设备可能有多个标签的要求,我将不得不使用上面显示的数据库设计”——不,你不需要。仅当您还从标签端获取数据时才需要它,并且频率足以使其值得。您的替代方法是对数据进行非规范化,将逗号分隔的标签列表作为设备表中的一列。
    • 是的,当然可以,但正如您正确猜测的那样,我还必须从标签端访问数据。基本上,用户在 multi_choice 列表视图(在弹出窗口中)中获得可用标签的列表,然后他们可以选择他们想要添加的标签。标签可能是可变的,并在稍后的时间点从服务器获取......但是,是的,避免查询另一个游标的唯一方法是准备内容提供程序中已有的数据或更改数据模型。
    【解决方案2】:

    我用大约 100 个条目测试了我的解决方案(在我的问题中提出)。它仍然可以使用,但滚动并不像人们想象的那么流畅。正如 CommonsWare 已经指出的那样,最好的办法是调整数据模型或至少正确聚合它。最后,光标包含在 UI 上显示所需的所有数据

    我不想完全更改底层数据模型。我所做的是以某种方式聚合数据,s.t. ContentProvider 返回的光标包含设备的数据,包括已经准备好的表示相关标签名称的字符串:

    | ID | NAME             | ... | LABELSSTRING   |
    ------------------------------------------------
    | 1  | "My Device name" | ... | "Work, Friend" |
    | 1  | "Some other"     | ... |                |
    | 1  | "Yet another"    | ... | "Work"         |
    

    这几乎解决了这个问题,因为不需要在 CursorAdapter 的 bindView(..) 中为每个设备重新查询,顺便说一句。很糟糕。使用 SQLite 的 group_concat(X, Y) function 可以实现此解决方案。

    【讨论】:

      猜你喜欢
      • 2016-12-10
      • 2011-07-03
      • 2011-10-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多