0

I have an app that loads data from a JSON URL, but it takes about 8 seconds to load and I believe this is because of parsing.
I want to know if is there a faster way to parse it faster and easier?

This is the function I am using to read the JSON:

public class LoadJson extends AsyncTask <String, Void, String> {


    @Override
    protected String doInBackground(String... params) {
        HttpURLConnection connection = null;
        BufferedReader reader = null;

        try {
            URL url = new URL(params[0]);
            connection = (HttpURLConnection) url.openConnection();
            connection.connect();
            InputStream stream = connection.getInputStream();
            reader = new BufferedReader(new InputStreamReader(stream));
            StringBuffer buffer = new StringBuffer();
            String line = "";
            while ((line = reader.readLine()) != null) {
                buffer.append(line);
            }
            String finalJson = buffer.toString();
            return finalJson;
        } catch (Exception e) {
            e.printStackTrace();
            return "Faild";
        }

    }
}

and:

 public JSONArray ExcuteLoad() {

    LoadJson task = new LoadJson();
    String resualt = null;

    try {
        resualt = task.execute("MY_JSON_FILE_URL").get();
        JSONObject json = new JSONObject(resualt);
        JSONArray jarray = json.getJSONArray("marcadores");


        return jarray;

    }

    catch (Exception e) {
        e.printStackTrace();
        return null;
    }
}

UPDATE 1:

Ok folks i change the code base on what you suggested about using onPostExecute but the problem is i can't return the value of jsonarray outside of asyncTask, got really confused....

public class LoadJson extends AsyncTask <String, Void, String> {


        public class LoadJson extends AsyncTask <String, Void, String> {

public interface AsyncResponse {
    void processFinish(String output);
}

public AsyncResponse delegate = null;

public LoadJson (AsyncResponse delegate){
    this.delegate = delegate;
}


    @Override
    protected String doInBackground(String... params) {
        String resualt = "";
        URL url;
        HttpURLConnection urlConnection = null;

        try {
            url = new URL(params[0]);
            urlConnection = (HttpURLConnection)url.openConnection();

            InputStream in = urlConnection.getInputStream();

            InputStreamReader reader = new InputStreamReader(in);

            int data = reader.read();

            while (data != -1) {
                char current = (char) data;
                resualt += current;
                data = reader.read();
            }
            return resualt;
        }
        catch (Exception e) {

            e.printStackTrace();

            return "Failed";
        }

    }

@Override
protected void onPostExecute(String result) {
    delegate.processFinish(result);
     }
 }

and My Fragment class:

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
    View rootView = inflater.inflate(R.layout.fragment_main, container, false);

    LoadJson asyncTask = (LoadJson) new LoadJson (new LoadJson.AsyncResponse(){

        @Override
        public void processFinish(String output){
            //Here you will receive the result fired from async class
            //of onPostExecute(result) method.
            try {
                JSONObject jsonObject = new JSONObject(output);
                JSONArray jsonArray = jsonObject.getJSONArray("marcadores");
            } catch (JSONException e) {
                e.printStackTrace();
            }
        }
    }).execute();
Blacksword
  • 331
  • 3
  • 17
  • http://square.github.io/retrofit/ – Akarsh M Sep 01 '16 at 14:02
  • 1
    Besides the strange parsing you are doing, you have a synchronous task with `task.execute().get()` – Murat Karagöz Sep 01 '16 at 14:05
  • You can use Jackson for parsing Json because it's fast and due to that most of Vendors uses Jakson for parsing JSON ie. DropBox,Box..... FYI:http://blog.takipi.com/the-ultimate-json-library-json-simple-vs-gson-vs-jackson-vs-json/ – ViramP Sep 01 '16 at 14:30
  • 2
    _I believe this is because of parsing_ - You believe this to be the parser but you don't know for sure. Instrument your code and find out where the problem is exactly! – Gary Bak Sep 01 '16 at 14:57
  • @MuratK. do you believe if i change it to work in 1 function it will work faster? – Blacksword Sep 02 '16 at 06:49
  • @GaryBak i'll look for it – Blacksword Sep 02 '16 at 06:50

1 Answers1

1

Your problem isn't parsing the JSON. You can't speed that up. Using a different library (probably) isn't going to make that faster either. (I'm talking about load times, not development time).

It comes down to how you make the request as well as your internet speed.

For example, this is not how you use an AsyncTask.

resualt = task.execute("MY_JSON_FILE_URL").get();

Because you just made an asynchronous call into a synchronous one. In other words, the get() method will block and wait for a result, therefore taking time and cause the data to load slowly.

