【问题标题】:ArrayIndexOutOfBoundsException error... why?ArrayIndexOutOfBoundsException 错误...为什么?
【发布时间】:2012-04-04 12:19:10
【问题描述】:

我对这个有点迷茫。如何获得 OutOfBounds?是否有大小限制(除了 sizeof (int))?

也许是因为多线程可以来这里? UI 线程和 Service 线程?

java.lang.ArrayIndexOutOfBoundsException 在 kenyu73.realsignal.DatabaseWrapper.getSignalValues(DatabaseWrapper.java:137) 在 kenyu73.realsignal.DatabaseWrapper.getSignalValues(DatabaseWrapper.java:116) 在 kenyu73.realsignal.BarScaleGraph$buildGraphThread.drawGraph(BarScaleGraph.java:128) 在 kenyu73.realsignal.BarScaleGraph$buildGraphThread.execute(BarScaleGraph.java:94) 在 kenyu73.realsignal.BarScaleGraph$buildGraphThread.run(BarScaleGraph.java:74)

另外,我用一个静态实例来调用这些类方法。我认为线程正在竞争相同的变量???想法?

BarScaleGraph 类

ContentValues[] values = DatabaseWrapper.getInstance().getSignalValues(getContentResolver(), signal_type, false);

DatabaseWrapper 类

private static final DatabaseWrapper    instance    = new DatabaseWrapper();

// grab static instance so we only have one db wrapper
public static DatabaseWrapper getInstance() {
    return instance;
}

. . . .

public ContentValues[] getSignalValues(ContentResolver cr, int signal_type_id, boolean bGroupByLatLon) {

    String sWhere = "signal_type_id=" + signal_type_id;

    Cursor cursor;

    if (bGroupByLatLon) {
        cursor = cr.query(CONSTS.CONTENT_URI_GRP_LATLNG, null, sWhere, null, null);
    } else {
        cursor = cr.query(CONSTS.CONTENT_URI_LOGGER, null, sWhere, null, null);
    }

    ContentValues[] values = new ContentValues[cursor.getCount()];

    int count = 0;
    if (cursor.getCount() > 0) {
        cursor.moveToFirst();
        do {
            values[count] = new ContentValues(); // <--- LINE 137
            values[count].put("signal_value", cursor.getInt(cursor.getColumnIndex("signal_value")));
            values[count].put("latitude", cursor.getInt(cursor.getColumnIndex("latitude")));
            values[count].put("longitude", cursor.getInt(cursor.getColumnIndex("longitude")));
            values[count].put("timestamp", cursor.getLong(cursor.getColumnIndex("timestamp")));
            values[count].put("network", cursor.getString(cursor.getColumnIndex("network")));

            count++;

        } while (cursor.moveToNext());
    }
    cursor.close();

    return values;
}

EDIT:要试试这个 - 将 synchronized 添加到实例

// grab static instance so we only have one db wrapper
public static synchronized DatabaseWrapper getInstance() {
    return instance;
}

【问题讨论】:

  • 你从未在你的 sn-p 中声明一个数组。发布此内容将有助于我们解决您的问题。
  • 你在哪方面得到了例外?
  • 这种情况很少发生。我只收到过两份报告。该方法被多个线程调用。我想知道是不是这个原因?有一个 UI 和一个服务线程。
  • 更新帖子以包含可能的线程问题...
  • 您是否尝试过同步该方法?

标签: java android


【解决方案1】:

唯一合理的答案是 cursor.getCount() 返回的数字低于您的 do..while 循环进行的循环数。我在您的 do..while 循环的逻辑中没有看到错误(尽管这是不寻常的逻辑;见下文)。

我的猜测是它是一个实时游标,而其他东西是 deleting 在循环运行时添加相关行。找出答案的唯一真正方法是在代码中添加检测,以便您可以查看 cursor.getCount() 返回的内容、count 在每次循环迭代开始时的内容等。

如果您并不真正关心为什么并且只是希望它停止发生,您可以改用List

