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,