【问题标题】:How to deal with a large JSON object on Android如何处理 Android 上的大型 JSON 对象
【发布时间】:2016-01-26 09:33:57
【问题描述】:

我正在制作一个运行 ASP.NET WebService 的 Android 应用程序。 Webservice 发送一个 JSON 对象,应用程序解析该对象并显示在屏幕上。在一种情况下,JSON 对象太大,我收到 Failed Binder Transaction 错误。我的解决方案是获取该 JSON 对象并将其嵌入到应用程序代码中,这样就不需要从服务器获取该 JSON 对象。你能告诉我可以为这个问题做的任何其他事情吗? 或者你能告诉我如何从 Webservice 获取那个 JSON 对象吗?谢谢。

【问题讨论】:

  • 您能否提供large 的情况?喜欢超过 xxx Mb?下载整个 JSON 和解析可能都很昂贵。
  • 为什么要从服务器获取它,如果它是静态的并且可以在应用程序中发送,为什么要将它保存为 json?
  • 这个问题不是因为 json 文件,还有其他你忽略的东西。如果你想要一个解决方案,你应该附加 logcat 输出。

标签: android json web-services


【解决方案1】:

大尺寸数据从服务器发送到移动设备。 JSON 是轻量级的。 如果您想使用更有效的方式传递数据,请在分页中传递它。 如果您想使用比 JSON 更轻量级的协议,那么请实现以下 google 协议,这些协议非常有用,支持主要语言。 下面是较小的序列化数据结构。 Google 的数据交换协议。

1.Google Protocol

2.Flat Buffers

3.Nano-proto buffers

希望这对你有用。

