Respuesta breve, si su objetivo es ~ API nivel 7, es "Do not". La situación puede haber mejorado en posteriores API, pero tal como estaba ...Recomiendo encarecidamente evitar SyncAdapter por completo; está documentado muy mal y la gestión "automática" de la cuenta/autenticación tiene un alto precio, ya que la API también es intrincada y está poco documentada. Esta parte de la API no ha sido pensada más allá de los casos de uso más triviales.
Así que aquí está el patrón Terminé yendo con. Dentro de mis actividades que tenía un controlador con una simple adición de una superclase controlador personalizado (podría comprobar si hay un bool m_bStopped
):
private ResponseHandler mHandler = new ResponseHandler();
class ResponseHandler extends StopableHandler {
@Override
public void handleMessage(Message msg) {
if (isStopped()) {
return;
}
if (msg.what == WebAPIClient.GET_PLANS_RESPONSE) {
...
}
...
}
}
La actividad invocaría las solicitudes REST como se muestra a continuación. Tenga en cuenta que el controlador se transfiere a la clase WebClient (una clase auxiliar para crear/realizar solicitudes HTTP, etc.). WebClient utiliza este controlador cuando recibe la respuesta HTTP para enviar un mensaje a la actividad y le informa que los datos se han recibido y, en mi caso, se han almacenado en una base de datos SQLite (que recomendaría). En la mayoría de las actividades, que yo llamaría mHandler.stopHandler();
en onPause()
y mHandler.startHandler();
en onResume()
para evitar la respuesta HTTP que se marcó de nuevo a una actividad inactiva etc. Esto resultó ser un enfoque bastante robusto.
final Bundle bundle = new Bundle();
bundle.putBoolean(WebAPIRequestHelper.REQUEST_CREATESIMKITORDER, true);
bundle.putString(WebAPIRequestHelper.REQUEST_PARAM_KIT_TYPE, sCVN);
final Runnable runnable = new Runnable() { public void run() {
VendApplication.getWebClient().processRequest(null, bundle, null, null, null,
mHandler, NewAccountActivity.this);
}};
mRequestThread = Utils.performOnBackgroundThread(runnable);
Handler.handleMessage()
se invoca en el hilo principal. De modo que puede detener sus diálogos de progreso aquí y realizar otras actividades de forma segura.
I declaró un ContentProvider:
<provider android:name="au.com.myproj.android.app.webapi.WebAPIProvider"
android:authorities="au.com.myproj.android.app.provider.webapiprovider"
android:syncable="true" />
y puesto en práctica para crear y gestionar el acceso a la base de datos SQLite:
public class WebAPIProvider extends ContentProvider
modo que lo pueda conseguir cursores sobre la base de datos en sus actividades como esta :
mCursor = this.getContentResolver().query (
WebAPIProvider.PRODUCTS_URI, null,
Utils.getProductsWhereClause(this), null,
Utils.getProductsOrderClause(this));
startManagingCursor(mCursor);
Encontré la clase org.apache.commons.lang3.text.StrSubstitutor
para ser imme nly útil en la construcción de las torpes solicitudes XML requeridas por la API REST que tuve que integrar con, por ejemplo, en WebAPIRequestHelper
tuve métodos auxiliares como:
public static String makeAuthenticateQueryString(Bundle params)
{
Map<String, String> valuesMap = new HashMap<String, String>();
checkRequiredParam("makeAuthenticateQueryString()", params, REQUEST_PARAM_ACCOUNTNUMBER);
checkRequiredParam("makeAuthenticateQueryString()", params, REQUEST_PARAM_ACCOUNTPASSWORD);
valuesMap.put(REQUEST_PARAM_APIUSERNAME, API_USERNAME);
valuesMap.put(REQUEST_PARAM_ACCOUNTNUMBER, params.getString(REQUEST_PARAM_ACCOUNTNUMBER));
valuesMap.put(REQUEST_PARAM_ACCOUNTPASSWORD, params.getString(REQUEST_PARAM_ACCOUNTPASSWORD));
String xmlTemplate = VendApplication.getContext().getString(R.string.XMLREQUEST_AUTHENTICATE_ACCOUNT);
StrSubstitutor sub = new StrSubstitutor(valuesMap);
return sub.replace(xmlTemplate);
}
Qué me anexar a la URL de punto final apropiado.
Aquí hay más detalles sobre cómo la clase WebClient hace las peticiones HTTP. Este es el método processRequest()
llamado anteriormente en Runnable. Observe el parámetro handler
que se utiliza para enviar los resultados al ResponseHandler
que describí anteriormente. El syncResult
es en el parámetro fuera utilizado por el SyncAdapter hacer retardo exponencial, etc. Yo lo uso en el executeRequest()
, incrementando sus varias cuentas de errores, etc. Una vez más, muy poco documentadas y una pita para conseguir trabajo. parseXML()
aprovecha el excelente Simple XML lib.
public synchronized void processRequest(Account account, Bundle extras, String authority, ContentProviderClient provider, SyncResult syncResult, Handler handler, Context context)
{
// Helper to construct the query string from the query params passed in the extras Bundle.
HttpUriRequest request = createHTTPRequest(extras);
// Helper to perform the HTTP request using org.apache.http.impl.client.DefaultHttpClient.
InputStream instream = executeRequest(request, syncResult);
/*
* Process the result.
*/
if(extras.containsKey(WebAPIRequestHelper.REQUEST_GETBALANCE))
{
GetServiceBalanceResponse xmlDoc = parseXML(GetServiceBalanceResponse.class, instream, syncResult);
Assert.assertNotNull(handler);
Message m = handler.obtainMessage(WebAPIClient.GET_BALANCE_RESPONSE, xmlDoc);
m.sendToTarget();
}
else if(extras.containsKey(WebAPIRequestHelper.REQUEST_GETACCOUNTINFO))
{
...
}
...
}
Usted debe poner un poco de los tiempos de espera en las peticiones HTTP por lo que la aplicación no espera siempre si los datos móviles se retira, o se cambia de Wifi a 3G. Esto provocará una excepción si se produce el tiempo de espera.
// Set the timeout in milliseconds until a connection is established.
int timeoutConnection = 30000;
HttpConnectionParams.setConnectionTimeout(httpParameters, timeoutConnection);
// Set the default socket timeout (SO_TIMEOUT) in milliseconds which is the timeout for waiting for data.
int timeoutSocket = 30000;
HttpConnectionParams.setSoTimeout(httpParameters, timeoutSocket);
HttpClient client = new DefaultHttpClient(httpParameters);
Así que en general, el SyncAdapter y cuentas material era un dolor total y me costó mucho tiempo sin ganancia. ContentProvider fue bastante útil, principalmente para el soporte de cursor y transacción. La base de datos SQLite fue realmente buena. Y la clase Handler es increíble.Usaría la clase AsyncTask ahora en lugar de crear tus propios subprocesos como lo hice arriba para generar las solicitudes HTTP.
Espero que esta explicación divagante ayude a alguien un poco.
Estoy luchando con una [configuración similar] (http://stackoverflow.com/questions/11906172/synchronize-android-client-and-rest-server). Quizás puedas ayudar. – JJD