Sending location to webservice

Asked

Viewed 577 times

0

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

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

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

I researched but found few explanations for this problem.

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

Anyway, I was wondering if there is any lib that takes care of this problem, create a request queue and that if the internet connection falls, then wait, and when available redo the request.

Thank you.

1 answer

1


I personally do like you. I created a class that mounts the queue of uploads 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 there is a connection tries to run the queue. This solution is working very well.

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

http://developer.android.com/intl/pt-br/training/sync-adapters/index.html

CODE REQUESTED BY THE QUESTIONER

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

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 runWhat runs the request queue. As I said my program has requests that need to be sequential, sometimes I have a queue with 2 requests, in the first the server returns essential information pro 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 already got its answer. So I had to create a semaphore that only allows the runTry to run if you’re 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 answer, I needed to use a Requestfuture that blocks the Thread, so we can’t use the Thread UI not to lock 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 get the answer I parse the result, if successful I delete the Request from the a 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( );
    }
}

Since you will only send location, I don’t think it needs to be sequential, so you wouldn’t need to use Requestfuture or the traffic light (which is a piece of code that I found very confusing and susceptible to errors, every possibility of failure in this code I have to put Maphore = false, if forget the queue is stopped and will only run when called again. I intend to review in the future).

  • So, I’ve got a Syncadapter running... he’s the one who sends the data to the server. So I’m gonna go with that idea anyway, it should work out just fine. I use Syncadapter to send the data to the server, that is, I have 3 services, one to get the location another to run Syncadapter and the other is for the authentication account. Thank you for the reply.

  • Can you show me some of your code that you made your queue? Thank you.

  • I added the important parts of my code explained in the reply.

  • Thanks! Thank you so much. Your idea is pretty much the same as mine. Thanks so much for sharing. :)

Browser other questions tagged

You are not signed in. Login or sign up in order to post.