如果您的目标是 API 级别 7,简短的回答是“不要”。这种情况在后来的 API 中可能有所改善,但事实上……我强烈建议完全避免使用 SyncAdapter;它的记录非常糟糕,并且“自动”帐户/身份验证管理的代价很高,因为它的 API 也很复杂且记录不足。除了最琐碎的用例之外,API 的这一部分还没有经过深思熟虑。
这就是我最终采用的模式。在我的活动中,我有一个处理程序,其中包含来自自定义处理程序超类的简单添加(可以检查m_bStopped
bool):
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();
in onPause()
and mHandler.startHandler();
in onResume()
以避免 HTTP 响应被发送回非活动活动等。事实证明这是一个非常强大的方法。
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
参数,用于将结果消息返回给ResponseHandler
我在上面描述过。这syncResult
是 SyncAdapter 使用的 out 参数来进行指数退避等。我在executeRequest()
,增加它的各种错误计数等。同样,文档非常少,需要 PITA 才能工作。parseXML()
发挥了卓越的作用简单的 XML 库 http://simple.sourceforge.net/.
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 请求。
我希望这个漫无目的的解释对某人有所帮助。