Now, sure, libraries reduce the complexity of AsyncTask and make development "faster and easier", but the quick answer here is to actually use onPostExecute of the AsyncTask class to load the data asynchronously, off the main thread.

The best example I can give is Using a callback to return the data


Update

private JSONArray array;

private void parseJSON(JSONArray array) {
    this.array = array;
    // TODO: something
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
    View rootView = inflater.inflate(R.layout.fragment_main, container, false);

    LoadJson asyncTask = (LoadJson) new LoadJson (new LoadJson.AsyncResponse(){

        @Override
        public void processFinish(String output){
            //Here you will receive the result fired from async class
            //of onPostExecute(result) method.
            try {
                JSONObject jsonObject = new JSONObject(output);
                JSONArray jsonArray = jsonObject.getJSONArray("marcadores");

                for (int i = 0; i < jsonArray.length; i++) {
                    // TODO: Parse the array, fill an arraylist
                }
                // TODO: Set / notify an adapter

                // Or.... 
                parseJSON(jsonArray);

            } catch (JSONException e) {
                e.printStackTrace();
            }
        }
    }).execute();
Community
  • 1
  • 1
OneCricketeer
  • 179,855
  • 19
  • 132
  • 245
  • you are right but the problem is then i have to execute all of my code inside the: @Override void processFinish(String output){ otherwise i can't access my variable that passed from asynctask and executing the code in this override function is another pain in ... ! – Blacksword Sep 02 '16 at 11:01
  • You can do the network request and parsing the response to a JSONObject in the doInBackground. You can return a JSONObject to onPostExecute (not processFinish). From there, you update the UI thread. That's just how AsyncTask works. Other options like Volley, OkHttp, and Retrofit exist, but this is the answer without external libraries – OneCricketeer Sep 02 '16 at 13:20
  • yea but im using AsyncTask inside a separated class and want to pass it to my fragment class, that's the problem now if all of them be in a 1 class, yes but they are separated – Blacksword Sep 02 '16 at 13:31
  • Did you read the link at the bottom of my answer? It perfectly addresses the separate class problem – OneCricketeer Sep 02 '16 at 14:07
  • Ok, i updated my post and tried your method but it is not working for me please take a look and tell me what is wrong :( – Blacksword Sep 02 '16 at 16:50
  • Okay, what's the problem, now? Your Fragment should have a JSONArray. You haven't done anything with it; No return statement is needed. See update – OneCricketeer Sep 02 '16 at 17:16
  • yea it has, i defined it at the first line of Fragment class before onCreateView – Blacksword Sep 02 '16 at 17:21
  • You could return a JSONObject instead of a String, but that's your choice. Does my update make sense? – OneCricketeer Sep 02 '16 at 17:22
  • this is the point i was talking about, your update has the same approach, we can't take this jsonArray variable "JSONArray jsonArray = jsonObject.getJSONArray("marcadores");" outside of processFinish method we have to do all the codes insde processFinish if we wanna work with the result of AsyncTask – Blacksword Sep 02 '16 at 17:40
  • Yes, you can take it out. See the new update. The point is that the callback interface is the first point at which the data is actually returned. Just be aware of that, so don't use that variable elsewhere beforehand, otherwise it will be null. – OneCricketeer Sep 02 '16 at 19:48
  • yea i got null because of that now it is working but the load time haven't changed the only change is first the app load then the markers and clusters load and wont wait for the whole app to load i guess i should give the retrofit a try – Blacksword Sep 03 '16 at 13:10
  • As I said, Retrofit won't be faster. Matter of fact, it'll actually be slower because it has to actually convert the JSON response into a Java object. Your response string must be very very large – OneCricketeer Sep 03 '16 at 14:08
  • maybe it is because of calculations im doing on cluster markers on map going to try optimize the codes to see if it work faster or not. thanks by the way – Blacksword Sep 05 '16 at 09:24
  • @cricket_007 shouldn't you parse the JSON in `doInBackground()` as well? Because if the parsing takes long, the UI will get jerks or even stuck. I'd prefer moving `parseJSON(jsonArray);` line in the `onPostExecute()` and convert it to something like `finalResult(myJavaArray);`. The idea is to parse on background thread. – Sufian Sep 06 '16 at 10:03
  • @Sufian - probably, but returning the string or the JsonObject gives you full control over how to update the UI thread from onPostExecute. For example, you wouldn't need to write multiple AsyncTasks for returning various pieces of the JSON data – OneCricketeer Sep 06 '16 at 12:38