public ContentValues[] getSignalValues(ContentResolver cr, int signal_type_id, boolean bGroupByLatLon) {

    String sWhere = "signal_type_id=" + signal_type_id;

    Cursor cursor;

    if (bGroupByLatLon) {
        cursor = cr.query(CONSTS.CONTENT_URI_GRP_LATLNG, null, sWhere, null, null);
    } else {
        cursor = cr.query(CONSTS.CONTENT_URI_LOGGER, null, sWhere, null, null);
    }

    List<ContentValues> values = new LinkedList<ContentValues>();
    ContentValues entry;

    while (cursor.moveToNext()) {
        entry = new ContentValues();
        entry.put("signal_value", cursor.getInt(cursor.getColumnIndex("signal_value")));
        entry.put("latitude", cursor.getInt(cursor.getColumnIndex("latitude")));
        entry.put("longitude", cursor.getInt(cursor.getColumnIndex("longitude")));
        entry.put("timestamp", cursor.getLong(cursor.getColumnIndex("timestamp")));
        entry.put("network", cursor.getString(cursor.getColumnIndex("network")));
        values.add(entry);
    }
    cursor.close();

    return values.toArray(new ContentValues[values.size()]);
}

(或实现该效果的代码。)

我使用了一个临时链表,所以我不在乎 cursor.getCount 返回什么,完成后将其转换为数组。我还使用了更常见的循环游标的习惯用法(因为游标在第一行之前开始,while (cursor.moveToNext()) 是一种方便的循环方式),而不是(再次)我在您的do..while 中看到了逻辑错误,但我喜欢while (cursor.moveToNext()) 的简单直接。

【讨论】:

  • 感谢您的建议。从 UI 和服务线程调用此方法。您认为访问同一方法的两个线程会导致这种情况吗?我正在用更多信息更新我最初的问题。
  • @kenyu73:我不知道怎么做。光标不在线程之间共享,所有内容都是函数的本地内容。两个线程同时调用应该没问题。
  • 使用静态实例调用该方法。那会有什么不同吗? xyz = "DatabaseWrapper.getInstance().getSignalValues(..);"
  • @kenyu73:不,因为您没有在函数中使用实例字段。游标是局部的,每次调用函数都会得到自己的局部变量。
  • 那时我不知道。我的开发控制台中只有 2 份报告,但如果它与线程无关,那么我有点难过。
【解决方案2】:

我猜这是一个竞争条件,线程之间的计数是变化的...尝试同步方法:

public synchronized ContentValues[] getSignalValues(...){
    ...
}

如果以前的不适合你,总有这样的:

public ContentValues[] method1(...){
    synchronized (monitor1) {
        ...
    }
}

public ContentValues[] method2(...){
    synchronized (monitor2) {
        ...
    }
}

这将解决问题,但我会尝试使用不同的架构来阻止这种方法。

