问题 如何在Android上处理大型JSON对象


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


9247
2018-01-26 09:33


起源

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


答案:


发送 大尺寸 从服务器到移动的数据。 JSON重量轻。 如果要使用更有效的方式传递数据,则将其传递给分页。 如果你想使用 协议比JSON更轻 然后实现以下谷歌协议,它们非常有用,支持主要语言。 下面是较小的序列化数据结构。谷歌的数据交换协议。

1.Google协议

2.Flat Buffers

3.Nano-proto缓冲区

希望这对你有用。


9
2018-02-07 18:43





如果数据很大,那么尝试将其保存在数据库中,然后使用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/


4
2018-02-01 09:01



我会推荐杰克逊而不是GSON。在我的测试中,从InputStream解析一个非常大的JSON对象,前者大约快25%并且使用更少的内存。 - ban-geoengineering


第一件事:如果您的代码中存在崩溃或异常,您可能希望发布该代码。 “失败的Binder异常”有点太模糊,无法理解你在做什么。

如果你真的想发布你的Android应用程序内嵌JSON嵌入(为了避免从服务器获取它,考虑将其存储为资产并使用它访问它 AssetManager。您基本上将json放在应用程序的assets文件夹中,然后使用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数据存储在资产中,也可以使用它来解析JSON数据。只需将资产数据的InputStream传递给它,它的工作方式也一样。


2
2018-02-06 18:20





您可以按照建议将JSON嵌入到应用程序的代码中,但如果JSON是动态的,这将是一个糟糕的方法。然后,只要JSON发生更改,您就需要为应用程序推送更新。

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


0
2018-02-01 09:02



问题是我无法从Webservice获得那么大的JSON,你能告诉我如何获得它吗?谢谢。 - jason
使用GSON或Jackson库的Streaming API是一个不错的选择,正如本文其他答案中所建议的那样。但是,只有在无法更改WebService的输出时才应执行此操作。最好先分解WebService的输出。如果您无法做到这一点,那么请转到流API。 - Saraj Munjal


以下课程 ApiUrlClass.java 拥有您需要的所有方法。请阅读我写的课程评论。这将帮助您做您需要的事情。这也利用透明。

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;
    }

}

0
2018-02-08 07:28





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

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

您必须使用流式JSON解析器。 对于Android你可以使用这些2:

  1. GSON

  2. 杰克逊

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

我个人喜欢Gson。


0
2018-02-08 07:26