Android-Multi-List Project (Rxjava+Rtrofit+Recyclerview+Glide+Adapter Packaging)

Keywords: Retrofit Android network github

Next, the second article in this series is the most important one, network layer encapsulation. The first one is: Android - Project Architecture of Multiple Lists (Rxjava+Rtrofit+Recyclerview+Glide+Adapter Encapsulation)

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.

Official website http://square.github.io/retrofit/
github https://github.com/square/retrofit
Rxjava: http://gank.io/post/560e15be2dca930e00da1083

2. How to encapsulate the network layer

1. import

// https://github.com/ReactiveX/RxAndroid rx asynchrony
    compile 'io.reactivex:rxandroid:1.2.1'
    compile 'io.reactivex:rxjava:1.1.6'
    // https://github.com/square/retrofit network request
    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'
    //Cookie cache of https://github.com/franmontiel/Persistent CookieJar Okhttp
    compile 'com.github.franmontiel:PersistentCookieJar:v1.0.0'





2.RetrofitUtil implements Retrofit singletons:

public class RetrofitUtil {
    private static final String TAG = "retrofit";
    //TODO: Modify host address
    private static final String BASE_URL = "https://api.douban.com";
    private static final int DEFAULT_TIMEOUT = 5;
    private static Retrofit retrofit;

    //Instantiating private ownership
    private RetrofitUtil(){

    }
//Single Retrofit 
    public static Retrofit getInstance(){
        if(retrofit==null) {
        //One of the key points here is to pass in the context of App.
            ClearableCookieJar cookieJar =
                    new PersistentCookieJar(new SetCookieCache(), new SharedPrefsCookiePersistor(App.getInstance()));
            OkHttpClient.Builder httpClientBuilder = new OkHttpClient.Builder();                //okhttp creation, write caching, and addInterceptor
            httpClientBuilder.connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS);
            File cacheFile = new File(App.getInstance().getCacheDir(), "superman");
            Cache cache = new Cache(cacheFile, 1024 * 1024 * 100); //100Mb. This is where the cache settings begin. Implementing one-click caching and configuration of request headers, etc.
            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()                          //The creation of retrofit.
                    .client(httpClientBuilder.build())          //Incoming okhttp
                    .addConverterFactory(GsonConverterFactory.create())             //Afferent gson parsing means
                    .addCallAdapterFactory(RxJavaCallAdapterFactory.create())           //Afferent asynchronous means
                    .baseUrl(BASE_URL)              //Inbound server address
                    .build();
        }else{
            return retrofit;
        }
    }
    //okHttp Interceptor
    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;
        }
    };
    //okHttp Interceptor
    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())){//Determine whether there is a network to operate again
                request = request.newBuilder()
                        .cacheControl(CacheControl.FORCE_CACHE)
                        .build();
                Logger.t(TAG).w("no network");
            }
            //Request Header Cache Control
            Response originalResponse = chain.proceed(request);
            if(NetUtil.isConnected(App.getInstance())){
                //When you have a network, you can read the configuration in @Headers on the interface, where you can set up unified settings.
                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 {
//    // Request parameters, note that the interface should correspond exactly to the bean, and operate according to the second point.
    @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<>();//Save bean s for lists
    private CommonAdapter<AbilityItem> adapter;//List adapters
    private MovieRequest movieRequest;//Request interface




//This method can be loaded into loadData.
 private void getMovie() {
        movieRequest = RetrofitUtil.getInstance().create(MovieRequest.class);//Using our tool class, reflect the request body
        //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);//This is a drop-down refresh.
                        datas.clear();//Avoid duplication and clarity of data
                        ToastUtils.showToast(getActivity(), getString(R.string.request_success));
                        for (int i = 0; i < abilityBean.subjects.size(); i++) {
                            datas.add(new AbilityItem(abilityBean.subjects.get(i).images.medium, abilityBean.subjects.get(i).title, abilityBean.subjects.get(i).year));//List data
                            bitmapList.add(abilityBean.subjects.get(i).images.large);//Data from that rotation chart
                        }
                        loadConvenientBanner();//A method inherited from a parent class
                        //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.

Source download: Android-Multi-List Project Rxjava+Rtrofit+Recyclerview+Glide+Adapter Encapsulation

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!!

More content, accessible Jack Frost's blog

Posted by Dunoon on Fri, 21 Dec 2018 21:33:05 -0800