【讨论】:

  • ...很有可能。我想知道我是否可以在我的 getInstance() 上使用同步。这样任何方法都是安全的?只是猜测......我不是线程专家。我可以制作它们,但不能有效地解释它们(=
  • 如果这是一个竞争条件问题,您的建议不会克服它。两个线程都将以同步顺序获取实例,并且都将异步调用“getSignalValues”方法。
  • 好点。问题是我有很多可以多线程访问的公共方法。我想我必须在此调用中的所有方法上添加同步。这一切都假设它是一个竞争条件。我有很好的论据,在一个方法中拥有多个线程是不可能的,因为每个线程都会有自己的实例。我注意到如果使用静态实例会静音。我认为静态实例会使所有方法本质上都是静态的。
  • 很难快速证明这是否有效,但我会接受这个作为答案。唯一合乎逻辑的事情是竞争条件。如果它是一个计数/循环的事情,我会报告 1000 个错误,我自己会看到它......不过我会重写循环。
【解决方案3】:

您使用 do...while 循环,do while 循环将执行额外的迭代,因为在每次迭代之后检查条件,而不是之前。代码越过了你的保护条件并进入循环,然后当有一个结果时执行 2x。

将循环切换到 while 循环,这应该可以正常工作。

int count = 0;
    if (cursor.getCount() > 0) {
        cursor.moveToFirst();
        while (cursor.moveToNext()) {
            values[count] = new ContentValues();
            values[count].put("signal_value", cursor.getInt(cursor.getColumnIndex("signal_value")));
            values[count].put("latitude", cursor.getInt(cursor.getColumnIndex("latitude")));
            values[count].put("longitude", cursor.getInt(cursor.getColumnIndex("longitude")));
            values[count].put("timestamp", cursor.getLong(cursor.getColumnIndex("timestamp")));
            values[count].put("network", cursor.getString(cursor.getColumnIndex("network")));

            count++;

        }
    }
    cursor.close();

【讨论】:

  • 不,他的循环很好。这有点令人费解,但没关系。他预先检查计数是否大于零,移动到第一行,在循环体中处理它,然后移动到下一行。 moveToNext 返回 false 如果光标不在一行上,并且循环结束。
【解决方案4】:

虽然我不确定问题出在哪里,但您可以以更安全的方式解决问题:

List<ContentValues> values = new ArrayList<ContentValues>();
if (cursor.getCount() > 0) {
    cursor.moveToFirst();
    do {
        ContentValues value = new ContentValues();
        value.put("signal_value", cursor.getInt(cursor.getColumnIndex("signal_value")));
        values.add(value);
    } while (cursor.moveToNext());
}
// ...
return values.toArray(new ContentValues[0]);

【讨论】:

  • 感谢您的想法。如果我不能找出原因,ArrayList 是一个很好的解决方法。
【解决方案5】:

cursor.moveToFirst 可能返回 false。尝试将其包装在 if 语句中:

if (cursor.moveToFirst()) {
  do....
}

【讨论】:

  • 这只是提出了一个问题,为什么 getCount 返回 &gt; 0moveToFirst 返回 false
【解决方案6】:

这个呢,

int count = 0;
if (cursor.getCount() > 0) {
ContentValues[] values = new ContentValues[cursor.getCount()];
cursor.moveToFirst();
do {
        if(cursor.getCount() >= count)
        {
        values[count].put("signal_value", cursor.getInt(cursor.getColumnIndex("signal_value")));
        values[count].put("latitude", cursor.getInt(cursor.getColumnIndex("latitude")));
        values[count].put("longitude", cursor.getInt(cursor.getColumnIndex("longitude")));
        values[count].put("timestamp", cursor.getLong(cursor.getColumnIndex("timestamp")));
        values[count].put("network", cursor.getString(cursor.getColumnIndex("network")));
        }
        count++;
    }  while (cursor.moveToNext());
  }
cursor.close();

【讨论】:

  • @kmb385 - 我没有提到 while 部分。
  • 这将是一个可能会捕获错误的 hack,但理论上,getCount() 应该是死的。
  • @kenyu73 - 是的,您必须为 cursor.getCount() 声明 int 并在代码中使用该变量。
  • @user370305:他的do..while,虽然不是通常的做法,但很好。只有当光标下有一行时,他的逻辑才会成功循环。
  • @user370305:对。没有争议。问题是为什么。他的循环体应该只在光标下有一行时执行,逻辑不寻常,但没有明显缺陷。有一个预先的getCount() &gt; 0 检查,一个moveToFirst 将他带到身体处理的第一条记录,然后是一个moveToNext。如果moveToNext 成功(有第二条记录),则第二次执行循环体。所以这不是 do..whilewhile 的事情。这是另一回事。
猜你喜欢
  • 2014-06-27
  • 2021-07-12
  • 1970-01-01
  • 1970-01-01
  • 2021-08-04
  • 1970-01-01
  • 1970-01-01
  • 2014-01-01
  • 2012-02-23
相关资源
最近更新 更多