十六、RxJava篇(應用場景)

一、RxJava的核心思想

介紹RxJava的核心思想之前先來看一個例子

  • 例子
    在我們開發中,假如我們需要下載一張圖片,並且顯示出來,可以有多種方法,A可能使用new Thread+Handler來實現、B使用AsynTask來實現、C可能使用別的方式來實現。各自寫的代碼都不一樣,這樣就會造成下載圖片的一個功能,沒有統一的思維。
    RxJava的思想就是基於起點到終點的一個模式,在起點中輸入一個事件,終點輸出我們想要的結果。在起點和終點之間可能會有多個攔截加工對事件的處理,但是目的都是爲了在終點輸出我們想要的結果。起點和終點的線路不會斷。
    就好比生活中的例子,在工廠的車間的生產線中,總會有一個材料的輸入源頭,和成品的輸出結果。中間是一條流水線,在流水線中可能會有多道加工程序進行加工,當最後一道加工程序加工完之後,就會輸出一個成品。也就是我們終點的結果就是拿到離我們最近的加工程序的結果。
    下面我們通過RxJava來實現一個下載圖片的功能:
 String path = "http://39.108.14.94:9007/donghui_oa/IMG/20210103041020962378.jpg";
    private void test1() {
        //1、輸入一個事件path
        Observable.just(path)
                //2、對事件加工
                .map(new Function<String, Bitmap>() {
            @Override
            public Bitmap apply(@NonNull String s) throws Exception {
                URL url = new URL(path);
                HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();
                httpURLConnection.setConnectTimeout(16000);
                int code = httpURLConnection.getResponseCode();
                if (code == HttpURLConnection.HTTP_OK) {
                    InputStream inputStream = httpURLConnection.getInputStream();
                    Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
                    return bitmap;
                }
                return null;
            }
        })
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                //3、訂閱
                .subscribe(new Observer<Bitmap>() {
                    @Override
                    public void onSubscribe(@NonNull Disposable d) {
                        Log.e(TAG, "onSubscribe: " );
                    }
                   //4、輸出事件
                    @Override
                    public void onNext(@NonNull Bitmap bitmap) {
                        Log.e(TAG, "onNext: " );
                        image.setImageBitmap(bitmap);
                    }

                    @Override
                    public void onError(@NonNull Throwable e) {
                        Log.e(TAG, "e: "+e );
                    }

                    @Override
                    public void onComplete() {
                        Log.e(TAG, "onComplete: " );
                    }
                });
    }

起點我們輸入一個path事件,接着對事件進行加工即通過path去網絡下載圖片,然後將事件類型轉換成bitmap。我們的終點接受上一道加工程序的事件bitmap。最後拿到結果顯示我們的圖片。

二、RxJava配合Retrofit的使用

在RxJava配合Retrofit的使用中,Retrofit負責去請求網絡拿到數據,並且將拿到的數據封裝成一個起點。接着通過RxJava反射數據將起點事件傳遞到終點。

  • 例子
    (1)封裝一個下載圖片的Api
package com.example.andoiddemo;



import io.reactivex.Observable;
import okhttp3.ResponseBody;
import retrofit2.http.GET;
import retrofit2.http.Streaming;

public interface Api {
    @GET("IMG/20210103041020962378.jpg")
    @Streaming
    Observable<ResponseBody> downLoad();
    
}

(2)封裝Retrofit

package com.example.andoiddemo;

import android.util.Log;

import com.facebook.stetho.okhttp3.StethoInterceptor;

import java.util.concurrent.TimeUnit;

import okhttp3.OkHttpClient;
import okhttp3.logging.HttpLoggingInterceptor;
import retrofit2.Retrofit;
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
import retrofit2.converter.gson.GsonConverterFactory;

public class RetrofitUtil {
    final static String baseUrl = "http://39.108.14.94:9007/donghui_oa/";
     static OkHttpClient okHttpClient = new OkHttpClient.Builder()

