本篇涉及比较基础,因目前有很多种网络请求框架,在这简单的介绍一下我在项目中使用的okhttp请求
首先加入okhttp的引入
implementation 'com.squareup.okhttp3:okhttp:3.7.0'
在加入gson的引入
implementation 'au.com.gridstone.rxstore:converter-gson:5.0.2'
好,接下来进入主题,首先创建一个 CcApiClient类 用户封装okhttp的类
private static final String TAG = "CcApiClient"; private static final String APIVersion = "1.0.0"; final MediaType JSON = MediaType.parse("application/json; charset=utf-8"); private final Context mContext; private String mBaseUri; private String mDevice; private String mRequestId = "0"; private final OkHttpClient client; private static final String[] API_URLS = {""};//多种服务器 private static final HashMap, List > calls = new HashMap<>();
这里面主要对读写超时做了设定,还有当网络遇到错误的时候我们可以进行构建其他节点获取数据,这个功能比较适应于开发国外的产品,当一个地方网络不好时,换一个服务器节点重新获取数据
private CcApiClient(final Context context) { this.mContext = context; this.setRouterIndex(-1); this.client = new OkHttpClient.Builder() .connectTimeout(10, TimeUnit.SECONDS) .writeTimeout(5, TimeUnit.SECONDS) .readTimeout(15, TimeUnit.SECONDS) .addInterceptor(new Interceptor() { @Override public Response intercept(Chain chain) throws IOException { Request request = chain.request(); try { return chain.proceed(request); } catch (Exception e) { if (!NetUtil.isConnected(mContext)) { //没有网络时, 抛出网络错误。 Logger.e(TAG, "error noconnect"); throw e; } else { if (Constent.DEBUG_VERSION) { throw e; } else { //构建其他节点请求 //return chain.proceed(buildOtherNodeRequest(request)); return chain.proceed(request); } } } } }) .build(); }
这块主要是作用于在程序写上一个功能,可以手动的更换服务器路径,如果程序只有一个服务器,那就直接写死就好
public void setRouterIndex(int index) { String debugUrl = UIUtils.getSputils().getString("API_BASE_URL_DEBUG", ""); if (!TextUtils.isEmpty(debugUrl)) { this.mBaseUri = debugUrl; return; } if (Constent.DEBUG_VERSION) {//如果是debug环境 改用debug服务器 this.mBaseUri = "";//开发服务器 return; } if (index > -1) { this.mBaseUri = API_URLS[index]; UIUtils.getSputils().putString("API_BASE_URL", this.mBaseUri); Logger.d(TAG, "************** Select URL Router: " + index + " **************"); } else { String baseUri = UIUtils.getSputils().getString("API_BASE_URL", ""); if (TextUtils.isEmpty(baseUri)) { this.mBaseUri = API_URLS[0]; } else { this.mBaseUri = baseUri; } } }
里面用到的方法,比如取消任务
public String getBaseUri() { return this.mBaseUri; } public void resetBaseUrl(String url) { this.mBaseUri = url; } public void setUserDevice(String device) { this.mDevice = device + "&api=" + APIVersion; } public void setRequestId(String requestId) { this.mRequestId = requestId; } public void cancelRequest(String requestId) { if (calls.containsKey(requestId)) { Listrequest_calls = calls.get(requestId); for (int i = 0; i < request_calls.size(); i++) { request_calls.get(i).cancel(); } request_calls.clear(); calls.remove(requestId); } }
先写上一个网络请求
/** * 获取用户信息 */ public void getUserInfo(OnCcListener listener) { CcListener mListener = new CcListener(listener, "getUserInfo"); Request("/v1.0/user/getUser", null, mListener, false); }
Request方法 主要用于检查 Context上下文和回调是否有值
private void Request(String path, String params, final CcListener listener, boolean isPost) { if (!NetUtil.isConnected(mContext)) { if (listener != null && listener.mListener != null) listener.mListener.onResponse(new CcApiResult(1, "Network connect failed")); return; } RequestURL(mBaseUri + path, params, listener, isPost); }
下面是RequestURL方法 主要针对get/post请求做出了一点相应的改变
private void RequestURL(String url, String params, final CcListener listener, boolean isPost) { Logger.d(TAG, "Request url:" + url); Logger.d(TAG, "Request params:" + params); Request.Builder builder; if (isPost) { if (TextUtils.isEmpty(params)) { params = ""; } RequestBody body = RequestBody.create(JSON, params); builder = new Request.Builder().url(url).post(body); } else { builder = new Request.Builder().url(url); } if (UIUtils.getSputils().getBoolean(Constent.LOADING_BROCAST_TAG, false)) { builder.addHeader(Constent.TOKEN, UIUtils.getSputils().getString(Constent.TOKEN, "")); Logger.d(TAG, "token " + UIUtils.getSputils().getString(Constent.TOKEN, "")); } else { builder.addHeader(Constent.TOKEN, ""); Logger.d(TAG, "token " + ""); } builder.addHeader("Content-Type", "application/json;charset=UTF-8'"); if (null != mDevice) { String device = mDevice + "&wifi=" + (NetUtil.isConnectedWifi(mContext) ? "1" : "0"); // builder.addHeader("X-Cc-Device", device); Logger.d(TAG, "X-Cc-Device " + device); } Call call = client.newCall(builder.build()); call.enqueue(listener); if (!calls.containsKey(mRequestId)) { calls.put(mRequestId, new ArrayList()); } calls.get(mRequestId).add(call); }
然后就是最重要的CcListener方法,针对成功/失败做出相应的处理
private class CcListener implements Callback { public final String mTag; public final OnCcListener mListener; public boolean shouldCheckRouter = false; public boolean handled = false; public CcListener(OnCcListener listener, String tag) { mTag = tag; mListener = listener; } @Override public void onFailure(Call call, IOException e) { if (call.isCanceled()) return; if (handled) return; //记录网络请求失败的原因 if (NetUtil.isConnected(mContext)) { Request request = call.request(); String url = request.url().toString().split("[?]")[0]; //请求超时不记录错误日志 if (!(e instanceof SocketTimeoutException) && !(e instanceof UnknownHostException)) { Logger.e(TAG, "Exception " + url + "=" + e); } } final int statusCode = 1; final String errMessage = "Network connect failed"; AndroidUtilities.runOnUIThread(new Runnable() { @Override public void run() { if (mListener != null) mListener.onResponse(new CcApiResult(statusCode, errMessage)); } }); } @Override public void onResponse(Call call, Response response) throws IOException { if (call.isCanceled()) return; Request request = call.request(); String url = request.url().toString(); if (response == null || !response.isSuccessful()) { int statusCode = 1; String errMessage = "Network connect failed"; if (null != response) { statusCode = response.code(); if (statusCode >= 500) { statusCode = 500; } else if (statusCode == 401 || statusCode == 403) { //不变 } else { statusCode = 1; } errMessage = response.message(); if (statusCode > 1) { Logger.e(TAG, new IOException("Unexpected code " + response)); } } if (handled) { Logger.d(TAG, url + ": too slow"); return; } handled = true; if (shouldCheckRouter) { for (int i = 0; i < API_URLS.length; i++) { if (url.startsWith(API_URLS[i])) { setRouterIndex(i); break; } } } Logger.d(TAG, "Error Response Status code:" + String.valueOf(statusCode) + ", Error message:" + errMessage); final int finalStatusCode = statusCode; final String finalErrMessage = errMessage; AndroidUtilities.runOnUIThread(new Runnable() { @Override public void run() { if (mListener != null) mListener.onResponse(new CcApiResult(finalStatusCode, finalErrMessage)); } }); } else { ResponseBody responseBody = response.body(); String arg0; try { arg0 = responseBody.string(); } catch (SocketTimeoutException e) { Logger.d(TAG, "read-timeout:" + url); final int statusCode = 1; final String errMessage = "Network connect failed"; AndroidUtilities.runOnUIThread(new Runnable() { @Override public void run() { if (mListener != null) mListener.onResponse(new CcApiResult(statusCode, errMessage)); } }); return; } catch (Exception ex) { Logger.d(TAG, "Exception:" + url); final int statusCode = 1; final String errMessage = "Network connect failed"; AndroidUtilities.runOnUIThread(new Runnable() { @Override public void run() { if (mListener != null) mListener.onResponse(new CcApiResult(statusCode, errMessage)); } }); return; } if (handled) { Logger.d(TAG, url + ": too slow"); return; } handled = true; if (shouldCheckRouter) { for (int i = 0; i < API_URLS.length; i++) { if (url.startsWith(API_URLS[i])) { setRouterIndex(i); break; } } } Logger.d(TAG, url + ": " + arg0); final CcApiResult mRes = new CcApiResult(); //数据被篡改 if (!arg0.startsWith("{")) { AndroidUtilities.runOnUIThread(new Runnable() { @Override public void run() { if (mListener != null) mListener.onResponse(new CcApiResult(1, "Network connect failed")); } }); return; } if (mTag.equals("doUserBind") || mTag.equals("getUserInfo") || mTag.equals("doFouceMatch") || mTag.equals("doUodatePassWord") || mTag.equals("doThreeLogin") || mTag.equals("doLogin") || mTag.equals("doFouceMatchBall") || mTag.equals("doRegistTrial")) { mRes.fromUserInfoResult(arg0); } else if (mTag.equals("doCategoryList")) { mRes.fromCategoryListResult(arg0); } else if (mTag.equals("doMyPayBallList") || mTag.equals("doMyPayBallUnCheck") || mTag.equals("doRecentType")) { mRes.fromMyBallHistoryResult(arg0); } else if (mTag.equals("doRecentType2")) { mRes.fromRecentType2Result(arg0); } else if (mTag.equals("doBankList")) { mRes.fromBankListResult(arg0); } else if (mTag.equals("doAccountList")) { mRes.fromAccountListResult(arg0); } else if (mTag.equals("doArticleList")) { mRes.fromArticleListResult(arg0); } else if (mTag.equals("doExpertGuessList")) { mRes.fromExpertGuessListResult(arg0); } else if (mTag.equals("doShareInfo")) { mRes.fromShareInfoResult(arg0); } else if (mTag.equals("doArticleDetails")) { mRes.fromArticleDetailsResult(arg0); } else if (mTag.equals("doCommentDetailsList")) { mRes.fromCommentDetailsListResult(arg0); } else if (mTag.equals("doRecommHot")) { mRes.fromRecommHotListResult(arg0); } else if (mTag.equals("doNotices")) { mRes.fromNoticesResult(arg0); } else if (mTag.equals("doVersionIsUpdate")) { mRes.fromVersionIsUpdateResult(arg0); } else if (mTag.equals("doMatchData")) { mRes.fromMatchDataResult(arg0); } else if (mTag.equals("foArticleListById")) { mRes.fromArticleListByIdResult(arg0); } else if (mTag.equals("doMatchVoice")) { mRes.fromMatchVoiceResult(arg0); } else if (mTag.equals("doMyInfoTypeList")) { mRes.fromMyInfoTypeListResult(arg0); } else if (mTag.equals("doReportList")) { mRes.fromRepostListResult(arg0); } else if (mTag.equals("doGiftHistory") || mTag.equals("doAwardExchangeList")) { mRes.fromGiftHistoryResult(arg0); } else if (mTag.equals("doAgentInfo")) { mRes.fromAgentInfoResult(arg0); } else if (mTag.equals("doAgentCodeList")) { mRes.fromAgentCodeListResult(arg0); } else if (mTag.equals("doAgentCode")) { mRes.fromAgentCodeResult(arg0); } else if (mTag.equals("doAgentCheckList")) { mRes.fromAgentCheckListResult(arg0); } else if (mTag.equals("doAgentRemark")) { mRes.fromAgentRemarkResult(arg0); } else if (mTag.equals("doThirdImage")) { mRes.fromThirdImage(arg0); } else if (mTag.equals("doGetInviteCode")) { mRes.fromInviteCodeResult(arg0); } else if (mTag.equals("doExpertMoney")) { mRes.fromExpertMoneyResult(arg0); } else if (mTag.equals("doExpertDetails")) { mRes.fromExpertDetailsResult(arg0); } else if (mTag.equals("doRecommentMatchDetails")) { mRes.fromRecommentMatchDetailsResult(arg0); } else if (mTag.equals("doExpertInfoByName")) { mRes.fromExpertInfoByNameResult(arg0); } else if (mTag.equals("doUserAddress")) { mRes.fromUserAddressResult(arg0); } else if (mTag.equals("doMyMsgList")) { mRes.fromMyMsgListResult(arg0); } else if (mTag.equals("doGetDemoUser")) { mRes.fromDemoUserResult(arg0); } else if (mTag.equals("doUserWithdrawalsDetails")) { mRes.fromUserWithdrawalsDetailsResult(arg0); } else if (mTag.equals("doMatchResult") || mTag.equals("doPayByIdList") || mTag.equals("doMatchBasketBallResult")) { mRes.fromMatchResult(arg0); } else if (mTag.equals("doMatchGuessList") || mTag.equals("doMatchBallGuessList") || mTag.equals("doMatchGuessLeague") || mTag.equals("doMatchBallGuessLeague")) { mRes.fromMatchGuessListResult(arg0); } else if (mTag.equals("doChargeList")) { mRes.fromChargeListResult(arg0); } else if (mTag.equals("doAllArticleList")) { mRes.fromAllArticleListResult(arg0); } else if (mTag.equals("doUploadUserInfo")) { mRes.fromUploadUserInfo(arg0); } else if (mTag.equals("doUserSign")) { mRes.fromUserSignResult(arg0); } else if (mTag.equals("doWithDrawalsHistory")) { mRes.fromWithDrawalsHistoryResult(arg0); } else if (mTag.equals("doDisportList")) { mRes.fromDisportListResult(arg0); } else if (mTag.equals("doSysMessageList")) { mRes.fromSysMessageListResult(arg0); } else if (mTag.equals("doMainDataList")) { mRes.fromMainDataListResult(arg0); } else if (mTag.equals("doScoreByIdList")) { mRes.fromScoreByIdListResult(arg0); } else if (mTag.equals("doMatchFilterList") || mTag.equals("doMatchBallFilterList") || mTag.equals("doMatchFilter") || mTag.equals("doMatchBasketBallFilterList")) { mRes.fromMatchFilterListResult(arg0); } else if (mTag.equals("doActivityList")) { mRes.fromActivityListResult(arg0); } else if (mTag.equals("doGetAccount")) { mRes.fromAccountResult(arg0); } else if (mTag.equals("doMatchFx")) { mRes.fromMatchFxResult(arg0); } else if (mTag.equals("doMatchInfoMessage") || mTag.equals("doMatchInfoUpTimeMessage") || mTag.equals("doMatchInfoDownTimeMessage")) { mRes.fromMatchInfoMessageResult(arg0); } else if (mTag.equals("doPayTypeList")) { mRes.fromPayTypeListResult(arg0); } else if (mTag.equals("doMatchCount")) { mRes.fromMatchCountResult(arg0); } else if (mTag.equals("doMatchDetailsEvent")) { mRes.fromMatchDetailsEventResult(arg0); } else if (mTag.equals("doMatchDetailsPlay")) { mRes.fromMatchDetailsPlayResult(arg0); } else if (mTag.equals("doRecommMatchList")) { mRes.fromRecommMatchListResult(arg0); } else if (mTag.equals("doScrollBallList") || mTag.equals("doMatchBallTypeList") || mTag.equals("doMatchBallTypeList2")) { mRes.fromScrollBallListResult(arg0); } else if (mTag.equals("doEarlyFootBallList") || mTag.equals("doMatchBallEarlyList")) { mRes.fromEarlyListResult(arg0); } else if (mTag.equals("doMyOrderList")) { mRes.fromMyOrderListResult(arg0); } else if (mTag.equals("doMyYlList")) { mRes.fromMyYlListResult(arg0); } else if (mTag.equals("doMyFollowArticle")) { mRes.fromMyFollowArticleResult(arg0); } else if (mTag.equals("doMyArticleList") || mTag.equals("doFouceArticleList")) { mRes.fromMyArticleListResult(arg0); } else if (mTag.equals("doAchieveList")) { mRes.fromAchieveListResult(arg0); } else if (mTag.equals("doTaskList")) { mRes.fromTaskListResult(arg0); } else if (mTag.equals("doDayTask")) { mRes.fromDayTaskResult(arg0); } else if (mTag.equals("doChargeHistoryList")) { mRes.fromChargeHistoryListResult(arg0); } else if (mTag.equals("doGuideData")) { mRes.fromGuideDataResult(arg0); } else if (mTag.equals("doFansList") || mTag.equals("doExpertHotList")) { mRes.fromFansListResult(arg0); } else if (mTag.equals("doMyFansList")) { mRes.fromMyFansListResult(arg0); } else if (mTag.equals("doMatchGuessCg") || mTag.equals("doMatchGuessByIds")) { mRes.fromMatchGuessCgResult(arg0); } else if (mTag.equals("doWcollectList")) { mRes.fromWcollectListResult(arg0); } else if (mTag.equals("doRecommPage")) { mRes.fromRecommPageResult(arg0); } else if (mTag.equals("doHotDataPage")) { mRes.fromHotDataPageResult(arg0); } else if (mTag.equals("doRecommentExpert")) { mRes.fromRecommExpertResult(arg0); } else if (mTag.equals("doFiveDataList")) { mRes.fromFiveDataListResult(arg0); } else if (mTag.equals("doDiamondExchange") || mTag.equals("doAwardExchangeProps2")) { mRes.fromDiamondExchangeResult(arg0); } else if (mTag.equals("doDiamondExchange2") || mTag.equals("doAwardExchangeProps")) { mRes.fromDiamondExchangeResult2(arg0); } else if (mTag.equals("doAwardTypeList")) { mRes.fromAwardTypeListResult(arg0); } else if (mTag.equals("doAwardTypeList2")) { mRes.fromAwardTypeList2Result(arg0); } else if (mTag.equals("doAwardMessageTypeList")) { mRes.fromAwardMessageTypeListResult(arg0); } else if (mTag.equals("doAwardTurn")) { mRes.fromAwardTurnResult(arg0); } else if (mTag.equals("doChampionList")) { mRes.fromChampionsResult(arg0); } else if (mTag.equals("doMatchBasketLastResult")) { mRes.fromMatchBasketLastResult(arg0); } else { mRes.fromDefaultResult(arg0); } if (mRes.getErrno() < 0 && TextUtils.isEmpty(mRes.getMessage())) { mRes.setMessage("抱歉, 我们的服务出现了一点故障, 工程师正在紧张修复中, 请稍候再试"); } AndroidUtilities.runOnUIThread(new Runnable() { @Override public void run() { if (mListener != null) mListener.onResponse(mRes); } }); } } }
最后一个就是CcResult针对返回数据做的处理,因为errno和meesgae还有status是每个接口都会返回的,所以抽取出来做公共
public class CcApiResult { private static final int ERRNO_OK = 0; private int errno; private String message; private boolean status; private Object data; public CcApiResult() { this.errno = -1; } public CcApiResult(int errno) { this.setErrno(errno); } public CcApiResult(int errno, String message) { this.setErrno(errno); this.setMessage(message); } public CcApiResult(int errno, String message, Object data) { this.setErrno(errno); this.setMessage(message); this.setData(data); } private void setErrno(int errno) { this.errno = errno; } public int getErrno() { return this.errno; } public void setStatus(boolean status) { this.status = status; } public boolean getStatus() { return this.status; } public void setMessage(String message) { this.message = message; } public String getMessage() { return this.message; } private void setData(Object data) { this.data = data; } public Object getData() { return this.data; } public boolean isOk() { return getErrno() == ERRNO_OK && status; }
把gson封装起来
class BooleanTypeAdapter implements JsonSerializer, JsonDeserializer { public JsonElement serialize(Boolean value, Type typeOfT, JsonSerializationContext context) { return new JsonPrimitive(value ? 1 : 0); } public Boolean deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { int code = json.getAsInt(); return code == 0 ? false : code == 1 ? true : null; } } private Gson getGson() { GsonBuilder builder = new GsonBuilder(); builder.registerTypeAdapter(Boolean.class, new BooleanTypeAdapter()); return builder.create(); }
解析返回数据
public CcApiResult fromCategoryListResult(String str) { Gson gson = getGson(); Listitems = new ArrayList<>(); try { JSONObject json = new JSONObject(str); this.setErrno(json.getInt("code")); this.setMessage(json.getString("msg")); this.setStatus(json.getBoolean("status")); if (json.has("data") && !json.isNull("data")) { JSONArray data = json.getJSONArray("data"); items = gson.fromJson(data.toString(), new TypeToken >() { }.getType()); } } catch (Exception e) { Logger.e("CcApiResult", e); } this.setData(items); return this; }
使用:
private void getNetDataWork(final int off, int lim) { String mDate = DateUtils.getCurTime("yyyyMMdd"); SSQSApplication.apiClient(0).getScrollBallList(true, 6, mDate, sType, leagueIDs, off, lim, new CcApiClient.OnCcListener() { @Override public void onResponse(CcApiResult result) { loadingDialog.dismiss(); swipeToLoadLayout.setRefreshing(false); swipeToLoadLayout.setRefreshEnabled(true); if (result.isOk()) { CcApiResult.ResultScorePage page = (CcApiResult.ResultScorePage) result.getData(); if (page != null && page.getItems() != null && page.getItems().size() >= 1) { defaultView.setVisibility(View.GONE); filterCell.beginRunnable(true); originalData = page.getItems(); totalPage = page.getTotalCount(); filterCell.setTotalPage(totalPage); filterCell.setCurrPage(off); adapter.setList(getData(getFilterData(page.getItems()))); } else { adapter.clearData(); filterCell.destoryRunnable(true); defaultView.setVisibility(View.VISIBLE); } isRefresh = false; } else { adapter.clearData(); filterCell.destoryRunnable(true); defaultView.setVisibility(View.VISIBLE); ToastUtils.midToast(mContext, result.getMessage(), 1000); } } }); }
最后附加上构造CcApiClient时用到的方法在Application中构建
mApiClient = CcApiClient.instance(mContext); mApiClient.setUserDevice(getUA());
获取用户手机信息
//获取用户手机信息 public static String getUA() { String deviceModel = "unknown"; String langCode = "en"; String appVersion = "unknown"; String systemVersion = "Android " + Build.VERSION.SDK_INT; String imei = "0000"; try { imei = DeviceIDUtil.getIMEI(mContext); langCode = LocaleController.getLocalLanguage(); deviceModel = Build.MANUFACTURER; PackageInfo pInfo = mContext.getPackageManager().getPackageInfo(mContext.getPackageName(), 0); appVersion = String.valueOf(pInfo.versionCode); systemVersion = "Android " + Build.VERSION.SDK_INT; } catch (Exception e) { Logger.e("Application", e); } return "imei=" + imei + "&model=" + deviceModel + "&language=" + langCode + "&version=" + appVersion + "&os=" + systemVersion; }
获取手机IMEI
public class DeviceIDUtil { private static String sIMEI = null; public static String getIMEI(Context context) { if (sIMEI != null) { return sIMEI; } String imei = UIUtils.getSputils().getString("IMEI", ""); if (!TextUtils.isEmpty(imei)) { sIMEI = imei; return sIMEI; } try { imei = ((TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE)).getDeviceId(); if (!TextUtils.isEmpty(imei)) { sIMEI = imei; } } catch (Exception ignored) { } if (TextUtils.isEmpty(imei)) { try { sIMEI = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ANDROID_ID); } catch (Exception ignored) { } } if (TextUtils.isEmpty(sIMEI)) { UUID uuid = UUID.randomUUID(); sIMEI = uuid.toString().replaceAll("-", ""); } UIUtils.getSputils().putString("IMEI", sIMEI); return sIMEI; } }