Java tutorial
/*Copyright (C) 2012 Crow Hou (crow_hou@126.com) Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.*/ package crow.api; import java.io.InputStream; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.ThreadPoolExecutor; import org.apache.http.Header; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.HttpStatus; import org.apache.http.client.CookieStore; import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.client.protocol.ClientContext; import org.apache.http.cookie.Cookie; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.message.BasicNameValuePair; import org.apache.http.params.BasicHttpParams; import org.apache.http.params.HttpConnectionParams; import org.apache.http.protocol.BasicHttpContext; import org.apache.http.protocol.HTTP; import org.apache.http.protocol.HttpContext; import android.content.Context; import android.os.Handler; import android.os.Message; import crow.api.parser.ParseException; import crow.api.parser.Parser; import crow.cache.Cache; /** * * @author crow * * @param <C> * APIContext,APIContext ? * @param <T> * APIRequest,APIRequest ?* * @param <R> * APIResponse */ @SuppressWarnings("hiding") public abstract class ApiClient<C extends ApiContext, R extends ApiResponse, T extends ApiRequest<? extends R, C>> { private static final int SEND_MESSAGE = 1; /** Api ? ?? milliseconds */ public int API_CONNECT_TIMEOUT = 15 * 1000; /** Api ? ?? milliseconds */ public int API_SOCKET_TIMEOUT = 30 * 1000; /** ????? UTF-8 */ public String PARAM_ENCODING = HTTP.UTF_8; private CookieStore cookieStore; private HttpContext localContext; private Parser parser; // ?? private ThreadPoolExecutor threadPool; private Cache cache; private Handler handler; // ? private Map<T, Runnable> runnableSet = new HashMap<T, Runnable>(); public ApiClient(Context context, Parser parser, ThreadPoolExecutor threadPool, Cache cache, CookieStore store) { this.parser = parser; this.threadPool = threadPool; this.cache = cache; this.handler = new Handler(context.getMainLooper()) { @Override public void handleMessage(Message msg) { switch (msg.what) { case SEND_MESSAGE: RequestRunnable<?, ?> r = (RequestRunnable<?, ?>) msg.obj; r.sendMessageToCallback(); break; } } }; // Create a local instance of cookie store cookieStore = store; // Create local HTTP context localContext = new BasicHttpContext(); // Bind custom cookie store to the local context localContext.setAttribute(ClientContext.COOKIE_STORE, cookieStore); } /** * ?Api Api ??? * * @return ????? */ public abstract C getApiContext(); /** * ? Post? ?ApiPost ?? ??????? * * @param request * * @return Post ? */ protected abstract Map<String, String> makeRequestParam(T request); /** * ?Http ? * * @param request * * @return */ protected abstract Header[] makeRequestHeaders(T request); /** * Api ? 1.? 2.? * * @param <Q> * Api * @param request * Api * @param callback * */ public final <Q extends R> void request(T request, ApiCallback<Q> callback) { if (!runnableSet.containsKey(request)) { // ?? RequestRunnable<T, Q> r = new RequestRunnable<T, Q>(request, callback, false); runnableSet.put(request, r); threadPool.execute(r); } } public final <Q extends R> void requestWithDirty(T request, ApiCallback<Q> callback) { if (!runnableSet.containsKey(request)) { RequestRunnable<T, Q> r = new RequestRunnable<T, Q>(request, callback, true); runnableSet.put(request, r); threadPool.execute(r); } } /** * ?request ? * * @param request */ public final void cancel(T request) { Runnable r; if ((r = runnableSet.remove(request)) != null) { threadPool.remove(r); // ?? } } /** * Api * * @param request * @return * @throws ApiException */ private final boolean postToCache(T request) throws ApiException { // 1.http? BasicHttpParams httpParams = new BasicHttpParams(); // HttpConnectionParams.setConnectionTimeout(httpParams, API_CONNECT_TIMEOUT); HttpConnectionParams.setSoTimeout(httpParams, API_SOCKET_TIMEOUT); HttpPost httpPost = new HttpPost(request.getRequestURL(getApiContext())); httpPost.setHeaders(makeRequestHeaders(request)); // DefaultHttpClient httpClient = new DefaultHttpClient(httpParams); // Request ? Map<String, String> param = makeRequestParam(request); // ??? ArrayList<BasicNameValuePair> postData = new ArrayList<BasicNameValuePair>(); for (Map.Entry<String, String> m : param.entrySet()) { postData.add(new BasicNameValuePair(m.getKey(), m.getValue())); } try { // ?UTF-8? UrlEncodedFormEntity entity = new UrlEncodedFormEntity(postData, PARAM_ENCODING); // ? httpPost.setEntity(entity); // ?? HttpResponse httpResponse = httpClient.execute(httpPost, localContext); // ? if (httpResponse.getStatusLine().getStatusCode() == HttpStatus.SC_OK || httpResponse.getStatusLine().getStatusCode() == HttpStatus.SC_CREATED) { List<Cookie> cookies = httpClient.getCookieStore().getCookies(); for (Cookie c : cookies) { cookieStore.addCookie(c); } HttpEntity httpEntity = httpResponse.getEntity(); InputStream input = httpEntity.getContent(); return cache.cache(input, request); } else { throw new ApiException("Api HttpStatusCode:" + httpResponse.getStatusLine().getStatusCode()); } } catch (Exception e) { throw new ApiException("Api ?", e); } } /** * Api * * @param request * @return * @throws ApiException */ private final boolean getToCache(T request) throws ApiException { // 1.http? BasicHttpParams httpParams = new BasicHttpParams(); // HttpConnectionParams.setConnectionTimeout(httpParams, API_CONNECT_TIMEOUT); HttpConnectionParams.setSoTimeout(httpParams, API_SOCKET_TIMEOUT); // Request ? Map<String, String> param = makeRequestParam(request); StringBuilder basicUrl = new StringBuilder(request.getRequestURL(getApiContext())); basicUrl.append("?"); // ??? for (Map.Entry<String, String> m : param.entrySet()) { basicUrl.append(m.getKey()).append("=").append(m.getValue()).append("&"); } HttpGet httpGet = new HttpGet(basicUrl.toString()); httpGet.setHeaders(makeRequestHeaders(request)); DefaultHttpClient httpClient = new DefaultHttpClient(httpParams); try { // ?? HttpResponse httpResponse = httpClient.execute(httpGet); // ? if (httpResponse.getStatusLine().getStatusCode() == HttpStatus.SC_OK || httpResponse.getStatusLine().getStatusCode() == HttpStatus.SC_CREATED) { List<Cookie> cookies = httpClient.getCookieStore().getCookies(); for (Cookie c : cookies) { cookieStore.addCookie(c); } HttpEntity httpEntity = httpResponse.getEntity(); InputStream input = httpEntity.getContent(); return cache.cache(input, request); } else { throw new ApiException("Api HttpStatusCode:" + httpResponse.getStatusLine().getStatusCode()); } } catch (Exception e) { throw new ApiException("Api ?", e); } } private class RequestRunnable<P extends T, Q extends R> implements Runnable { private P request; private ApiCallback<Q> callback; private boolean readDirty; private Q response; private ApiException e; public RequestRunnable(P request, ApiCallback<Q> callback, boolean readDirty) { this.request = request; this.callback = callback; this.readDirty = readDirty; } public void sendMessageToCallback() { runnableSet.remove(request); // runnable if (response != null) { // if (response.isSuccess()) { callback.onSuccess(response); } else { callback.onFail(response); } } else { // ? callback.onException(e); } } @SuppressWarnings("unchecked") @Override public void run() { if (!cache.isExpired(request)) { // try { response = (Q) parser.parse(cache.getCacheFile(request), request.getResponseClass()); // ? handler.sendMessage(handler.obtainMessage(SEND_MESSAGE, this)); return; } catch (ParseException e) { // do nothing } } // ?? try { if (request.getHttpRequestClass().equals(HttpPost.class)) { if (!postToCache(request)) { // ?? throw new ApiException("" + request.toString()); } } else if (request.getHttpRequestClass().equals(HttpGet.class)) { if (!getToCache(request)) { // ?? throw new ApiException("" + request.toString()); } } else { throw new ApiException( "HttpApiRequestgetHttpRequestClass()?HttpPost.classHttpGet.class" + request.toString()); } } catch (ApiException e) { this.e = e; if (!readDirty) { // ?? handler.sendMessage(handler.obtainMessage(SEND_MESSAGE, this)); return; } } try { response = (Q) parser.parse(cache.getCacheFile(request), request.getResponseClass()); if (!response.isSuccess()) { // ? cache.clearCache(request); } } catch (ParseException e) { this.e = new ApiException("??", e); } handler.sendMessage(handler.obtainMessage(SEND_MESSAGE, this)); } } }