Submitting location to webservice

0

Hello, I have a problem, I have a service on android that captures the user's location in a given range.

Captured I send to the server, where it will take this location and save it in the database.

The problem is that maybe the user does not always have internet to send the location, so I need to save this location, so when the connection is available I send the location to webservice .

I researched but found few explanations for this problem.

Currently I am trying to use SharePreferences to save this location and I am creating queues for request, since the user can stay 3 days for example without connection, so the amount of data saved would be great (Then I send by parts) and would exceed the limit of the post, get or header to make the request.

Finally, I wanted to know if there is any lib that takes care of this problem, create a queue of requests and that if the connection to the internet drops, then wait, and when available redo the request.

Thank you.

    
asked by anonymous 25.02.2016 / 20:15

1 answer

1

I personally do just like you. I created a class that assembles the queue of submissions to the server, when I add a request to the end of the queue I try to send the requests if there is a connection. I also have a service that is called when the connection is changed and if connection tries to rotate the queue. This solution is working very well.

In Google documentation they recommend using Sync Adapters. When I created this part in my application I was not aware of this recommendation, if I had possibly tried to implement it that way. Unfortunately it is in English and is extensive and too comprehensive to summarize in a message here. You can find what you need to use a SyncAdapter here:

link

CODE REQUESTED BY QUESTION AUTHOR

First important thing in the class is to be a Singleton that I start in the Application's OnCreate, so the whole program will always have the same queue instance.

I create my request parameters as JSONObject, add them to a JSONArray with all requests, and save the array as text in the same SharedPreferences. I chose to do it this way because it is very easy to transform JSON to text and vice versa:

public void addToRequestQueue( final JSONObject request )
{
    try
    {
        String pendingRequests = mPref.getString( Globals.PENDING_REQUESTS, "[]" );

        JSONArray jArray = new JSONArray( pendingRequests );
        jArray.put( request );

        SharedPreferences.Editor mEditor = mPref.edit( );
        mEditor.putString( Globals.PENDING_REQUESTS, jArray.toString( ) );
        mEditor.apply( );
    }
    catch( JSONException e )
    {
        e.printStackTrace( );
    }

    runQueue( );
}

After that I have the runQueue method that runs the queue of requests. As I said my program has requests that need to be sequential, on some occasions I have a queue with 2 requests, in the first the server returns essential information for the second request. Soon in case there is no connection, I will not have this information returned by the server. To make the second request, I need to make sure that the first request has already obtained its response. So I had to create a semaphore that only allows runQueue to run if it is not expecting any response from the server.

public void runQueue( )
{
    try
    {
        if( semaphore )
        {
            return;
        }

        semaphore = true;
        int position = 0;

        String pendingRequests = mPref.getString( Globals.PENDING_REQUESTS, "[]" );
        JSONArray array = new JSONArray( pendingRequests );

        if( array.length( ) > position )
        {
            JSONObject request = array.getJSONObject( position );
            String tag = request.getString( "tag" );

            switch( tag )
            {
                case Globals.MONITOR:
                {
                    // Se necessário adicionar informações novas ao JSONObject request
                    sendRequest( position, tag, request );
                    break;
                }
                .
                .
                .
                default:
                    semaphore = false;
                    break;
            }
        }
        else
        {
            semaphore = false;
        }
    }
    catch( JSONException e )
    {
        semaphore = false;
        e.printStackTrace( );
    }
}

I use Volley to communicate with the server, but since I need to wait for the server to respond, I needed to use a RequestFuture that blocks the Thread, so we can not use the Thread UI to not crash the application:

public void sendRequest( final int position, final String tag, final JSONObject params )
{
    Thread thread = new Thread( )
    {
        @Override
        public void run( )
        {
            JSONObject result = syncVolleyRequest( tag, params );
            if( result != null )
            {
                parseRequestResult( position, tag, result );
            }
            else
            {
                semaphore = false;
            }
        }
    };
    thread.start( );
}

public JSONObject syncVolleyRequest( final String tag, final JSONObject params )
{
    ConnectionDetector cd = new ConnectionDetector( mContext );
    if( cd.hasActiveInternetConnection( ) )
    {
        RequestFuture< String > futureRequest = RequestFuture.newFuture( );
        StringRequest request =
                new StringRequest( Request.Method.POST, NetworkConfig.SERVER_URL, futureRequest,
                        futureRequest )
                {
                    @Override
                    protected Map< String, String > getParams( )
                    {
                        return getRequestParams( params );
                    }
                };
        request.setTag( tag );

        mRequestQueue = VolleyRequestQueue.getInstance( mContext ).getVolleyRequestQueue( );
        mRequestQueue.add( request );

        try
        {
            return new JSONObject( futureRequest.get( 30, TimeUnit.SECONDS ) );
        }
        catch( JSONException e )
        {
            e.printStackTrace( );

        }
        catch( InterruptedException e )
        {
            e.printStackTrace( );
            Thread.currentThread( ).interrupt( );
        }
        catch( ExecutionException e )
        {
            e.printStackTrace( );
        }
        catch( TimeoutException e )
        {
            semaphore = false;
            runQueue( );
            e.printStackTrace( );
            return null;
        }
    }

    semaphore = false;
    return null;
}

After I receive the answer I give parse in the result, if successful I delete the Request from the queue and rotate it again:

private void parseRequestResult( final int position, final String tag, final JSONObject result )
{
    try
    {
        if( ! result.getBoolean( "error" ) )
        {
            switch( tag )
            {
                case Globals.MONITOR:
                    .
                    .
                    .
                    break;
                .
                .
                .
                default:
                    break;
            }

            semaphore = false;

            deleteRequest( position );
            runQueue( );
        }
    }
    catch( JSONException e )
    {
        e.printStackTrace( );
    }

    semaphore = false;
}



private void deleteRequest( int position )
{
    try
    {
        JSONArray pivotArray = new JSONArray( );

        String pendingRequests = mPref.getString( Globals.PENDING_REQUESTS, "[]" );
        JSONArray array = new JSONArray( pendingRequests );

        for( int i = 0; i < array.length( ); i++ )
        {
            if( i != position )
            {
                pivotArray.put( array.getJSONObject( i ) );
            }
        }

        array = pivotArray;

        SharedPreferences.Editor mEditor = mPref.edit( );
        mEditor.putString( Globals.PENDING_REQUESTS, array.toString( ) );
        mEditor.apply( );
    }
    catch( JSONException e )
    {
        e.printStackTrace( );
    }
}

As you are only going to send a location, I do not think it needs to be sequential, so I would not need to use RequestFuture or the semaphore (which is a part of the code that I found very confusing and susceptible to errors,

    
25.02.2016 / 20:52