接口回調(diào)
在對(duì)象中,有些事件不確定什么時(shí)候能完成寨躁,此時(shí)可以通過(guò)預(yù)留監(jiān)督機(jī)制,關(guān)注事件的變化,這個(gè)機(jī)制即回調(diào)。
舉個(gè)例子
模擬下載器腋,實(shí)施監(jiān)督下載的進(jìn)度翎冲,當(dāng)100%時(shí)赏壹,提醒下載完成
1. 建立一個(gè)下載進(jìn)度的類
需要監(jiān)督加載的進(jìn)度,因此方法內(nèi)需要一個(gè)監(jiān)督進(jìn)度的形參接口
class Progress {
public void loadProgress(IProgressListener progressListener) {
//模擬進(jìn)度
for (int i = 1; i <= 100; i++) {
//進(jìn)度每改變一次,即調(diào)用更新一次監(jiān)督的數(shù)據(jù)
progressListener.curProgress(i);
}
}
}
2. 建立監(jiān)聽(tīng)的回調(diào)接口数苫,用來(lái)監(jiān)聽(tīng)進(jìn)度
interface IProgressListener {
void curProgress(int curProgress);
}
3.運(yùn)行調(diào)用
實(shí)際還是對(duì)多態(tài)的運(yùn)用
當(dāng)Progress類調(diào)用loadProgress(IProgressListener progressListener)時(shí)
傳入的實(shí)參是一個(gè)匿名內(nèi)部類谈山,也是IProgressListener的子類
相當(dāng)于 IProgressListener progressListener = new 子類();
此時(shí)調(diào)用的是子類的實(shí)現(xiàn)斜脂。
public class Main {
public static void main(String[] args) {
Progress progress = new Progress();
progress.loadProgress(new IProgressListener() {
@Override
public void curProgress(int curProgress) {
System.out.printf("curProgress:" + curProgress + "\r\n");
if (curProgress == 100)
System.out.printf("下載完成");
}
});
}
}
OkHttp
Android網(wǎng)絡(luò)請(qǐng)求的變遷
OkHttp的基本用法
前置工作
1. 添加依賴
OkHttp依賴:implementation 'com.squareup.okhttp3:okhttp:3.11.0'
Gson依賴:implementation 'com.google.code.gson:gson:2.8.5'
2. Android在做網(wǎng)絡(luò)請(qǐng)求時(shí)要添加權(quán)限
<uses-permission android:name="android.permission.INTERNET" />
注意
Android P 限制了明文流量的網(wǎng)絡(luò)請(qǐng)求涝动,非加密的流量請(qǐng)求都會(huì)被系統(tǒng)禁止鼻吮。
如果 WebView 的 url 用 http 協(xié)議,同樣會(huì)出現(xiàn)加載失敗肥照,https 不受影響。
如果當(dāng)前應(yīng)用的請(qǐng)求是 htttp 而非 https梧税,系統(tǒng)會(huì)禁止當(dāng)前應(yīng)用進(jìn)行該請(qǐng)求缓醋。
在 Android P 版本如果使用了明文流量褪贵,OkHttp3會(huì)拋出
CLEARTEXT communication to " + host + " not permitted by network security policy異常
解決方案
服務(wù)器和客戶端的請(qǐng)求最好都用https
OkHttp提供的API
Get請(qǐng)求
1. 首先創(chuàng)建OkHttpClient
OkHttpClient okHttpClient = new OkHttpClient();
2. 發(fā)送請(qǐng)求需要構(gòu)建Request對(duì)象
Request request = new Request.Builder()
.url(requestUrl())
.build();
3. okHttpClient 調(diào)用newCall()創(chuàng)建一個(gè)Call對(duì)象橄镜,并調(diào)用enqueue()
發(fā)送請(qǐng)求并獲取服務(wù)器返回的數(shù)據(jù)姊氓。
okHttpClient.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
}
@Override
public void onResponse(Call call, Response response) throws IOException {
if (response.isSuccessful()) {
String resJson = response.body().string();
Gson gson = new Gson();
User user = gson.fromJson(resJson, User.class);
}
}
});
Post請(qǐng)求
1. 需要構(gòu)建一個(gè)RequestBody對(duì)象來(lái)存放提交的參數(shù)
RequestBody body = new FormBody.Builder()
.add("userName", getUserName())
.add("userCode", getUserCode())
.build();
2. 將body 添加到Request.Builder中
Request request = new Request.Builder()
.url(requestUrl())
.post(body)
.build();
舉個(gè)例子
客戶端向服務(wù)器發(fā)送用戶輸入的用戶名瘦锹,用戶編號(hào)
服務(wù)端返回用戶的信息
1. 服務(wù)端
Module類
public class User {
String name;
int code;
String info;
}
@WebServlet(value = "/user")
public class UserServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
req.setCharacterEncoding("UTF-8");
resp.setContentType("text/html;charset=UTF-8");
Gson gson = new Gson();
String userName = req.getParameter("userName");
String userCode = req.getParameter("userCode");
if (userName.equals("admin") && userCode.equals("123")) {
User user = new User("admin", 1, "演示信息");
resp.getWriter().println(gson.toJson(user));
}
}
}
2. 客戶端
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
private EditText mEtUserName;
private EditText mEtUserCode;
private Button mBtnQuery;
private TextView mTvShowUserInfo;
@SuppressLint("HandlerLeak")
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 1:
User user = (User) msg.obj;
showUserInfo(user);
break;
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
initEvent();
}
private void initEvent() {
mBtnQuery.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
getUserInfo();
}
});
}
//網(wǎng)絡(luò)請(qǐng)求
private void getUserInfo() {
OkHttpClient okHttpClient = new OkHttpClient();
RequestBody body = new FormBody.Builder()
.add("userName", getUserName())
.add("userCode", getUserCode())
.build();
final Request request = new Request.Builder()
.url(requestUrl())
.post(body)
.build();
okHttpClient.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
}
@Override
public void onResponse(Call call, Response response) throws IOException {
if (response.isSuccessful()) {
String resJson = response.body().string();
Gson gson = new Gson();
User user = gson.fromJson(resJson, User.class);
Message message = Message.obtain();
message.what = 1;
message.obj = user;
mHandler.sendMessage(message);
}
}
});
}
private void initView() {
mEtUserName = findViewById(R.id.et_user_name);
mEtUserCode = findViewById(R.id.et_user_code);
mBtnQuery = findViewById(R.id.btn_query_data);
mTvShowUserInfo = findViewById(R.id.tv_user_info_show);
}
private String getUserName() {
return mEtUserName.getText().toString();
}
private String getUserCode() {
return mEtUserCode.getText().toString();
}
private void showUserInfo(User user) {
StringBuilder builder = new StringBuilder();
builder.append("UserName:" + user.getName())
.append("\r\n")
.append("UserCode:" + user.getCode())
.append("\r\n")
.append("UserInfo:" + user.getInfo());
mTvShowUserInfo.setText(builder.toString());
}
private String requestUrl() {
return "http://192.168.1.104:8080/user";
}
}
OkHttp封裝
思路
通過(guò)觀察OkHttp贴妻,不難發(fā)現(xiàn)所有的請(qǐng)求都是通過(guò)Request這個(gè)類構(gòu)建
為了保證不浪費(fèi)資源,將OkHttp設(shè)置為單例模式蝙斜。
1. 用枚舉區(qū)分Get名惩,Post,設(shè)置對(duì)外調(diào)用方法孕荠,獲取網(wǎng)絡(luò)請(qǐng)求
傳遞參數(shù)
1.請(qǐng)求的路徑
2.post請(qǐng)求時(shí)傳遞的參數(shù)
3.一個(gè)用于監(jiān)測(cè)請(qǐng)求完成的回調(diào)類
2. 定義OkHttp的請(qǐng)求doRequest()方法娩鹉,用于監(jiān)聽(tīng)請(qǐng)求成功失敗攻谁,請(qǐng)求前的狀態(tài)
傳遞參數(shù)
1.構(gòu)建的Request類
2.監(jiān)聽(tīng)回調(diào)類
3. 所有請(qǐng)求都用異步在子線程操作,修改UI需要Handler發(fā)送到主線程
這就需要根據(jù)成功失敗的回調(diào)弯予,創(chuàng)建發(fā)送的Handler方法
4. 定義回調(diào)類
1.請(qǐng)求成功
2.請(qǐng)求失敗
5. 設(shè)置回調(diào)的包裝類戚宦,幫助簡(jiǎn)化構(gòu)建不必要的方法
1. 設(shè)置回調(diào)類
public abstract class IBaseCallBack<T> {
public Type mType;
public IBaseCallBack() {
mType = getSuperclassTypeParameter(getClass());
}
/**
* 將Type類型轉(zhuǎn)換成Gson,解析
*/
static Type getSuperclassTypeParameter(Class<?> subclass) {
Type superclass = subclass.getGenericSuperclass();
if (superclass instanceof Class) {
throw new RuntimeException("Missing type parameter.");
}
ParameterizedType parameterized = (ParameterizedType) superclass;
return $Gson$Types.canonicalize(parameterized.getActualTypeArguments()[0]);
}
//請(qǐng)求成功前回調(diào)
public abstract void onRequestBefore();
//網(wǎng)絡(luò)請(qǐng)求成功后根據(jù)返回碼調(diào)用
public abstract void onSuccess(Response response, T t);
//請(qǐng)求失敗時(shí)
public abstract void onFailure(Call call, Exception e);
}
2. 將OkHttp封裝
public class OkHttpHelper {
private static OkHttpHelper mOkHttpHelper;
private static OkHttpClient mOkHttpClient;
private static Gson mGson;
private Handler mHandler;
private OkHttpHelper() {
mOkHttpClient = new OkHttpClient();
mGson = new Gson();
//每發(fā)現(xiàn)一條消息就推送到Handler
mHandler = new Handler(Looper.getMainLooper());
}
public static OkHttpHelper getOkHttpHelperInstance() {
if (mOkHttpHelper == null) {
synchronized (OkHttpHelper.class) {
if (mOkHttpHelper == null) {
mOkHttpHelper = new OkHttpHelper();
}
}
}
return mOkHttpHelper;
}
//來(lái)區(qū)別請(qǐng)求的類型
enum HttpMethodType {
GET, POST
}
/**
* 對(duì)外調(diào)用的Get锈嫩,Post請(qǐng)求
*/
public void get(String url, IBaseCallBack callBack) {
Request request = buildRequest(
url,
null,
HttpMethodType.GET);
doRequest(request, callBack);
}
public void post(String url, Map<String, String> parameter,
IBaseCallBack callBack) {
Request request = buildRequest(
url,
parameter,
HttpMethodType.POST);
doRequest(request, callBack);
}
/**
* 構(gòu)建Get或Post請(qǐng)求的Request
*
* @param url Url地址
* @param parameter Post請(qǐng)求所需要的參數(shù)
* @param methodType 通過(guò)枚舉來(lái)判斷是Get還是Post
* @return Request
*/
private Request buildRequest(String url,
Map<String, String> parameter,
HttpMethodType methodType) {
Request.Builder builder = new Request.Builder();
builder.url(url);
if (methodType == HttpMethodType.GET) {
builder.get();
} else {
//構(gòu)建參數(shù)獲取
RequestBody body = buildFormData(parameter);
builder.post(body);
}
return builder.build();
}
/**
* 構(gòu)建Post請(qǐng)求的body
*/
private RequestBody buildFormData(Map<String, String> parameter) {
FormBody.Builder body = new FormBody.Builder();
if (null != parameter) {
for (Map.Entry<String, String> entry : parameter.entrySet()) {
body.add(entry.getKey(), entry.getValue());
}
}
return body.build();
}
/**
* 通過(guò)構(gòu)建好的Get或Post請(qǐng)求的Request受楼,做網(wǎng)絡(luò)訪問(wèn)
*/
private void doRequest(final Request request, final IBaseCallBack callBack) {
callBack.onRequestBefore();
mOkHttpClient.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
requestFailure(callBack, call, e);
}
@Override
public void onResponse(Call call, Response response)
throws IOException {
if (response.isSuccessful()) {
String resultStr = response.body().string();
if (callBack.mType == String.class) {
requestSuccess(callBack, response, request);
} else {
try {
//防止Json解析錯(cuò)誤
Object obj = mGson.fromJson(resultStr, callBack.mType);
requestSuccess(callBack, response, obj);
} catch (JsonParseException e) {
requestFailure(callBack, call, e);
}
}
} else {
requestFailure(callBack, call, null);
}
}
});
}
private void requestFailure(final IBaseCallBack callBack,
final Call call,
final Exception e) {
mHandler.post(new Runnable() {
@Override
public void run() {
callBack.onFailure(call, e);
}
});
}
private void requestSuccess(final IBaseCallBack callBack,
final Response response,
final Object o) {
mHandler.post(new Runnable() {
@Override
public void run() {
callBack.onSuccess(response, o);
}
});
}
}
3. 設(shè)置接口的包裝類,幫助簡(jiǎn)化不必要的實(shí)現(xiàn)接口或者添加其他方法
注意:兩者同樣是抽象類呼寸,SpotsCallBack重寫(xiě)的抽象方法艳汽,在其子類是不會(huì)主動(dòng)提示覆蓋的
public abstract class SpotsCallBack<T> extends IBaseCallBack<T> {
ProgressDialog mDialog;
public SpotsCallBack(Context context) {
mDialog = new ProgressDialog(context);
mDialog.setCancelable(false);
mDialog.setCanceledOnTouchOutside(false);
}
public void showDialog() {
mDialog.show();
}
public void dismissDialog() {
mDialog.dismiss();
}
@Override
public void onRequestBefore() {
showDialog();
}
}
具體調(diào)用
private void getUserInfo() {
HashMap<String, String> paramester = new HashMap<>();
paramester.put("userName", getUserName());
paramester.put("userCode", getUserCode());
OkHttpHelper.getOkHttpHelperInstance()
.post(requestUrl(), paramester, new SpotsCallBack<User>(this) {
@Override
public void onSuccess(Response response, User user) {
dismissDialog();
showUserInfo(user);
}
@Override
public void onFailure(Call call, Exception e) {
dismissDialog();
}
});
}