简短的回答,如果你的目标是~API 级别 7,是“不要”。这种情况在后来的 API 中可能有所改善,但事实上……我强烈建议完全避免使用 SyncAdapter;它的文档记录非常差,“自动”帐户/身份验证管理的价格很高,因为它的 API 也很复杂且文档不足。除了最琐碎的用例之外,API 的这一部分还没有经过深思熟虑。
所以这是我最终采用的模式。在我的活动中,我有一个处理程序,它从自定义处理程序超类中简单添加(可以检查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) {
...
}
...
}
}
该活动将调用 REST 请求,如下所示。注意,处理程序被传递给 WebClient 类(用于构建/发出 HTTP 请求等的帮助类)。WebClient 在接收到返回给活动的消息的 HTTP 响应时使用此处理程序,并让它知道数据已被接收,在我的情况下,它存储在 SQLite 数据库中(我会推荐)。在大多数活动中,我会调用并调用mHandler.stopHandler();以避免将 HTTP 响应信号返回给非活动活动等。事实证明,这是一种非常强大的方法。onPause()mHandler.startHandler();onResume()
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()在主线程上调用。因此,您可以在此处停止进度对话框并安全地执行其他活动内容。
我声明了一个 ContentProvider:
<provider android:name="au.com.myproj.android.app.webapi.WebAPIProvider"
android:authorities="au.com.myproj.android.app.provider.webapiprovider"
android:syncable="true" />
并实现它来创建和管理对 SQLite 数据库的访问:
public class WebAPIProvider extends ContentProvider
因此,您可以在活动中将光标移到数据库上,如下所示:
mCursor = this.getContentResolver().query (
WebAPIProvider.PRODUCTS_URI, null,
Utils.getProductsWhereClause(this), null,
Utils.getProductsOrderClause(this));
startManagingCursor(mCursor);
我发现org.apache.commons.lang3.text.StrSubstitutor该类在构建我必须与之集成的 REST API 所需的笨拙 XML 请求方面非常有帮助,例如,WebAPIRequestHelper我有一些辅助方法,例如:
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);
}
我会将其附加到适当的端点 URL。
以下是有关 WebClient 类如何处理 HTTP 请求的更多详细信息。这是processRequest()前面在 Runnable 中调用的方法。请注意handler用于将结果返回给上述ResponseHandlerI 的参数。SyncAdapter使用syncResultin out 参数来执行指数退避等。我在 中使用它executeRequest(),增加它的各种错误计数等。同样,文档记录非常差,PITA 开始工作。parseXML()利用了极好的Simple XML 库。
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))
{
...
}
...
}
您应该对 HTTP 请求设置一些超时时间,这样应用程序就不会在移动数据丢失或从 Wifi 切换到 3G 时永远等待。如果发生超时,这将导致抛出异常。
// 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);
所以总的来说,SyncAdapter 和 Accounts 的东西是一件非常痛苦的事情,我花了很多时间却没有收获。ContentProvider 相当有用,主要用于光标和事务支持。SQLite 数据库非常好。Handler 类很棒。我现在将使用 AsyncTask 类,而不是像上面那样创建自己的线程来生成 HTTP 请求。
我希望这个漫无边际的解释对某人有所帮助。