0

我拼命地试图修复一个错误:

  • 总是在我的 Android 版本 2.2、2.3 的模拟器中发生
  • 在模拟器 android 版本 4.* 中永远不会发生
  • 在真实设备中永远不会发生(android 4.*)

它是以下 IndexOutOfBoundsException 异常:

java.lang.RuntimeException: Unable to start activity
ComponentInfo{<myapppackage>}: java.lang.IndexOutOfBoundsException:
Invalid index 39, size is 0

在我的应用程序中,我正在从显示为文本的 json 文件中提取数据。我已经确定了错误的来源,即当我调用此方法时:

public String getItemValue(int id, String s) {      

    List<JsonItems> list = new ArrayList<JsonItems>();

    try {
        // CONVERT RESPONSE STRING TO JSON ARRAY
        JSONArray ja = new JSONArray(s);

        // ITERATE THROUGH AND RETRIEVE 
        int n = ja.length();            

        for (int i = 0; i < n; i++) {
            // GET INDIVIDUAL JSON OBJECT FROM JSON ARRAY
            JSONObject jo = ja.getJSONObject(i);

            // RETRIEVE EACH JSON OBJECT'S FIELDS
            JsonItems ji = new JsonItems();
            ji.id = jo.getInt("id");
            ji.text= jo.getString("text");
            list.add(ji);

        }
    } catch (Exception e) {
        e.printStackTrace();
    }
    return list.get(id).text; 
}

我的 JsonItems 类非常基础:

public class JsonItems{
int id;
String text;
}

我的 json 文件中的示例:

[
{"id":0,"text":"some text 0"},
{"id":1,"text":"some text 1"},
{"id":2,"text":"some text 2"}
]

这是我将 json 文件的内容处理为字符串的方法

public static String fromJsonFileToString(String fileName, Context c) {
    //JSONArray jArray = null;
    String text = "";
    try {
        InputStream is = c.getAssets().open(fileName);
        int size = is.available();
        byte[] buffer = new byte[size];
        is.read(buffer);
        is.close();
        text = new String(buffer);

    } catch (IOException e) {
        throw new RuntimeException(e);
    }
    return text;

}

我再重复一遍:IndexOutOfBoundsException 永远不会在装有 Android 4.* 的设备上发生,它仅在我在装有 Android 2.* 的模拟器上测试应用程序时才会发生

知道它来自哪里吗?

谢谢

4

1 回答 1

2

第一个问题

您没有正确读取输入流。从字面上调用就是这样做的 - 它会在调用available返回您可以读取的数据量。这个数字可能代表文件的全部内容,也可能不代表文件的全部内容

为您准备的阅读材料:

请注意,有一些帮助程序库,如Apache Commons IO,可以在一行代码 ( IOUtils.toString(inputStream)) 中读取文件内容。Android 尚不支持 Java 7,但在该版本中提供了一个值得注意的替代方法,即该Files.readAllLines方法。在任何情况下,您都可以对文件读取代码进行如下所示的更改,它应该会更好地工作:

public static String fromFileToString(String fileName, Context context) {
    try {
        BufferedReader reader = new BufferedReader(new InputStreamReader(
                context.getAssets().open(fileName)));

        String line = null;
        StringBuilder builder = new StringBuilder(1024);
        while ((line = reader.readLine()) != null) {
            builder.append(line);
        }

        return builder.toString();
    } catch (IOException e) {
        throw new RuntimeException(e);
    }
}

第二个问题:

您不进行任何绑定检查,以确保您传递给“搜索”方法的参数:

public String getItemValue(int id, String s)

不超过您最终计算的项目列表的长度:

return list.get(id).text;
//              ^
// -------------
// 'id' could be larger than the list size!

无论如何,您当前的设计根本不符合您真正想要做的事情,也就是确定 JSON 数组中具有与您提供给该方法的内容匹配的“id”字段的元素。您需要将 JSON 数据作为地图处理,以便能够执行此操作。

public String getItemValue(int id, String json) {
    if(json == null || json.trim().equals("")) return null;

    Map<Integer, JsonItems> map = new HashMap<Integer, JsonItems>(4);

    try {
        JSONArray ja = new JSONArray(json);

        int n = ja.length();

        for (int i = 0; i < n; i++) {
            JSONObject jo = ja.getJSONObject(i);

            JsonItems ji = new JsonItems();
            ji.id = jo.getInt("id");
            ji.text = jo.getString("text");
            map.put(Integer.valueOf(ji.id, ji);
        }
    } catch(NumberFormatException nfe) {
      throw new IllegalArgumentException("Invalid JSON format");  
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
    
    JsonItems item = map.get(id);
    return item != null ? item.text : null;
}

一些快速说明:

  • JsonItems 应该被称为 JsonItem,以符合良好的 Java 命名标准
  • 你真的应该只解析和存储一次 JSON,以提高性能
  • 您实际上只使用 JSON 数据的最小子集,您实际上可以在 for 循环中确定匹配节点并直接返回其值,而无需使用中间 Java bean 对象
于 2013-03-11T14:57:32.020 回答