            .addInterceptor(new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() {
                @Override
                public void log(String message) {   //訪問網絡請求,和服務端響應請求時。將數據攔截並輸出
                    Log.e("RetrofitUtil", "log: " + message);
                }
            }).setLevel(HttpLoggingInterceptor.Level.BODY))     //Log等級
            .connectTimeout(16 ,TimeUnit.SECONDS)       //超時時間
            .readTimeout(16, TimeUnit.SECONDS)
            .writeTimeout(16, TimeUnit.SECONDS)
            .addNetworkInterceptor(new StethoInterceptor())

            .build();
    static Retrofit retrofit = new Retrofit
            .Builder()
            .baseUrl(baseUrl)
            .client(okHttpClient)
            .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
            .addConverterFactory(GsonConverterFactory.create()).build();

    public static Retrofit getInstance() {
        return retrofit;
    }

}

(3)下載圖片

  //下載圖片
    private void test2() {
        //1、起點
        Observable<ResponseBody> bodyObservable = RetrofitUtil.getInstance().create(Api.class).downLoad();
        //2、起點發射事件
        bodyObservable.
                subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                //3、轉換事件類型
                .map(new Function<ResponseBody, Bitmap>() {
                    @Override
                    public Bitmap apply(@NonNull ResponseBody responseBody) throws Exception {
                        Bitmap bitmap = BitmapFactory.decodeStream(responseBody.byteStream());
                        return bitmap;
                    }
                })
                //4、訂閱
                .subscribe(new Observer<Bitmap>() {
                    @Override
                    public void onSubscribe(@NonNull Disposable d) {
                        Log.e(TAG, "onSubscribe: ");
                    }
                    //5、接受事件
                    @Override
                    public void onNext(@NonNull Bitmap bitmap) {
                        Log.e(TAG, "onNext: " + bitmap);
                        image.setImageBitmap(bitmap);
                    }

                    @Override
                    public void onError(@NonNull Throwable e) {
                        Log.e(TAG, "onError: " + e);
                    }

                    @Override
                    public void onComplete() {
                        Log.e(TAG, "onComplete: ");
                    }
                });
    }

在Retrofit中支持添加一個addCallAdapterFactory爲RxJava2CallAdapterFactory,即我們定義API的時候可以定義返回RxJava的數據類型 Observable<ResponseBody>。這個即是我們在RxJava中說的起點。
接着訂閱Observable<ResponseBody>起點發射數據,並轉換數據成Bitmap。最後終點接受我們的Bitmap,拿到最後結果。顯示圖片。

三、防抖

在RxJava中封裝了一系列的庫,其中包含了對View操作的rxbinding。我們演示一個開發中經常遇到的例子,就是一個View在某個時間段內快速點擊了很多次,我們只響應一次。

 private void test3() {
        RxView.clicks(tvDown).throttleFirst(1000, TimeUnit.MILLISECONDS).subscribe(new Observer<Object>() {
            @Override
            public void onSubscribe(@NonNull Disposable d) {

            }

            @Override
            public void onNext(@NonNull Object o) {
                Log.e(TAG, "onNext: 響應事件....." );
            }

            @Override
            public void onError(@NonNull Throwable e) {

            }

            @Override
            public void onComplete() {

            }
        });

    }

設置1S鍾內只響應一次。

四、網絡嵌套

在我們開發中經常會有這種需求,就是我們的業務是需要調用某個接口,然後拿到某個接口的數據再去調用某個接口,這樣是一個嵌套的網絡請求。當然還有可能嵌套很多層。就會給我們開發造成了比較大的麻煩。我們通過使用RxJava來實現一個網絡嵌套請求。
我們的業務是通過查詢一個分類列表接口,然後再通過分類裏面的ID 再查詢分類下的集合。

  • 定義API
   /**
     * 查詢分類數據
     * @return
     */
    @GET("project/tree/json")
    Observable<ProjectData> getProjectData();

    /**
     * 根據分類查詢列表數據
     * @param page
     * @param cid
     * @return
     */
    @GET("project/list/{page}/json?cid=294")
    Observable<ItemData> getItemData(@Path("page") String page, @Query("cid") Integer cid)
  • 封裝Retrofit
package com.example.andoiddemo;

import android.util.Log;

import com.facebook.stetho.okhttp3.StethoInterceptor;

import java.util.concurrent.TimeUnit;

import okhttp3.OkHttpClient;
import okhttp3.logging.HttpLoggingInterceptor;
import retrofit2.Retrofit;
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
import retrofit2.converter.gson.GsonConverterFactory;

public class RetrofitUtil {
    final static String baseUrl = "https://www.wanandroid.com/";
     static OkHttpClient okHttpClient = new OkHttpClient.Builder()

