接口回調
在對象中,有些事件不確定什么時候能完成,此時可以通過預留監督機制,關注事件的變化,這個機制即回調。
舉個例子
模擬下載,實施監督下載的進度,當100%時,提醒下載完成
1. 建立一個下載進度的類
需要監督加載的進度,因此方法內需要一個監督進度的形參接口
class Progress {
public void loadProgress(IProgressListener progressListener) {
//模擬進度
for (int i = 1; i <= 100; i++) {
//進度每改變一次,即調用更新一次監督的數據
progressListener.curProgress(i);
}
}
}
2. 建立監聽的回調接口,用來監聽進度
interface IProgressListener {
void curProgress(int curProgress);
}
3.運行調用
實際還是對多態的運用
當Progress類調用loadProgress(IProgressListener progressListener)時
傳入的實參是一個匿名內部類,也是IProgressListener的子類
相當于 IProgressListener progressListener = new 子類();
此時調用的是子類的實現。
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網絡請求的變遷
OkHttp的基本用法
前置工作
1. 添加依賴
OkHttp依賴:implementation 'com.squareup.okhttp3:okhttp:3.11.0'
Gson依賴:implementation 'com.google.code.gson:gson:2.8.5'
2. Android在做網絡請求時要添加權限
<uses-permission android:name="android.permission.INTERNET" />
注意
Android P 限制了明文流量的網絡請求,非加密的流量請求都會被系統禁止。
如果 WebView 的 url 用 http 協議,同樣會出現加載失敗,https 不受影響。
如果當前應用的請求是 htttp 而非 https,系統會禁止當前應用進行該請求。
在 Android P 版本如果使用了明文流量,OkHttp3會拋出
CLEARTEXT communication to " + host + " not permitted by network security policy異常
解決方案
服務器和客戶端的請求最好都用https
OkHttp提供的API
Get請求
1. 首先創建OkHttpClient
OkHttpClient okHttpClient = new OkHttpClient();
2. 發送請求需要構建Request對象
Request request = new Request.Builder()
.url(requestUrl())
.build();
3. okHttpClient 調用newCall()創建一個Call對象,并調用enqueue()
發送請求并獲取服務器返回的數據。
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請求
1. 需要構建一個RequestBody對象來存放提交的參數
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();
舉個例子
客戶端向服務器發送用戶輸入的用戶名,用戶編號
服務端返回用戶的信息
1. 服務端
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();
}
});
}
//網絡請求
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封裝
思路
通過觀察OkHttp,不難發現所有的請求都是通過Request這個類構建
為了保證不浪費資源,將OkHttp設置為單例模式。
1. 用枚舉區分Get,Post,設置對外調用方法,獲取網絡請求
傳遞參數
1.請求的路徑
2.post請求時傳遞的參數
3.一個用于監測請求完成的回調類
2. 定義OkHttp的請求doRequest()方法,用于監聽請求成功失敗,請求前的狀態
傳遞參數
1.構建的Request類
2.監聽回調類
3. 所有請求都用異步在子線程操作,修改UI需要Handler發送到主線程
這就需要根據成功失敗的回調,創建發送的Handler方法
4. 定義回調類
1.請求成功
2.請求失敗
5. 設置回調的包裝類,幫助簡化構建不必要的方法
1. 設置回調類
public abstract class IBaseCallBack<T> {
public Type mType;
public IBaseCallBack() {
mType = getSuperclassTypeParameter(getClass());
}
/**
* 將Type類型轉換成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]);
}
//請求成功前回調
public abstract void onRequestBefore();
//網絡請求成功后根據返回碼調用
public abstract void onSuccess(Response response, T t);
//請求失敗時
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();
//每發現一條消息就推送到Handler
mHandler = new Handler(Looper.getMainLooper());
}
public static OkHttpHelper getOkHttpHelperInstance() {
if (mOkHttpHelper == null) {
synchronized (OkHttpHelper.class) {
if (mOkHttpHelper == null) {
mOkHttpHelper = new OkHttpHelper();
}
}
}
return mOkHttpHelper;
}
//來區別請求的類型
enum HttpMethodType {
GET, POST
}
/**
* 對外調用的Get,Post請求
*/
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);
}
/**
* 構建Get或Post請求的Request
*
* @param url Url地址
* @param parameter Post請求所需要的參數
* @param methodType 通過枚舉來判斷是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 {
//構建參數獲取
RequestBody body = buildFormData(parameter);
builder.post(body);
}
return builder.build();
}
/**
* 構建Post請求的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();
}
/**
* 通過構建好的Get或Post請求的Request,做網絡訪問
*/
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解析錯誤
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. 設置接口的包裝類,幫助簡化不必要的實現接口或者添加其他方法
注意:兩者同樣是抽象類,SpotsCallBack重寫的抽象方法,在其子類是不會主動提示覆蓋的
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();
}
}
具體調用
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();
}
});
}