Preface: Encapsulation is only to deepen their understanding, there are already very good encapsulation online, I also draw on Okgo and Hongyang okHttputils. This project is based on MVC mode, but this article only talks about how to encapsulate OKHTTP (here I follow the most basic steps, need additional functions, see the source code and this article understanding, can certainly be implemented).
The features we want to package are:
- Support for GET requests
- Support for POST requests
- Uploading files
- Support download files and resumable breakpoint
- Support cache when network is available (validity period when network is connected)
- Disconnect from the network, support offline cache (offline cache validity period)
- Whether to request the same URL more than once while the network is still requesting it
- Supports automatic reconnection when a request fails
First look at the effect OF my encapsulation (suggest opening permissions)
A get request | A post request | Upload a file |
---|---|---|
The download file | ||
First, okHTTP makes a simple GET request like this:
OkHttpClient okHttpClient = new OkHttpClient.Builder()
.readTimeout(10, TimeUnit.SECONDS)
.writeTimeout(10, TimeUnit.SECONDS)
.build();
Request.Builder mBuilder = new Request.Builder();
mBuilder.url("url? parm1=x&parm2=y");
mBuilder.header("head"."headValue");
Request okHttpRequest = mBuilder.build();
okHttpClient.newCall(okHttpRequest).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {}@Override
public void onResponse(Call call, Response response) throws IOException {}});Copy the code
Get request parameters are spelled after the URL, and above it is the asynchronous request enqueue, which is in the child thread. Callbacks onFailure and onResponse are also in the child thread, so we can put time-consuming operations like parsing in here, but we can’t change the UI directly, we have to switch it to the main thread.
1. Package EasyOk
1.1 Canceling a Network Request
//tag cancels network request
public void cancleOkhttpTag(String tag) {
Dispatcher dispatcher = okHttpClient.dispatcher();
synchronized (dispatcher) {
// Cancel the network request in the request list
for (Call call : dispatcher.queuedCalls()) {
if(tag.equals(call.request().tag())) { call.cancel(); }}// Cancel the network request if you are requesting the network
for (Call call : dispatcher.runningCalls()) {
if(tag.equals(call.request().tag())) { call.cancel(); }}}}Copy the code
As you can see, cancel the okHttpClient that the code is requesting, so we want to keep okHttpClient unique, and EasyOk is going to use the singleton here
1.2 Simple EasyOk package
public class EasyOk {
private static EasyOk okHttpUtils;
private OkHttpClient okHttpClient;
// This handler is used to switch child threads to the main thread. In a later implementation of the interface, there is no need to use a handler to call back
private Handler mDelivery;
private EasyOk(a) {
mDelivery = new Handler(Looper.getMainLooper());
okHttpClient = new OkHttpClient.Builder()
.hostnameVerifier(new HostnameVerifier() {// Certificate trust
@Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
})
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(10, TimeUnit.SECONDS)
.writeTimeout(10, TimeUnit.SECONDS)
.build();
}
public static EasyOk getInstance(a) {
if (okHttpUtils == null) {
okHttpUtils = new EasyOk();
}
return okHttpUtils;
}
public OkHttpClient getOkHttpClient(a) {
return okHttpClient;
}
public Handler getmDelivery(a) {
return mDelivery;
}
//tag cancels network request
public void cancleOkhttpTag(String tag) {
Dispatcher dispatcher = okHttpClient.dispatcher();
synchronized (dispatcher) {
// Cancel the network request in the request list
for (Call call : dispatcher.queuedCalls()) {
if(tag.equals(call.request().tag())) { call.cancel(); }}// Cancel the network request if you are requesting the network
for (Call call : dispatcher.runningCalls()) {
if (tag.equals(call.request().tag())) {
call.cancel();
}
}
}
}
}
Copy the code
MDelivery is the handler that switches the child thread to the main thread. Because it’s best to keep all the repetitive work in the package. The most important advantage of encapsulation is convenience.
According to the original OKHTTP get Request, we still lack Request, and a callback for network Request, so see below.
2. Encapsulate OkGetBuilder(here I encapsulate each request into a different Builder, which repeats a lot, but is clearer and easier to understand)
Since the body of the Request requires new for every Request, you can imagine that this is not a singleton, and every time the Request is called, the Request comes out of new. From the original GET request, we knew that OkGetBuilder needed 1, URL, 2, parameters, 3, header, 4, tag, 5, and its own network callback.
2.1 Custom Request Callback Abstract Class (recommended interface class based on MVC secondary encapsulation)
So we have to have a network callback interface, and I’m using an abstract class here, ResultMyCall, and the nice thing about using an abstract class here is that we can put the same repeated operations in the parent class, and if we don’t override methods, we’ll implement them in the parent class, so sometimes you just have to override an onSuccess method, Unlike interfaces, methods are not implemented entirely. Note that if you’re going to be MVC based, it’s best to use the interface ResulCall, which we’ll cover when we talk about MVC. Now we all follow the abstract class ResultMyCall. The abstract class is as follows ResultMyCall
public abstract class ResultMyCall<T> {
// Loading is usually displayed before requesting a network
public void onBefore(a) {}// The network ends and the loading disappears
public void onAfter(a) {}// Monitor the progress of uploading images (currently support image uploading, other rewrite this method is invalid)
public void inProgress(float progress) {}// Error message
public void onError(String errorMessage) {
ToastUtils.showToast(errorMessage);
}
public void onSuccess(Object response) {}// If T is a generic type, this method takes the type of the generic type, which is used for parsing. If T is not a generic type, the default return is String
public Type getSuperclassTypeParameter(Class
subclass) {
Type superclass = subclass.getGenericSuperclass();
if (superclass instanceof Class) {
return null;
}
ParameterizedType parameterized = (ParameterizedType) superclass;
return $Gson$Types.canonicalize(parameterized.getActualTypeArguments()[0]);
}
public Type getType(a) {
returngetSuperclassTypeParameter(getClass()); }}Copy the code
I believe this class is also easy to understand, so OnError here I wrote a Toast. If you need to do something else, such as not playing Toast or opening another page, you can override this method as follows:
@Override
public void onError(String errorMessage) {
super.onError(errorMessage);
/ / comment super. OnError (errorMessage); So instead of using the superclass method,
}
Copy the code
2.2 Simple OkGetBuilder package (part of the code has been removed, and clarity is preferred)
public class OkGetBuilder {
private String url;
private String tag;
private Map<String, String> headers;
private Map<String, String> params;
private OkHttpClient okHttpClient;
private Context context;
private Handler mDelivery;
private Request okHttpRequest;
public OkGetBuilder(a) {
this.okHttpClient = EasyOk.getInstance().getOkHttpClient();
this.context = MyApplication.getContext();
this.mDelivery = EasyOk.getInstance().getmDelivery();
}
public OkGetBuilder build(a) {
Request.Builder mBuilder = new Request.Builder();
if(params ! =null) {
mBuilder.url(appendParams(url, params));
} else {
mBuilder.url(url);
}
if(! TextUtils.isEmpty(tag)) { mBuilder.tag(tag); }if(headers ! =null) {
mBuilder.headers(appendHeaders(headers));
}
okHttpRequest = mBuilder.build();
return this;
}
public void enqueue(final ResultMyCall resultMyCall) {
if(resultMyCall ! =null) {
mDelivery.post(new Runnable() {
@Override
public void run(a) { resultMyCall.onBefore(); }}); } okHttpClient.newCall(okHttpRequest).enqueue(new Callback() {
@Override
public void onFailure(Call call, final IOException e) {
if(resultMyCall ! =null) {
mDelivery.post(new Runnable() {
@Override
public void run(a) {
resultMyCall.onAfter();
String errorMsg;
if (e instanceof SocketException) {
} else {
if (e instanceof ConnectException) {
errorMsg = context.getString(R.string.network_unknow);
} else if (e instanceof SocketTimeoutException) {
errorMsg = context.getString(R.string.network_overtime);
} else{ errorMsg = context.getString(R.string.server_error); } resultMyCall.onError(errorMsg); }}}); }}@Override
public void onResponse(Call call, final Response response) throws IOException {
// The network request succeeded
if (response.isSuccessful()) {
if(resultMyCall ! =null) {
String result = response.body().string();
Object successObject = null;
try {
if (resultMyCall.getType() == null) {
successObject = result;
} else{ successObject = GsonUtil.deser(result, resultMyCall.getType()); }}catch (Throwable e) {
mDelivery.post(new Runnable() {
@Override
public void run(a) {
resultMyCall.onAfter();
resultMyCall.onError("Data parsing error"); }});return;
}
if (successObject == null) {
successObject = result;
}
final Object finalSuccessObject = successObject;
mDelivery.post(new Runnable() {
@Override
public void run(a) { resultMyCall.onAfter(); resultMyCall.onSuccess(finalSuccessObject); }}); }}else {
// The interface request did succeed, code is not 200
if(resultMyCall ! =null) {
final String errorMsg = response.body().string();
mDelivery.post(new Runnable() {
@Override
public void run(a) { resultMyCall.onAfter(); resultMyCall.onError(errorMsg); }}); }}}}); }private Headers appendHeaders(Map<String, String> headers) {
Headers.Builder headerBuilder = new Headers.Builder();
if (headers == null || headers.isEmpty()) return null;
for (String key : headers.keySet()) {
headerBuilder.add(key, headers.get(key));
}
return headerBuilder.build();
}
The get argument is spelled after the URL
private String appendParams(String url, Map<String, String> params) {
StringBuilder sb = new StringBuilder();
if (url.indexOf("?") = = -1) {
sb.append(url + "?");
} else {
sb.append(url + "&");
}
if(params ! =null && !params.isEmpty()) {
for (String key : params.keySet()) {
sb.append(key).append("=").append(params.get(key)).append("&");
}
}
sb = sb.deleteCharAt(sb.length() - 1);
LogUtils.i("Network Request"."Request interface ==>>" + sb.toString());
returnsb.toString(); }}Copy the code
First of all, when we new the class, we get the unique okHttpClient for the request, and we get the mDelivery for the child thread to switch the main thread, so we can do the UI directly in the later callback method. The OkGetBuilder loop adds heads to the head as appendHeaders (map) and concatenates parameters as appendParams (map).
So the next part is the request
- When we call our custom method enqueue, we call onBefore
- In the original callback failure onFailure, of course, we call back our onAfter and onError.
- In the original callback onResponse, it’s going to be a little bit tricky here, even if it returns code=200; There are two other cases, one is normal as you pass in the generic resolution, and one is for example: click follow, the network request is also successful, but the interface problem returned attention failed. Our company uses a status of 0 for failure, so be careful here
- OkGetBuilder packaged, put it into EasyOk as follows:
public static OkGetBuilder get(a) {
return new OkGetBuilder();
}
Copy the code
In fact, post and upload file are the same idea, the difference is that post has a variety of RequestBody, upload file is also a kind of post, here specifically can see hongyang and OkGO package)
// These are all methods
//paramsBuilder is a class I use to pass parameters. There are parameters to use consistent point down it is good...
EasyOk.get().url("http://gank.io/api/xiandu/category/wow")
.tag("cancleTag")
// The header has already been null
//.headers(paramsBuilder.getHeads())
// We have done null processing internally
//.params(paramsBuilder.getParams())
.build().enqueue(new ResultMyCall<T>() {
@Override
public void onBefore(a) {
super.onBefore();
}
@Override
public void onAfter(a) {
super.onAfter();
}
@Override
public void onError(String errorMessage) {
super.onError(errorMessage);
}
@Override
public void onSuccess(Object response) {
super.onSuccess(response);
// If you have generics in your new ResultMyCall, you only need them here
//T bean = (T)response ;
// If there is no generics, the default type returned is string,
//Sring bean = (String)response;}});Copy the code
If onBefore and onAfter and onError encapsulate unified operations and don’t need to override the ones that don’t have special operations you can do this:
EasyOk.get().url("http://gank.io/api/xiandu/category/wow")
.tag("cancleTag")
.build().enqueue(new ResultMyCall<T>() {
@Override
public void onSuccess(Object response) {
super.onSuccess(response);
// If you have generics in your new ResultMyCall, you only need them here
//T bean = (T)response ;
// If there is no generics, the default type returned is string,
//Sring bean = (String)response;}});Copy the code
I did not mention other functions such as cache and reconnection above, because it is too complicated and unclear to add them. I will take them out separately later. Through the understanding of this article, you will know how to package them and where to add them.
4, the next is to download files and resumable download files
With that said, let’s go straight to the OkDownloadBuilder (I’ve removed the extra parts to make it easier to understand) :
public class OkDownloadBuilder {
// The length of the breakpoint continuation
private long currentLength;
private String url;
private String tag;
// File path (excluding file name)
private String path;
/ / file name
private String fileName;
// Whether to enable breakpoint continuation
private boolean resume;
private OkHttpClient okHttpClient;
private Handler mDelivery;
private Request.Builder mBuilder;
public OkDownloadBuilder(a) {
this.okHttpClient = EasyOk.getInstance().getOkHttpClient();
this.mDelivery = EasyOk.getInstance().getmDelivery();
}
public OkDownloadBuilder build(a) {
mBuilder = new Request.Builder();
mBuilder.url(url);
if(! TextUtils.isEmpty(tag)) { mBuilder.tag(tag); }// If you upload a breakpoint, it will always be cached. So force network download
mBuilder.cacheControl(CacheControl.FORCE_NETWORK);
return this;
}
public void enqueue(final OnDownloadListener listener) {
if (resume) {
File exFile = new File(path, fileName);
if (exFile.exists()) {
currentLength = exFile.length();
mBuilder.header("RANGE"."bytes=" + currentLength + "-");
}
}
Request okHttpRequest = mBuilder.build();
okHttpClient.newCall(okHttpRequest).enqueue(new Callback() {
@Override
public void onFailure(Call call, final IOException e) {
// Download failed to listen for callback
mDelivery.post(new Runnable() {
@Override
public void run(a) { listener.onDownloadFailed(e); }}); }@Override
public void onResponse(Call call, Response response) throws IOException {
InputStream is = null;
byte[] buf = new byte[1024];
int len = 0;
FileOutputStream fos = null;
// The directory where the downloaded files are stored
File dir = new File(path);
if(! dir.exists()) { dir.mkdirs(); }final File file = new File(dir, fileName);
try {
is = response.body().byteStream();
/ / total length
final long total;
// If the current length is equal to the length to be downloaded, then the file is downloaded
// If this file is downloaded by default, to determine whether it can be resumed, it is best to determine whether the version number is approved when the network is started
if (currentLength == response.body().contentLength()) {
mDelivery.post(new Runnable() {
@Override
public void run(a) { listener.onDownloadSuccess(file); }});return;
}
if (resume) {
total = response.body().contentLength() + currentLength;
} else {
total = response.body().contentLength();
}
mDelivery.post(new Runnable() {
@Override
public void run(a) { listener.onDownLoadTotal(total); }});if (resume) {
// This method starts the file concatenation
fos = new FileOutputStream(file, true);
} else {
// Start from scratch
fos = new FileOutputStream(file);
}
long sum;
if (resume) {
sum = currentLength;
} else {
sum = 0;
}
while((len = is.read(buf)) ! = -1) {
fos.write(buf, 0, len);
sum += len;
final int progress = (int) (sum * 1.0 f / total * 100);
// Update progress bar in download
mDelivery.post(new Runnable() {
@Override
public void run(a) { listener.onDownloading(progress); }}); } fos.flush();// Download complete
mDelivery.post(new Runnable() {
@Override
public void run(a) { listener.onDownloadSuccess(file); }}); }catch (final Exception e) {
mDelivery.post(new Runnable() {
@Override
public void run(a) { listener.onDownloadFailed(e); }}); }finally {
try {
if(is ! =null) {
is.close();
}
if(fos ! =null) { fos.close(); }}catch(IOException e) { } } } }); }}Copy the code
If you don’t look at the breakpoint continuation, in onResponse it’s just the input stream and the file stream, writing the stream into the file; What are the key points of continuation of breakpoints? To know that resumable is to continue downloading the file that was not downloaded last time (of course, here to ensure that the download is the same file, such as the download update to ensure that it is the same version number, here to determine whether it is the same version when obtaining the version update content). There are two key points:
- When starting the file download, add the current file length to the header
mBuilder.header("RANGE"."bytes=" + currentLength + "-");
Copy the code
- At the same time, when the file stream is telling the stream, we are not overwriting, we are concatenating
fos = new FileOutputStream(file, true);// True
Copy the code
5. Online cache when there is a network
The scenario is as follows: if the home page advertisement, get requests down the data, and this may only change the data once a week, at this time if there is no this function, every time into the home page will request the network, if there is this function, then if the cache content in the validity period will skip the network request, directly take the cache. This saves traffic and reduces server pressure. Of course you need to implement caching, you need to set the cache file, set the cache file when initializing okHttpClient
// Set the cache file path and file size
okHttpClent.cache(new Cache(new File(Environment.getExternalStorageDirectory() +"/okhttp_cache/"), 50 * 1024 * 1024))
Copy the code
Read a lot of data, a lot of blogs are very bad. When you read this article, you will know that you can do it with interceptors anywhere else, but be aware of what online caching is and what offline caching is. These are two concepts. Online caching requires a network interceptor
okHttpClient.addNetworkInterceptor(NetCacheInterceptor.getInstance())
Copy the code
For the interceptor here, I use singletons, so that it is easy to change the parameters to achieve whether or not to use caching; The NetCacheIntertor code looks like this:
** * Created by Leo * on 2019/7/25. ** * Created by Leo * on 2019/7/25. ** * Created by Leo * on 2019/7/25. For example, after you set the cache of my solution list interface, you delete a solution, refresh. * He fetched the cache, and the deleted data came out. * (Note that if an interface is set to cache for 30 seconds, the next request to this interface will be fetched within 30 seconds, even if you set 0.) There is already a 30-second expiration date in the cache file identifier
public class NetCacheInterceptor implements Interceptor {
private static NetCacheInterceptor cacheInterceptor;
//30 The cache expiration time when the cache is online. If you want to stop the cache, the direct time is set to 0
private int onlineCacheTime;
public static NetCacheInterceptor getInstance(a) {
if (cacheInterceptor == null) {
cacheInterceptor = new NetCacheInterceptor();
}
return cacheInterceptor;
}
private NetCacheInterceptor(a) {}public void setOnlineTime(int time) {
this.onlineCacheTime = time;
}
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
Request.Builder builder1 = request.newBuilder();
// Here we log in, get the token in the head and store it, add the token to the head when the network requests, for identity identification
String token = (String) PreferenceUtil.get("USER_TOKEN"."");
if(! TextUtils.isEmpty(token)) { builder1.addHeader("Token", token)
.build();
}
request = builder1.build();
Response response = chain.proceed(request);
List<String> list = response.headers().values("Token");
if (list.size() > 0) {
PreferenceUtil.put("USER_TOKEN", list.get(0));
}
// Set the cache
if(onlineCacheTime ! =0) {
// Set the cache if you have time
int temp = onlineCacheTime;
Response response1 = response.newBuilder()
.header("Cache-Control"."public, max-age=" + temp)
.removeHeader("Pragma")
.build();
onlineCacheTime = 0;
return response1;
} else {
// Do not cache if there is no time
Response response1 = response.newBuilder()
.header("Cache-Control"."no-cache")
.removeHeader("Pragma")
.build();
return response1;
}
// return response;}}Copy the code
Max-age is the duration of the online cache. If I set max-age = 3600, it means that the network cache will be directly fetched for 1 hour after the first request, skipping the network request. Response.headers () can obtain all the head information returned by the server, including set-cookie information. And okhttp provides.cookjar(). You can customize it through things like cookie persistence
So how do you verify these Settings? Go back to your original network request onResponse callback; By:
if(response.networkResponse()! =null){
LogUtils.i("Source of content"."Request from network");
}
if(response.cacheResponse()! =null){
LogUtils.i("Source of content"."From cache");
}
Copy the code
Of course this is just validation, there’s no need to change the onResponse, okHTTP already does all the work internally.
6. Offline cache without network
For example, Tencent News will still display the data loaded before you enter the APP when you turn on the airplane mode of your phone. This is the offline cache, offline cache and online cache the biggest difference, online cache even if there is a conditional request network can also skip the network cache. Also added by interceptor, addNetworkInterceptor is not used when there is no network. However, with addInterceptor, there will be no network, and addInterceptor will run before addNetworkInterceptor
okHttpClient.addInterceptor(OfflineCacheInterceptor.getInstance());
Copy the code
Singletons are also used, as follows:
/** * Created by Leo * on 2019/7/25. * This will run before the network interceptor * when there is no network connection, the cache will be taken * Important: generally okHTTP only caches data that does not change very much for get. * This parameter is different from the previous one, and takes effect immediately. For example, if you set the offline cache validity period of 1 hour for an interface, set it to 0 immediately. After the next access, it will be invalid */
public class OfflineCacheInterceptor implements Interceptor {
private static OfflineCacheInterceptor offlineCacheInterceptor;
// The cache expiration time while offline
private int offlineCacheTime;
private OfflineCacheInterceptor(a) {}public static OfflineCacheInterceptor getInstance(a) {
if (offlineCacheInterceptor == null) {
offlineCacheInterceptor = new OfflineCacheInterceptor();
}
return offlineCacheInterceptor;
}
public void setOfflineCacheTime(int time) {
this.offlineCacheTime = time;
}
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
if(! NetWorkUtils.isNetworkConnected(MyApplication.getContext())) {if(offlineCacheTime ! =0) {
int temp = offlineCacheTime;
request = request.newBuilder()
// .cacheControl(new CacheControl
// .Builder()
// .maxStale(60,TimeUnit.SECONDS)
// .onlyIfCached()
// .build()
//) The result is the same in both ways
.header("Cache-Control"."public, only-if-cached, max-stale=" + temp)
.build();
offlineCacheTime = 0;
} else {
request = request.newBuilder()
.header("Cache-Control"."no-cache") .build(); }}returnchain.proceed(request); }}Copy the code
NetWorkUtils is a utility class that determines whether there is a network. Stale =3600; stale=3600; stale=3600; If you’re in the middle of nowhere and your offline cache expires after 1 hour and 01 minutes, you enter the app and the page goes blank. Of course, you can also set the offline cache to always be valid, integer.max_value.
7, multiple requests for the same URL, in the network request is not over, whether to request only once
If the call.request.tag() in the pool or the tag in the waiting queue contains the network tag you are currently requesting, loading is not required. But then you have to add a tag every time. So I just added one to EasyOk
// Prevent network repeat request tagList;
private ArrayList<String> onesTag;
Copy the code
If I don’t have a tag, I can use the request URL here. I remove this element from the collection at the end and success of the network request. Of course you would say, when we cancel the network request, actually canceling the network request will go onFailure(Call Call,IOException e). This time the error type is SocketException. So when you cancel the network, onFailure is removed, and the code looks like this:
public void enqueue(final ResultCall resultMyCall) {
if(resultMyCall ! =null) {
// This is an example of a child thread switching to the main thread. Whenever the network is requested, we set onBefore
mDelivery.post(new Runnable() {
@Override
public void run(a) { resultMyCall.onBefore(); }}); }// Whether the onlyOneNet parameter is onlyOneNet. This parameter is disabled by default. After return, the network will not be enabled
if (onlyOneNet) {
if(! TextUtils.isEmpty(tag)) {if (EasyOk.getInstance().getOnesTag().contains(tag)) {
return;
}
EasyOk.getInstance().getOnesTag().add(tag);
} else {
if (EasyOk.getInstance().getOnesTag().contains(url)) {
return; } EasyOk.getInstance().getOnesTag().add(url); }}... }Copy the code
8. Automatic reconnection of failed requests and reconnection times
Here also refer to a large number of blogs, say there are set okhttp reconnection, set okHttpClient. RetryOnConnectionFailure (true) both reconnection, but I have found a large number of tests, however, and soft (understand friend, please inform). This code is under Builder. Each network request will create a new Builder. For example, OkGetBuilder:
okHttpClient.newCall(okHttpRequest).enqueue(new Callback() {
@Override
public void onFailure(Call call, final IOException e) {
// Cancel the network request
if (e instanceof SocketException) {
} else {
//tryAgainCount is reconnection. This parameter is not set. The default value is 0
//currentAgainCount is a property in OkGetBuilder that will be new every time the network is started
// currentAgainCount starts with 0
if (currentAgainCount < tryAgainCount && tryAgainCount > 0) {
currentAgainCount++;
// Call. Request will retrieve your original request
// This is your current new Callback, so the network Callback will go there
okHttpClient.newCall(call.request()).enqueue(this);
return; }}}Copy the code
At this point, the general introduction is over. The above introduction exactly how to use.Check out my Github introduction to see how this works
9. How to use MVC based encapsulation? Let’s talk about the usage of encapsulation in this project
I’ve defined NetWorListener(get, POST, upload file network callback), OnDownloadListener(file download network callback), PermissionListener(permission request result callback), if you want to request the network, just implement this interface, Then call the network request via ModelSuperImpl and get the network request callback on any page where you implement this interface. The request only needs to look like this:
// To call outside, just pass in the parameters, put the URL and the parser class in the ModelSuperImpl
ModelSuperImpl.netWork().gankGet(ParamsBuilder.build().params(PARAMS.gank("android")) .command(GANK_COMMAND), this);
Copy the code
The ModelSuperImpl looks like this
public class ModelSuperImpl extends ModelBase {
private static final ModelSuperImpl ourInstance = new ModelSuperImpl();
public static ModelSuperImpl netWork(a) {
return ourInstance;
}
public static ModelPermissionImpl permission(a) {
return new ModelPermissionImpl();
}
private ModelSuperImpl(a) {}public void gankGet(ParamsBuilder paramsBuilder, NetWorkListener netWorkListener) {
paramsBuilder.url(SystemConst.GANK_GET)
.type(newTypeToken<ResponModel<User>>() { }.getType()) ; sendOkHttpGet(paramsBuilder, netWorkListener); }}Copy the code
Getting a callback in an Activity/Fragment or wherever the interface (NetWorListener) is implemented is like this. Command is used to distinguish between multiple network requests that a page may request:
@Override
public void onNetCallBack(int command, Object object) {
switch (command) {
case GANK_COMMAND:
Response<User> userModel = (Response<User>)object;
break; }}Copy the code
The ParamsBuilder has multiple parameters as follows:
public class ParamsBuilder {
// Request the network URL (required)
private String url;
// Network callback int value (mandatory)
private int command;
// Type returned by the network (optional) If this parameter is not specified, string is returned
private Type type;
// Header information required for network requests (optional, not null)
private HashMap<String, String> heads;
// Parameter required for network request (optional, not null)
private HashMap<String, String> params;
// Text information required for network loading (optional, leave null)
private String loadMessage;
// Whether to display network loading(default: display loading)
private boolean isShowDialog = true;
// Network request tag, can cancel the network request according to the tag (optional, do not fill: default host class name, automatically cancel after exit)
private String tag;
// Overwrite the network problem or timeout problem
// If it is true, you can perform additional operations on that part of the callback
//(optional, do not fill: can not rewrite and only play hint)
private boolean overrideError;
//json Parameter to upload
private String json;
// Network interface code=200, but failed, this user has been concerned
// Need to override with true, override can write logic including pop prompt
// There is no need to rewrite the popup prompt
private boolean successErrorOverrid;
// Offline cache time in seconds
private int cacheOfflineTime;
// Maximum cache time for network requests
private int cacheOnlineTime;
// Click the button multiple times to make only one network request
// Scenario: The network is still loading, and a request is clicked again. No new request is sent, but Loading is displayed
private boolean onlyOneNet = true;
// Network failed, retry times
private int tryAgainCount;
// The callback is not an activity or fragment, which is used to pass the context
// Used for showDialog, required when requesting a web page that is not an Activity or Fragment
private Context context;
/** ** ** */
private String path;
private String fileName;
// Whether to enable breakpoint continuation. Ensure that the same file is downloaded when you enable breakpoint continuation
// By default, breakpoint continuation is disabled, unless the file to be downloaded belongs to the same file as the undownloaded file
// If not, the previous file will be cleared.
private boolean resume;
}
Copy the code
See above, the specific design parameters are written. Throw new NullPonintException(” NullPonintException “); throw new NullPonintException(” NullPonintException “); Throw the exception, and if it doesn’t pass, it crashes.
Also in the download file only need to implement OnDownliadListener, can be. The call just needs to look like this:
ModelSuperImpl.netWork().downApk(ParamsBuilder.build().path(path)
.fileName(fileName).tag("downApk"), this);
Copy the code
ModelPerissionImpl encapsulates all the request permission logic in ModelPerissionImpl. If you implement PerimissionListener, you can get the network request callback.
/RESUME_COMMAND A page may request multiple permissions to distinguish between,thisBehind is PerimissionListener implementation class, access parameters are variable if there are multiple permissions can continue to be a comma to add ModelSuperImpl. Permission () requestPermission (RESUME_COMMAND,this, Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE);
Copy the code
Callbacks need only look like this:
// The callback only needs this; The command is to distinguish between multiple permission requests on a page. If there is only one request on a page, commad can not be transmitted
@Override
public void permissionSuccess(int command) {
switch (command) {
case NORMAL_COMMAND:
ModelSuperImpl.netWork().downApk(ParamsBuilder.build().path(path)
.fileName(fileName).tag("downApk"), this);
break; }}Copy the code
I encapsulate the specific networking operations and parsing in the abstract class ModelBase, and the specific permission request logic in the abstract class ModelPerissionImpl. ModelSuperImpl is only responsible for calling the network request method and permission request method, so the code is perfectly separated. The View layer receives the user’s operation or click request and responds to the controller. The Controller notifies the Model to process the logical business and tells the Controller to update the Ui through the interface after receiving the result. If you have multiple pages requesting the same URL, all you need to do is call the method via ModelSuperImpl.
OverrideError and successErrorOverrid in ParamBuilder are not very well understood by many people here. In fact, the default method here is not rewrite method. For example, if the network request fails, I will pop up toast in ModelBase by default. If the code is 200, but the interface may go wrong, for example, I will not rewrite the pop-up toast by default. OverrideError (true) and. SuccessErrorOverrid (true) codes are as follows:
@Override
public void onNetCallBack(int command, Object object) {
switch (command) {
case GANK_COMMAND:
/** * overrideError(true) * overrideError(true) * overrideError(true) * overrideError(true) *
// if (obj instanceof NetFail) {
// NetFail netFailBean = (NetFail) obj;
// Process logic
// return;
/ /}
SuccessErrorOverrid (true) * if code=200, result is not 1; If you only need Toast error messages, then you don't need to write them. You don't need to rewrite the following. Encapsulation has a default pop prompt * */
// if (obj instanceof ErrorBean) {
// ErrorBean errorBean = (ErrorBean) obj;
// Process logic
// return;
/ /}
ResponModel<User> detailModel = (ResponModel<User>) obj;
/ / update the UI
break; }}Copy the code
Conclusion: ENCAPSULATION of my personal ideas. If there is no welcome to correct, and if there is a better idea welcome to leave a message. Technical elementary school students, like to learn. See here, if it helps you, help the blogger star
Making portal
Remember the EventBus delete all in the builder, I didn’t think too much before, so at the time of presentation with EventBus delivered the p.p here, of course, use a generic class ResponModel, ErrorBean, NetFail are defined according to my project for me, Remember to modify the data structure as required if it is different