            .addInterceptor(new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() {
                @Override
                public void log(String message) {   //訪問網絡請求,和服務端響應請求時。將數據攔截並輸出
                    Log.e("RetrofitUtil", "log: " + message);
                }
            }).setLevel(HttpLoggingInterceptor.Level.BODY))     //Log等級
            .connectTimeout(16 ,TimeUnit.SECONDS)       //超時時間
            .readTimeout(16, TimeUnit.SECONDS)
            .writeTimeout(16, TimeUnit.SECONDS)
            .addNetworkInterceptor(new StethoInterceptor())

            .build();
    static Retrofit retrofit = new Retrofit
            .Builder()
            .baseUrl(baseUrl)
            .client(okHttpClient)
            .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
            .addConverterFactory(GsonConverterFactory.create()).build();

    public static Retrofit getInstance() {
        return retrofit;
    }

}

  • 封裝實體數據
package com.example.andoiddemo;

import java.util.List;

public class ProjectData {
    private List<Data> data;

    private int errorCode;

    private String errorMsg;

    public void setData(List<Data> data) {
        this.data = data;
    }

    public List<Data> getData() {
        return this.data;
    }

    public void setErrorCode(int errorCode) {
        this.errorCode = errorCode;
    }

    public int getErrorCode() {
        return this.errorCode;
    }

    public void setErrorMsg(String errorMsg) {
        this.errorMsg = errorMsg;
    }

    public String getErrorMsg() {
        return this.errorMsg;
    }

    static class Data {
        private int courseId;

        private int id;

        private String name;

        private int order;

        private int parentChapterId;

        private boolean userControlSetTop;

        private int visible;


        public void setCourseId(int courseId) {
            this.courseId = courseId;
        }

        public int getCourseId() {
            return this.courseId;
        }

        public void setId(int id) {
            this.id = id;
        }

        public int getId() {
            return this.id;
        }

        public void setName(String name) {
            this.name = name;
        }

        public String getName() {
            return this.name;
        }

        public void setOrder(int order) {
            this.order = order;
        }

        public int getOrder() {
            return this.order;
        }

        public void setParentChapterId(int parentChapterId) {
            this.parentChapterId = parentChapterId;
        }

        public int getParentChapterId() {
            return this.parentChapterId;
        }

        public void setUserControlSetTop(boolean userControlSetTop) {
            this.userControlSetTop = userControlSetTop;
        }

        public boolean getUserControlSetTop() {
            return this.userControlSetTop;
        }

        public void setVisible(int visible) {
            this.visible = visible;
        }

        public int getVisible() {
            return this.visible;
        }

    }


}
package com.example.andoiddemo;

import java.util.List;

public class ItemData {
    private Data data;

    private int errorCode;

    private String errorMsg;

    public void setData(Data data) {
        this.data = data;
    }

    public Data getData() {
        return this.data;
    }

    public void setErrorCode(int errorCode) {
        this.errorCode = errorCode;
    }

    public int getErrorCode() {
        return this.errorCode;
    }

    public void setErrorMsg(String errorMsg) {
        this.errorMsg = errorMsg;
    }

    public String getErrorMsg() {
        return this.errorMsg;
    }


    static class Datas {
        private String apkLink;

        private int audit;

        private String author;

        private boolean canEdit;

        private int chapterId;

        private String chapterName;

        private boolean collect;

        private int courseId;

        private String desc;

        private String descMd;

        private String envelopePic;

        private boolean fresh;

        private String host;

        private int id;

        private String link;

        private String niceDate;

        private String niceShareDate;

        private String origin;

        private String prefix;

        private String projectLink;

        private String publishTime;

        private String realSuperChapterId;

        private String selfVisible;

        private String shareDate;

        private String shareUser;

        private String superChapterId;

        private String superChapterName;



        private String title;

        private int type;

        private int userId;

        private int visible;

        private int zan;

        public String getApkLink() {
            return apkLink;
        }

        public void setApkLink(String apkLink) {
            this.apkLink = apkLink;
        }

        public int getAudit() {
            return audit;
        }

        public void setAudit(int audit) {
            this.audit = audit;
        }

        public String getAuthor() {
            return author;
        }

        public void setAuthor(String author) {
            this.author = author;
        }

        public boolean isCanEdit() {
            return canEdit;
        }