【讨论】:

    【解决方案2】:

    如果数据很大,那么尝试将其保存在数据库中,然后使用 SQLite 进行处理。 (但如果它是动态的,则不推荐)

    要解析 json 对象,请使用 gson 或 jackson。这将有助于显着减少内存消耗,因为 json 数据被部分解析。 得到Gson,杰克逊在这里 https://sites.google.com/site/gson/gson-user-guide http://jackson.codehaus.org/

    杰克逊的例子 http://www.mkyong.com/java/jackson-streaming-api-to-read-and-write-json/

    【讨论】:

    • 我会推荐 Jackson 而不是 GSON。在我的测试中,从 InputStream 解析一个非常大的 JSON 对象,前者的速度大约快 25%,并且使用的内存更少。
    【解决方案3】:

    第一件事:如果您的代码中出现崩溃或异常,您可能需要发布它。 “Failed Binder Exception”有点含糊不清,无法理解您在做什么。

    如果你真的想在你的 Android 应用程序中嵌入 JSON(为了避免从服务器获取它,请考虑将其存储为资产并使用 AssetManager 访问它。你基本上删除带有 json 的文件在您应用的资产文件夹中并使用 AssetManager 将其读出。

    如果您仍想从服务器下载它并对其进行操作,请考虑使用流式 API 来下载和解析 JSON。 Android 的 JSONObject 不这样做,它坚持将整个 JSON 字符串保存在内存中,然后才能对其进行解析。

    如果您想直接从 URL 下载流式传输到流式解析器(例如 GSON),请尝试以下方式。首先从您要获取的 URL 中获取 InputStream:

        URL u = new URL(url);
        URLConnection conn = u.openConnection();
        InputStream is = new BufferedInputStream(conn.getInputStream());
    

    然后将该 InputStream 直接提供给您的流解析器。这应该可以避免在解析之前将整个响应拉入内存,但您仍然需要足够的内存来包含解析器创建的所有对象:

        GsonBuilder gb = new GsonBuilder(); // configure this as necessary
        Gson gson = gb.create();
        final Result response = gson.fromJson(
            new InputStreamReader(is, Charset.forName("UTF-8")),
            Result.class
        );
    

    这里的“结果”是一个包含来自 JSON 响应的数据的类。您必须确保所有映射都适用于您的数据,因此请阅读 GSON 并为您的情况做任何适用的事情。

    如果您将 JSON 数据存储在资产中,您也可以使用 GSON 来解析它。只需将资产数据的 InputStream 交给它,它的工作方式相同。

    【讨论】:

      【解决方案4】:

      以下类ApiUrlClass.java 具有您需要的所有方法。请阅读我写的课程的cmets。这将帮助你做你需要的。这也利用了透明。

      import android.graphics.Bitmap;
      import android.net.Uri;
      import android.os.Build;
      import android.util.Log;
      import org.apache.http.entity.mime.HttpMultipartMode;
      import org.apache.http.entity.mime.content.ByteArrayBody;
      import org.apache.http.entity.mime.content.ContentBody;
      import org.apache.http.entity.mime.content.StringBody;
      import org.json.JSONArray;
      import org.json.JSONException;
      import org.json.JSONObject;
      
      import java.io.BufferedReader;
      import java.io.BufferedWriter;
      import java.io.ByteArrayOutputStream;
      import java.io.FileNotFoundException;
      import java.io.IOException;
      import java.io.InputStream;
      import java.io.InputStreamReader;
      import java.io.OutputStream;
      import java.io.OutputStreamWriter;
      import java.net.HttpURLConnection;
      import java.net.SocketTimeoutException;
      import java.net.URL;
      import java.net.UnknownHostException;
      
      import javax.net.ssl.HttpsURLConnection;
      import javax.net.ssl.SSLPeerUnverifiedException;
      
      /*
      Usage of the class
      
      Create all the necessary API Call methods you need.
      And either use a Thread or AsyncTask to call the following.
      
          JSONObject response = ApiUrlCalls.login("username", "passowrd");
      
      After the response is obtained, check for status code like
      
          if(response.getInt("status_code") == 200){
              //TODO: code something
          } else {
              //TODO: code something
          }
      */
      
      public class ApiUrlCalls {
      
          private String HOST = "https://domain/path/"; //This will be concated with the function needed. Ref:1
      
          /*
              Now utilizing the method is so simple. Lets consider a login function, which sends username and password.
              See below for example.
          */
      
          public static JSONObject login(String username, String password){
      
              String functionCall = "login";
              Uri.Builder builder = new Uri.Builder()
                      .appendQueryParameter("username", username)
                      .appendQueryParameter("password", password);
      
              /*
                  The return calls the apiPost method for processing.
                  Make sure this should't happen in the UI thread, orelse, NetworkOnMainThread exception will be thrown.
              */
              return apiPost(builder, functionCall);
      
          }
      
          /*
              This method is the one which performs POST operation. If you need GET, just change it
              in like Connection.setRequestMethod("GET")
          */
          private static JSONObject apiPost(Uri.Builder builder, String function){
              try {
                  int TIMEOUT = 15000;
                  JSONObject jsonObject = new JSONObject();
                  try {
                      URL url = null;
                      String response = "";
      
                      /*
                          Ref:1
                          As mentioned, here below, in case the function is "login",
                          url looks like https://domain/path/login
      
                          This is generally a rewrited form by .htaccess in server.
                          If you need knowledge on RESTful API in PHP, refer 
                          http://stackoverflow.com/questions/34997738/creating-restful-api-what-kind-of-headers-should-be-put-out-before-the-response/35000332#35000332
      
                          I have answered how to create a RESTful API. It matches the above URL format, it also includes the .htaccess
                      */
      
                      url = new URL(HOST + function);
      
                      HttpsURLConnection conn = null;
                      conn = (HttpsURLConnection) url.openConnection();
                      assert conn != null;
                      conn.setReadTimeout(TIMEOUT);
                      conn.setConnectTimeout(TIMEOUT);
                      conn.setRequestMethod("POST");
                      conn.setDoInput(true);
                      conn.setDoOutput(true);
      
                      String query = builder.build().getEncodedQuery();
      
                      OutputStream os = conn.getOutputStream();
                      BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(os, "UTF-8"));
                      writer.write(query);
                      writer.flush();
                      writer.close();
                      os.close();
                      conn.connect();
      
      
                      int responseCode = conn.getResponseCode();
                      String responseMessage = conn.getResponseMessage();
                      jsonObject.put("status_code", responseCode);
                      jsonObject.put("status_message", responseMessage);
      
                      /*The if condition below will check if status code is greater than 400 and sets error status
                      even before trying to read content, because HttpUrlConnection classes will throw exceptions
                      for status codes 4xx and 5xx. You cannot read content for status codes 4xx and 5xx in HttpUrlConnection
                      classes. 
                      */
      
                      if (jsonObject.getInt("status_code") >= 400) {
                          jsonObject.put("status", "Error");
                          jsonObject.put("msg", "Something is not good. Try again later.");
                          return jsonObject;
                      }
      
                      String line;
                      BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream()));
      
                      while ((line = br.readLine()) != null) {
                          response += line;
                      }
                      //Log.d("RESP", response);
      
                      /*
                          After the actual payload is read as a string, it is time to change it into JSON.
                          Simply when it starts with "[" it should be a JSON array and when it starts with "{"
                          it is a JSONObject. That is what hapenning below.
                      */
                      if(response.startsWith("[")) {
                          jsonObject.put("content", new JSONArray(response));
                      }
                      if(response.startsWith("{")){
                          jsonObject.put("content", new JSONObject(response));
                      }
      
      
                  } catch(UnknownHostException e) {
                  //No explanation needed :)
                      jsonObject.put("status", "UnknownHostException");
                      jsonObject.put("msg", "Check your internet connection");
                  } catch (SocketTimeoutException){
                  //This is when the connection timeouts. Timeouts can be modified by TIMEOUT variable above.
                      jsonObject.put("status", "Timeout");
                      jsonObject.put("msg", "Check your internet connection");
                  } catch (SSLPeerUnverifiedException se) {
                  //When an untrusted SSL Certificate is received, this happens. (Only for https.)
                      jsonObject.put("status", "SSLException");
                      jsonObject.put("msg", "Unable to establish secure connection.");
                      se.printStackTrace();
                  } catch (IOException e) {
                  //This generally happens when there is a trouble in connection
                      jsonObject.put("status", "IOException");
                      jsonObject.put("msg", "Check your internet connection");
                      e.printStackTrace();
                  } catch(FileNotFoundException e){ 
                  //There is no chance that this catch block will execute as we already checked for 4xx errors
                      jsonObject.put("status", "FileNotFoundException");
                      jsonObject.put("msg", "Some 4xx Error");
                      e.printStackTrace();
                  } catch (JSONException e){ 
                  //This happens when there is a troble reading the content, or some notice or warnings in content, 
                  //which generally happens while we modify the server side files. Read the "msg", and it is clear now :)
                      jsonObject.put("status", "JSONException");
                      jsonObject.put("msg", "We are experiencing a glitch, try back in sometime.");
                      e.printStackTrace();
                  } return jsonObject;
      
              } catch (JSONException e) {
                  e.printStackTrace();
              }
              return null;
          }
      
      }
      

      【讨论】:

        【解决方案5】:

        您可以按照您的建议将 JSON 嵌入到应用的代码中,但如果 JSON 是动态的,这将是一种不好的方法。然后,您需要在 JSON 更改时为您的应用推送更新。

        更好的解决方案是对您从 WebService 生成的 JSON 进行分页,即将 JSON 分解为更小的部分,您可以在单独的 API 调用中按顺序获取这些部分。

        【讨论】:

        • 问题是我无法从 Webservice 获取这么大的 JSON,您能告诉我如何获取吗?谢谢。
        • 使用 GSON 或 Jackson 库的 Streaming API 是一个不错的选择,正如本文其他答案中所建议的那样。但是只有在您无法更改 WebService 的输出时才应该这样做。最好先分解 WebService 的输出。如果您无法做到这一点,请使用流式 API。
        【解决方案6】:

        最好尝试将 Json 对象分解为更小的对象并从 webService 获取,

        或部分获取数据,如果你不能这样做

        您必须使用流式 JSON 解析器。 对于 Android,您可以使用以下 2 个:

        1. GSON

        2. 杰克逊

        GSON Streaming 解释于:https://sites.google.com/site/gson/streaming

        我个人喜欢 Gson。

        【讨论】:

          猜你喜欢
          • 2015-12-11
          • 2018-10-17
          • 2022-01-19
          • 1970-01-01
          • 2022-01-12
          • 2018-12-06
          • 2012-06-06
          • 1970-01-01
          • 2022-06-15
          相关资源
          最近更新 更多