The structure of this paper: 1. What is involved in network layer encapsulation; 2. How to encapsulate network layer; 3. How to use encapsulated network layer.
I. What is involved in network layer encapsulation
Mass Program: OkHttp+Rxjava+Rtrofit
Introduction: Retrofit and okHttp come from the same family and are also Square's open source library. Retrofit simplifies the process of network requests, encapsulates them based on OkHtttp, and decouples them more thoroughly: for example, configuring request parameters through annotations, generating Call Adapter and Converter through factories, you can use different Call Adapters to compare with each other. For example, RxJava, Java 8, Guava. You can use different Converter tools, such as json, protobuff, xml, moshi and so on.
2. How to encapsulate the network layer
1. import
compile 'io.reactivex:rxandroid:1.2.1'
compile 'io.reactivex:rxjava:1.1.6'
compile 'com.squareup.retrofit2:retrofit:2.1.0'
compile 'com.squareup.retrofit2:converter-gson:2.1.0'
compile 'com.squareup.retrofit2:adapter-rxjava:2.1.0'
compile 'com.github.franmontiel:PersistentCookieJar:v1.0.0'
2.RetrofitUtil implements Retrofit singletons:
public class RetrofitUtil {
private static final String TAG = "retrofit";
private static final String BASE_URL = "https://api.douban.com";
private static final int DEFAULT_TIMEOUT = 5;
private static Retrofit retrofit;
private RetrofitUtil(){
}
public static Retrofit getInstance(){
if(retrofit==null) {
ClearableCookieJar cookieJar =
new PersistentCookieJar(new SetCookieCache(), new SharedPrefsCookiePersistor(App.getInstance()));
OkHttpClient.Builder httpClientBuilder = new OkHttpClient.Builder();
httpClientBuilder.connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS);
File cacheFile = new File(App.getInstance().getCacheDir(), "superman");
Cache cache = new Cache(cacheFile, 1024 * 1024 * 100);
httpClientBuilder.cache(cache);
httpClientBuilder.cookieJar(cookieJar);
httpClientBuilder.addInterceptor(LoggingInterceptor);
httpClientBuilder.addInterceptor(REWRITE_CACHE_CONTROL_INTERCEPTOR);
httpClientBuilder.addNetworkInterceptor(REWRITE_CACHE_CONTROL_INTERCEPTOR);
return new Retrofit.Builder()
.client(httpClientBuilder.build())
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.baseUrl(BASE_URL)
.build();
}else{
return retrofit;
}
}
private static final Interceptor LoggingInterceptor = new Interceptor() {
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
long t1 = System.nanoTime();
Logger.t(TAG).i(String.format("Sending request %s on %s%n%s", request.url(), chain.connection(), request.headers()));
Response response = chain.proceed(request);
long t2 = System.nanoTime();
Logger.t(TAG).i(String.format("Received response for %s in %.1fms%n%s", response.request().url(), (t2 - t1) / 1e6d, response.headers()));
return response;
}
};
private static final Interceptor REWRITE_CACHE_CONTROL_INTERCEPTOR = new Interceptor() {
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
if(!NetUtil.isConnected(App.getInstance())){
request = request.newBuilder()
.cacheControl(CacheControl.FORCE_CACHE)
.build();
Logger.t(TAG).w("no network");
}
Response originalResponse = chain.proceed(request);
if(NetUtil.isConnected(App.getInstance())){
String cacheControl = request.cacheControl().toString();
return originalResponse.newBuilder()
.header("Cache-Control", cacheControl)
.removeHeader("Pragma")
.build();
}else{
return originalResponse.newBuilder()
.header("Cache-Control", "public, only-if-cached, max-stale=2419200")
.removeHeader("Pragma")
.build();
}
}
};
}
3. Key configuration before invocation:
An app class captures the context of the app and its key configuration
public class App extends Application {
private static App app;
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
}
@Override
public void onCreate() {
super.onCreate();
MultiDex.install(this);
app = this;
}
public static App getInstance() {
return app;
}
}
//The key here is to register the context of the app.
<application
android:name=".App" //Here it is.
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:allowBackup="true"
android:theme="@style/AppTheme">
<activity android:name=".activity.MainActivity"
android:screenOrientation="portrait">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
3. How to use the encapsulated network layer:
1. Enter the host address and write the interface:
public interface MovieRequest {
@GET("/v2/movie/top250")
Observable<AbilityBean> getMovies(@Query("start") int start, @Query("count") int count);
}
2. Use postman and other tools to view that data first, json. Then use gsonformat to generate that bean. Use of GsonFormat
package com.fuzhucheng.rxjavartrofitrecyclerview.bean;
import java.util.List;
/**
* Created by The column is on 2016/12/14.
*/
public class AbilityBean {
public int image;
public String place;
public int like;
public int price;
public int count;
public int start;
public int total;
public String title;
public List<SubjectsEntity> subjects;
public AbilityBean(int image, String place, int like, int price) {
this.image = image;
this.place = place;
this.like = like;
this.price = price;
}
public static class SubjectsEntity {
public RatingEntity rating;
public String title;
public int collect_count;
public String original_title;
public String subtype;
public String year;
public RatingEntity.ImagesEntity images;
public String alt;
public String id;
public List<String> genres;
public List<RatingEntity.CastsEntity> casts;
public List<RatingEntity.DirectorsEntity> directors;
public static class RatingEntity {
public int max;
public double average;
public String stars;
public int min;
public static class ImagesEntity {
public String small;
public String large;
public String medium;
}
public static class CastsEntity {
public String alt;
public AvatarsEntity avatars;
public String name;
public String id;
public static class AvatarsEntity {
public String small;
public String large;
public String medium;
}
}
public static class DirectorsEntity {
public String alt;
public AvatarsEntityX avatars;
public String name;
public String id;
public static class AvatarsEntityX {
public String small;
public String large;
public String medium;
}
}
}
}
}
3. But we often see such a large json, some data we will not use, so we can write an additional bean to correspond to our client.
If our client only needs so much information for a list, it's enough to write such a small bean.
public class AbilityItem {
private String image;
private String title;
private String content;
public String getImage() {
return image;
}
public void setImage(String image) {
this.image = image;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public AbilityItem(String image, String title, String content) {
this.image = image;
this.title = title;
this.content = content;
}
}
4. use
private List<AbilityItem> datas = new ArrayList<>();
private CommonAdapter<AbilityItem> adapter;
private MovieRequest movieRequest;
//This method can be loaded into loadData.
private void getMovie() {
movieRequest = RetrofitUtil.getInstance().create(MovieRequest.class)
//Here's the rxjava format
movieRequest.getMovies(0, 10)
.subscribeOn(Schedulers.io())
.unsubscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<AbilityBean>() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
ToastUtils.showToast(getActivity(), getString(R.string.request_error))
}
@Override
public void onNext(AbilityBean abilityBean) {
swipeRefreshLayout.setRefreshing(false)
datas.clear()
ToastUtils.showToast(getActivity(), getString(R.string.request_success))
for (int i = 0
datas.add(new AbilityItem(abilityBean.subjects.get(i).images.medium, abilityBean.subjects.get(i).title, abilityBean.subjects.get(i).year))
bitmapList.add(abilityBean.subjects.get(i).images.large)
}
loadConvenientBanner()
//Here's how to set up the adapter, which corresponds to view and data
adapter = new CommonAdapter<AbilityItem>(getActivity(), R.layout.item_fragment_ability_recyclerview, datas) {
@Override
protected void convert(ViewHolder holder, final AbilityItem abilityItem, final int position) {
//Use this encapsulated adapter pattern to set up a series of view s corresponding to the data
holder.setText(R.id.place, abilityItem.getTitle())
holder.setText(R.id.price, abilityItem.getContent())
ImageView circleImageView = holder.getView(R.id.image)
Glide.with(getActivity()).load(abilityItem.getImage()).into(circleImageView)
}
}
recyclerView.setAdapter(adapter)
}
})
}
One of the debugging principles is Rtrofit's encapsulation: 1. The request failed to execute onError; 2. The request succeeded, but the onNext parsing process was wrong, and the onError was executed as well.
Well, the encapsulation of the network layer of the Android-multi-list project (Rxjava+Rtrofit+Recyclerview+Glide+Adapter package) is over. This blog is the second in this series, so it's about network layer encapsulation. In addition, this series also has some optimization I have done in the process of outsourcing projects, as well as some publishing and signing skills, I will finish as soon as possible to share experience with you. Welcome to point out the mistakes below and learn together!! Your praise is my best support!!