        public void setCanEdit(boolean canEdit) {
            this.canEdit = canEdit;
        }

        public int getChapterId() {
            return chapterId;
        }

        public void setChapterId(int chapterId) {
            this.chapterId = chapterId;
        }

        public String getChapterName() {
            return chapterName;
        }

        public void setChapterName(String chapterName) {
            this.chapterName = chapterName;
        }

        public boolean isCollect() {
            return collect;
        }

        public void setCollect(boolean collect) {
            this.collect = collect;
        }

        public int getCourseId() {
            return courseId;
        }

        public void setCourseId(int courseId) {
            this.courseId = courseId;
        }

        public String getDesc() {
            return desc;
        }

        public void setDesc(String desc) {
            this.desc = desc;
        }

        public String getDescMd() {
            return descMd;
        }

        public void setDescMd(String descMd) {
            this.descMd = descMd;
        }

        public String getEnvelopePic() {
            return envelopePic;
        }

        public void setEnvelopePic(String envelopePic) {
            this.envelopePic = envelopePic;
        }

        public boolean isFresh() {
            return fresh;
        }

        public void setFresh(boolean fresh) {
            this.fresh = fresh;
        }

        public String getHost() {
            return host;
        }

        public void setHost(String host) {
            this.host = host;
        }

        public int getId() {
            return id;
        }

        public void setId(int id) {
            this.id = id;
        }

        public String getLink() {
            return link;
        }

        public void setLink(String link) {
            this.link = link;
        }

        public String getNiceDate() {
            return niceDate;
        }

        public void setNiceDate(String niceDate) {
            this.niceDate = niceDate;
        }

        public String getNiceShareDate() {
            return niceShareDate;
        }

        public void setNiceShareDate(String niceShareDate) {
            this.niceShareDate = niceShareDate;
        }

        public String getOrigin() {
            return origin;
        }

        public void setOrigin(String origin) {
            this.origin = origin;
        }

        public String getPrefix() {
            return prefix;
        }

        public void setPrefix(String prefix) {
            this.prefix = prefix;
        }

        public String getProjectLink() {
            return projectLink;
        }

        public void setProjectLink(String projectLink) {
            this.projectLink = projectLink;
        }

        public String getPublishTime() {
            return publishTime;
        }

        public void setPublishTime(String publishTime) {
            this.publishTime = publishTime;
        }

        public String getRealSuperChapterId() {
            return realSuperChapterId;
        }

        public void setRealSuperChapterId(String realSuperChapterId) {
            this.realSuperChapterId = realSuperChapterId;
        }

        public String getSelfVisible() {
            return selfVisible;
        }

        public void setSelfVisible(String selfVisible) {
            this.selfVisible = selfVisible;
        }

        public String getShareDate() {
            return shareDate;
        }

        public void setShareDate(String shareDate) {
            this.shareDate = shareDate;
        }

        public String getShareUser() {
            return shareUser;
        }

        public void setShareUser(String shareUser) {
            this.shareUser = shareUser;
        }

        public String getSuperChapterId() {
            return superChapterId;
        }

        public void setSuperChapterId(String superChapterId) {
            this.superChapterId = superChapterId;
        }

        public String getSuperChapterName() {
            return superChapterName;
        }

        public void setSuperChapterName(String superChapterName) {
            this.superChapterName = superChapterName;
        }

        public String getTitle() {
            return title;
        }

        public void setTitle(String title) {
            this.title = title;
        }

        public int getType() {
            return type;
        }

        public void setType(int type) {
            this.type = type;
        }

        public int getUserId() {
            return userId;
        }

        public void setUserId(int userId) {
            this.userId = userId;
        }

        public int getVisible() {
            return visible;
        }

        public void setVisible(int visible) {
            this.visible = visible;
        }

        public int getZan() {
            return zan;
        }

        public void setZan(int zan) {
            this.zan = zan;
        }
    }

    static class Data {
        private int curPage;

        private List<Datas> datas;

        private int offset;

        private boolean over;

        private int pageCount;

        private int size;

        private int total;

        public void setCurPage(int curPage) {
            this.curPage = curPage;
        }

        public int getCurPage() {
            return this.curPage;
        }

        public void setDatas(List<Datas> datas) {
            this.datas = datas;
        }

        public List<Datas> getDatas() {
            return this.datas;
        }

        public void setOffset(int offset) {
            this.offset = offset;
        }

        public int getOffset() {
            return this.offset;
        }

        public void setOver(boolean over) {
            this.over = over;
        }

        public boolean getOver() {
            return this.over;
        }

        public void setPageCount(int pageCount) {
            this.pageCount = pageCount;
        }

        public int getPageCount() {
            return this.pageCount;
        }

        public void setSize(int size) {
            this.size = size;
        }

        public int getSize() {
            return this.size;
        }

        public void setTotal(int total) {
            this.total = total;
        }

        public int getTotal() {
            return this.total;
        }

    }

}

使用RxJava實現網絡嵌套請求

 /**
     * 查詢分類的列表,
     * 再根據分類id查詢分類下的集合
     */
    private void test4() {
        RetrofitUtil.getInstance().create(Api.class).getProjectData()
                .flatMap(new Function<ProjectData, ObservableSource<ProjectData.Data>>() {
                    @Override
                    public ObservableSource<ProjectData.Data> apply(@NonNull ProjectData projectData) throws Exception {
                        return Observable.fromIterable(projectData.getData());
                    }
                })
                .flatMap(new Function<ProjectData.Data, ObservableSource<ItemData>>() {
                    @Override
                    public ObservableSource<ItemData> apply(@NonNull ProjectData.Data data) throws Exception {
                        return RetrofitUtil.getInstance().create(Api.class).getItemData("1", data.getId());
                    }

                })
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Observer<ItemData>() {
                    @Override
                    public void onSubscribe(@NonNull Disposable d) {
                        Log.e(TAG, "onSubscribe: ");
                    }

                    @Override
                    public void onNext(@NonNull ItemData itemData) {
                        Log.e(TAG, "onNext: "+itemData.getData().getDatas() );
                    }

                    @Override
                    public void onError(@NonNull Throwable e) {
                        Log.e(TAG, "onError: "+e);
                    }

                    @Override
                    public void onComplete() {
                        Log.e(TAG, "onComplete: ");
                    }
                });
    }

五、doOnNext的運用

doOnNext操作符,允許我們從起點發射事件到終點期間對事件做一些額外的處理,然後繼續發射。例如我們發射一個長度爲3的集合,在發送期間,我們先對這個集合做一個校驗,然後繼續發射

private void test6() {
        List<String> list = new ArrayList<>();
        list.add("1");
        list.add("2");
        list.add("3");

        Observable.just(list)
                .doOnNext(new Consumer<List<String>>() {
            @Override
            public void accept(List<String> strings) throws Exception {
                check(list);
            }
        }).flatMap(new Function<List<String>, ObservableSource<String>>() {
            @Override
            public ObservableSource<String> apply(@NonNull List<String> strings) throws Exception {
                return Observable.fromIterable(strings);
            }
        }).subscribe(new Observer<String>() {
            @Override
            public void onSubscribe(@NonNull Disposable d) {

            }

            @Override
            public void onNext(@NonNull String s) {
                Log.e(TAG, "onNext: "+s );
            }

            @Override
            public void onError(@NonNull Throwable e) {
                Log.e(TAG, "onError: "+e);
            }

            @Override
            public void onComplete() {

            }
        });

    }

    private void check(List<String> list) {
        list.remove(0);
    }
2021-06-08 16:49:21.485 26310-26310/com.example.andoiddemo E/MainActivity: onNext: 2
2021-06-08 16:49:21.485 26310-26310/com.example.andoiddemo E/MainActivity: onNext: 3

六、小結

以上我們介紹了RxJava的核心思想,RxJava的主要核心思想就是基於起點和終點的模式,每一個事件從起點輸入到終點結束,起點和終點之間可以做相應的攔截處理。最後一道攔截處理的結果就是終點的結果。
又介紹了一些平時常用的開發場景防抖、配合Retrofit的使用、優雅的解決網絡嵌套問題,以及doOnNext操作符的介紹。以上還使用到了map和flatmap操作符,他們都是可以對事件發射期間變換處理,然後發射變換之後的數據。map發射的數據是我們的具體的對象,而flatmap發射的數據還是Observable包裝後的對象,flatmap可以發射多次。例如起點輸入的是一個集合,可以通過flatmap發射多次,發射集合裏的每一個元素。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章