摘要:本文主要是講與怎么結合的,也不會的操作符深入講解,不然就脫離了本文的重心廢話不多說了,一起來看看是如何使用的。延遲請求利用的操作符。
前言
之前在github開源過一個網絡庫RxEasyHttp,這是一款基于RxJava2+Retrofit2實現簡單易用的網絡請求框架。在這里對網絡庫的用法就不做過多介紹,感興趣的可以去了解下。在使用過程中一些網友反饋不知道怎么結合Rxjava2來實現一些場景需求,希望能夠寫一遍文章介紹下。終于抽出時間來對與Rxjava2在實際場景使用的一些案例做個簡單的總結和介紹。不知道怎么使用,主要是對RxEasyHttp有個誤區,RxEasyHttp不僅是支持采用鏈式調用一點到底方便使用,同時也支持返回Observable的用法,拿到了Observable自然就可以很好的利用Rxjava操作符了來實現各種強大的功能。本文主要是講RxEasyHttp與Rxjava2怎么結合的,也不會Rxjava2的操作符深入講解,不然就脫離了本文的重心!廢話不多說了,一起來看看是如何使用的。
場景介紹 場景一:延遲請求在頁面網絡接口請求中,不是希望立馬請求,而是需要延遲指定的時間后再去請求。
延遲請求:利用RxJava的timer操作符。
timer:主要作用就是創建一個Observable,它在一個給定的延遲后發射一個特殊的值,只是延遲發送一次并不會按照周期執行。
timer()源碼如下:
public static Observabletimer(long delay, TimeUnit unit) { return timer(delay, unit, Schedulers.computation()); }
可以看到采用timer()返回的是Observable
例如:延遲5秒請求
//延遲5s請求 Observable.timer(5, TimeUnit.SECONDS).flatMap(new Function>() { @Override public ObservableSource apply(@NonNull Long aLong) throws Exception { //延遲結束開始執行網絡請求 Observable observable = EasyHttp.get("/v1/app/chairdressing/skinAnalyzePower/skinTestResult") .timeStamp(true) .execute(SkinTestResult.class); return observable; } }).subscribe(new BaseSubscriber () { @Override protected void onStart() { } @Override public void onError(ApiException e) { showToast(e.getMessage()); } @Override public void onNext(@NonNull SkinTestResult skinTestResult) { Log.i("test", "=====" + skinTestResult.toString()); } }); //在不需要輪詢的時候,取消輪詢 //EasyHttp.cancelSubscription(polldisposable);
場景二:輪詢請求-無限輪詢timer在這里作用延遲5s結束時就會觸發網絡請求
flatMap在這里的作用就是將timer操作符返回的Observable
和網絡請求的Observable 做轉換,在subscribe訂閱時返回的內容,我們真正需要的SkinTestResult,而不是Long. 因此將Observable 變換成Observable 輸出SkinTestResult,完美達到目的。
在項目中需要用到每隔5s刷新一次頁面或者拉取最新消息。輪詢器大家一定不陌生,開發中無論是Java的Timer+TimeTask , 還是Android的Hanlder都可實現,現在介紹另一種簡單的實現方式。
無限輪詢:利用RxJava的Interval操作符。
interval:創建一個按固定時間間隔發射整數序列的Observable,它是按照周期執行的。源碼如下(只展示相關的兩個方法):
public static Observableinterval(long initialDelay, long period, TimeUnit unit) { return interval(initialDelay, period, unit, Schedulers.computation()); } public static Observable interval(long period, TimeUnit unit) { return interval(period, period, unit, Schedulers.computation()); }
可以看到采用interval()返回的是Observable
例如:間隔5s輪詢一次
//自己根據需要選擇合適的interval方法 Disposable polldisposable = Observable.interval(0, 5, TimeUnit.SECONDS).flatMap(new Function>() { @Override public ObservableSource apply(@NonNull Long aLong) throws Exception { return EasyHttp.get("/ajax.php") .baseUrl("http://fy.iciba.com") .params("a", "fy") .params("f", "auto") .params("t", "auto") .params("w", "hello world") //采用代理 .execute(new CallClazzProxy , Content>(Content.class) { }); } }).subscribeWith(new BaseSubscriber () { @Override public void onError(ApiException e) { showToast(e.getMessage()); } @Override public void onNext(@NonNull Content content) { showToast(content.toString()); } }); //在不需要輪詢的時候,取消輪詢 //EasyHttp.cancelSubscription(polldisposable);
場景三:輪詢請求-限定次數輪詢interval在這里作用每隔5s結束時就會觸發網絡請求
注意interval(0, 5, TimeUnit.SECONDS)和interval(5, TimeUnit.SECONDS)的區別,自己根據需要選擇合適的interval方法。
interval(0,5, TimeUnit.SECONDS):3個參數,第一個參數表示初始化延時多久開始請求,這里用0表示不延時直接請求,第二個參數表示間隔多久輪詢一次,這里表示間隔5s,第三個表示設置的時間單位。interval(5, TimeUnit.SECONDS):2個參數,其中的這個5就表示,初始延時5秒開始執行請求,輪詢也是5s,第二個表示設置的時間單位,從上面提供的interval()源碼可以看出。
flatMap在這里的作用就是將interval的Observable
和網絡請求的Observable 做轉換,輸出Content,而不是Long.
這個和無限輪詢用法基本一樣,只是多了輪詢的次數限制條件,不是一直無限的輪詢下去。
輪詢次數:利用RxJava的intervalRange或者take操作符。
intervalRange:以一個例子說明可能更清楚,intervalRange(0,3,0,5, TimeUnit.SECONDS)表示從0開始輸出3個數據,延遲0秒執行,每隔5秒執行一次。
take:表示只取前n項。這里用take和interval操作符聯合使用,由于一旦interval計時開始除了解綁就無法停止,使用take操作符就簡單很多了,它的意思是只釋放前n項,過后Observable流就自動終止。
例如:只輪詢3次
int count = 3;//輪詢3次 //方式一:采用intervalRange //Observable.intervalRange(0,count,0,5, TimeUnit.SECONDS).flatMap(new Function場景四:輪詢請求-條件輪詢>() { //方式一:采用take countdisposable = Observable.interval(0, 5, TimeUnit.SECONDS).take(count).flatMap(new Function >() { @Override public ObservableSource apply(@NonNull Long aLong) throws Exception { return EasyHttp.get("/ajax.php") .baseUrl("http://fy.iciba.com") .params("a", "fy") .params("f", "auto") .params("t", "auto") .params("w", "hello world") //采用代理 .execute(new CallClazzProxy , Content>(Content.class) { }); } }).subscribeWith(new BaseSubscriber () { @Override public void onError(ApiException e) { showToast(e.getMessage()); } @Override public void onNext(@NonNull Content content) { showToast(content.toString()); } }); //在不需要輪詢的時候,取消輪詢 //EasyHttp.cancelSubscription(polldisposable);
條件輪詢和限定次數輪詢比較像,都是起達到目的后終止輪詢。比如一個網絡請求一直在輪詢執行,直到獲取到了想要的內容后就終止掉輪詢。
條件輪詢:利用RxJava的takeUntil操作符。
takeUntil:使用一個標志Observable是否發射數據來判斷,當標志Observable沒有發射數據時,正常發射數據,而一旦標志Observable發射過了數據則后面的數據都會被丟棄。
例如:輪詢請求中如果返回的內容字符串中包含“示”就終止輪詢
Observable.interval(0, 5, TimeUnit.SECONDS).flatMap(new Function場景五:輪詢請求-過濾輪詢>() { @Override public ObservableSource apply(@NonNull Long aLong) throws Exception { return EasyHttp.get("/ajax.php") .baseUrl("http://fy.iciba.com") .params("a", "fy") .params("f", "auto") .params("t", "auto") .params("w", "hello world") //采用代理 .execute(new CallClazzProxy , Content>(Content.class) { }); } }).takeUntil(new Predicate () { @Override public boolean test(@NonNull Content content) throws Exception { //如果條件滿足,就會終止輪詢,這里邏輯可以自己寫 //結果為true,說明滿足條件了,就不在輪詢了 return content.getOut().contains("示"); } }).subscribeWith(new BaseSubscriber () { @Override public void onError(ApiException e) { showToast(e.getMessage()); } @Override public void onNext(@NonNull Content content) { showToast(content.toString()); } }); //在不需要輪詢的時候,取消輪詢 //EasyHttp.cancelSubscription(polldisposable);
過濾輪詢主要是指在輪詢的過程中對訂閱的內容做過濾,不是需要的內容就不會返回給訂閱者,但是它不會中斷輪詢。過濾輪詢也可以理解成是無限輪詢加了一個過濾條件而已。
過濾輪詢:利用Rxjava的filter操作符。
filter:是對源Observable產生的結果按照指定條件進行過濾,只有滿足條件的結果才會提交給訂閱者。
例如:返回的狀態碼如果是錯誤就不返回給訂閱者,不更新界面(只有保證每次請求成功才刷新界面),但是會繼續輪詢請求
Disposable filterdisposable = Observable.interval(0, 5, TimeUnit.SECONDS).flatMap(new Function>() { @Override public ObservableSource apply(@NonNull Long aLong) throws Exception { return EasyHttp.get("/ajax.php") .baseUrl("http://fy.iciba.com") .params("a", "fy") .params("f", "auto") .params("t", "auto") .params("w", "hello world") //采用代理 .execute(new CallClazzProxy , Content>(Content.class) { }); } }).filter(new Predicate () { @Override public boolean test(@NonNull Content content) throws Exception { //如果不滿足條件就過濾該條輪詢數據,但是輪詢還是一直執行 //ErrNo==0表示成功,如果不等于0就認為失敗,content不會返回給訂閱者 return content.getErrNo() != 0; } }).subscribeWith(new BaseSubscriber () { @Override public void onError(ApiException e) { showToast(e.getMessage()); } @Override public void onNext(@NonNull Content content) { showToast(content.toString()); } }); //在不需要輪詢的時候,取消輪詢 //EasyHttp.cancelSubscription(polldisposable);
場景六:嵌套請求filter操作符在這里只是為了舉列說明,是自己為了講解定義了一個過濾輪詢的概念,不是說filter只能在輪詢這里使用,它是可以和其它任何Rxjava操作符配合使用。
切記takeUntil和 filter的區別,takeUntil找到自己想要的數據后就結束了流,不再執行任何操作。filter發現不符合條件的不會給訂閱者,只有符合條件的才給訂閱者,發現不符合的,不會中斷操作。
在開發中由于請求網絡數據頻繁,往往后面一個請求的參數是前面一個請求的結果,于是經常需要在前面一個請求的響應中去發送第二個請求,從而造成“請求嵌套”的問題。如果層次比較多,代碼可讀性和效率都是問題。嵌套請求:利用RxJava的flatMap操作符。
flatMap:是一個Observable的操作符,接受一個Func1閉包,這個閉包的第一個函數是待操作的上一個數據流中的數據類型,第二個是這個flatMap操作完成后返回的數據類型的被封裝的Observable。說白了就是將一個多級數列“拍扁”成了一個一級數列。
例如:網絡請求獲取到token后,把token當成另一個接口的參數,一個接口依賴另一個接口。
//第一個網絡請求獲取到token Observablelogin = EasyHttp.post(ComParamContact.Login.PATH) .params(ComParamContact.Login.ACCOUNT, "186****4275") .params(ComParamContact.Login.PASSWORD, MD5.encrypt4login("123456", AppConstant.APP_SECRET)) .sign(true) .timeStamp(true).execute(AuthModel.class); login.flatMap(new Function >() { @Override public ObservableSource apply(@NonNull AuthModel authModel) throws Exception { //獲取到的token,給到第二個網絡當參數。第二個網絡開始請求 return EasyHttp.get("/v1/app/chairdressing/skinAnalyzePower/skinTestResult") .params("accessToken", authModel.getAccessToken())//這個地方只是舉例,并不一定是需要accessToken .timeStamp(true) .execute(SkinTestResult.class); } }).subscribe(new ProgressSubscriber (this, mProgressDialog) { @Override public void onError(ApiException e) { super.onError(e); showToast(e.getMessage()); } @Override public void onNext(SkinTestResult skinTestResult) { showToast(skinTestResult.toString()); } });
本例中只是展示了2個接口的嵌套請求,flatMap其實是可以支持嵌套很多個接口請求場景七:合并請求(zip)
zip合并請求就是指當一個頁面有多個不同的數據來源,既就是有多個不同的網絡請求接口,等待這些所有的接口都請求完成后才返回給訂閱者,刷新界面等操作。
zip:使用一個函數組合多個Observable發射的數據集合,然后再發射這個結果.
例如:一個頁面有3個不同數據來源的網絡請求接口,等待全部請求完成后才返回
ObservablemobileObservable = EasyHttp.get("http://apis.juhe.cn/mobile/get") .params("phone", "18688994275") .params("dtype", "json") .params("key", "5682c1f44a7f486e40f9720d6c97ffe4") .execute(new CallClazzProxy , ResultBean>(ResultBean.class) { }); Observable searchObservable = EasyHttp.get("/ajax.php") .baseUrl("http://fy.iciba.com") .params("a", "fy") .params("f", "auto") .params("t", "auto") .params("w", "hello world") //采用代理 .execute(new CallClazzProxy , Content>(Content.class) { }); Observable > listObservable = EasyHttp.get("http://news-at.zhihu.com/api/3/sections") .execute(new CallClazzProxy
>, List >(new TypeToken >() { }.getType()) { }); //new Function3最后一個參數這里用的是List
1.zip的參數中前幾個參數都表示數據源ObservableSource,最后一個參數new Function會輸出結果,前面如果有2個數據源就用Function2,3個數據源就用Function3.場景八:合并請求(merge)
2.Function的作用主要是會返回所有參與zip的結果內容,例如本例是有3個數據源(3個網絡接口請求對應的Observable),返回的內容是new Function3, List 其中ResultBean, Content, List> 是三個網絡接口各自返回的內容。但是最后一個參數List 表示什么呢?這一個就表示最終要給訂閱著subscribe的內容,例如本例new BaseSubscriber >()
。這里我是將返回的結果用一個List集合存起來,然后返回給訂閱者。最終想返回什么是根據自己的業務邏輯需求而定,并不一定就是List,切記!切記!切記!
剛才講解了zip合并請求,這是合并請求的另一種場景實現方式merge,一定要注意merge和zip的區別,雖然都是合并多個請求,但是是有區別的,請注意使用場景. 本例中利用Rxjava的merge、mergeDelayError操作符。
merge:將多個Observalbe發射的數據項,合并到一個Observable中再發射出去,可能會讓合并的Observable發射的數據交錯(concat操作符是連接不會出現交錯),如果在合并的途中出現錯誤,就會立即將錯誤提交給訂閱者,將終止合并后的Observable。
mergeDelayError:mergeDelayError操作符類似于merge操作符,唯一不同就是如果在合并途中出現錯誤,不會立即發射錯誤通知,而是保留錯誤直到合并后的Observable將所有的數據發射完成,此時才會將onError提交給訂閱者。
例如:一個界面有兩個網絡接口請求
//這個請求故意延時5秒再發送->最后測試結果發現,并不是searchObservable等待mobileObservable5秒后再發送,目的驗證無序的概念 ObservablemobileObservable = Observable.timer(5, TimeUnit.SECONDS).flatMap(new Function >() { @Override public ObservableSource apply(@NonNull Long aLong) throws Exception { return EasyHttp.get("http://apis.juhe.cn/mobile/get") .params("phone", "18688994275") .params("dtype", "json") .params("key", "5682c1f44a7f486e40f9720d6c97ffe4") .execute(new CallClazzProxy , ResultBean>(ResultBean.class) { }); } }); Observable searchObservable = EasyHttp.get("/ajax.php") .baseUrl("http://fy.iciba.com") .params("a", "fy") .params("f", "auto") .params("t", "auto") .params("w", "hello world") //采用代理 .execute(new CallClazzProxy , Content>(Content.class) { }); //Observable.merge(mobileObservable,searchObservable).subscribe(new BaseSubscriber () { Observable.mergeDelayError(mobileObservable,searchObservable).subscribe(new BaseSubscriber () { @Override public void onError(ApiException e) { showToast(e.getMessage()); } @Override public void onNext(@NonNull Object object) { //為什么用Object接收,因為兩個接口請求返回的數據類型不是一樣的,如果是一樣的就用具體的對象接收就可以了, // 不再需要instanceof麻煩的判斷 if (object instanceof ResultBean) {//mobileObservable 返回的結果 //處理 ResultBean邏輯 } else if (object instanceof Content) { //處理 Content邏輯 } showToast(object.toString()); } });
其中提到一個概念:合并的Observable發射的數據交錯,也就是發射的數據無序,怎么理解它呢?例如:代碼中merge(mobileObservable,searchObservable)合并的mobileObservable和searchObservable,其中mobileObservable在前,并不代表訂閱的subscribe->onNext(@NonNull Object object) 就先返回來數據,也可能是合并的searchObservable數據先返回回來。
延伸講解,剛才講解了merge操作符是無序的,如果想保證有序執行怎么辦呢,采用Rxjava的concat操作符
看完場景七和場景八:merge和zip的區別到底是什么呢?
merge和zip都是將多個Observalbe發射的數據項,合并到一個Observable中再發射出去。只是在發射的結果上有所不同。例如有3個網絡請求的Observalbe,zip是等待這3個請求都完成后才一起返回,既onNext調用1次。merge是3個Observalbe分別返回,而且無序,既onNext調用3次,相當于把3個本來分散的網絡請求。寫在同一個地方合并起來執行。
場景九:避免重復請求merge、mergeDelayError都是合并,但是需要注意二者區別。
merge合并的請求,如果有一個接口報錯了,就立馬報錯,會終止整個流,另外的接口也不會請求。
mergeDelayError合并的請求,如果有一個接口報錯了,會延遲錯誤處理,后面的接口會繼續執行沒有被中斷。
這里主要是講解RxEasyHttp如何結合rxbinding庫compile "com.jakewharton.rxbinding2:rxbinding:2.0.0"來使用。對rxbinding庫不了解的,可以自己去學習一下,這里不詳細介紹。在頁面請求中可能操作太快導致同一個網絡請求重復執行,這里結合view來說明,點擊一個按鈕去請求網絡,點擊太快就會重復執行-界面防抖。
避免重復請求:利用Rxjavad的throttleFirst操作符。
throttleFirst:會定期發射這個時間段里源Observable發射的第一個數據。
例如:點擊按鈕去請求網絡,1s內避免點擊過快重復請求
RxView.clicks(view).throttleFirst(1, TimeUnit.SECONDS).flatMap(new Function場景十:減少頻繁的網絡請求>() { @Override public ObservableSource apply(@NonNull Object o) throws Exception { return EasyHttp.get("http://apis.juhe.cn/mobile/get") .params("phone", "18688994275") .params("dtype", "json") .params("key", "5682c1f44a7f486e40f9720d6c97ffe4") .execute(new CallClazzProxy , ResultBean>(ResultBean.class) { }); } }).subscribe(new BaseSubscriber () { @Override public void onError(ApiException e) { showToast(e.getMessage()); } @Override public void onNext(@NonNull ResultBean resultBean) { showToast(resultBean.toString()); } });
這里也是主要講解RxEasyHttp如何結合rxbinding庫compile "com.jakewharton.rxbinding2:rxbinding:2.0.0"來使用。對rxbinding庫不了解的,可以自己去學習一下,這里不介紹用法。像即時搜索功能,在輸入框中輸入內容,實時搜索結果展示。
減少頻繁的網絡請求:利用Rxjavad的debounce操作符。
debounce:對源Observable每產生一個結果后,如果在規定的間隔時間內沒有別的結果產生,則把這個結果提交給訂閱者處理,否則忽略該結果。簡單的理解就是:當N個結點發生的時間太靠近(即發生的時間差小于設定的值T),debounce就會自動過濾掉前N-1個結點。
例如:即時搜索,避免每輸入(刪除)一個字就做一次請求,500毫秒才讓它去請求一次網絡,這樣可以避免數據混亂,也優了app性能。
Disposable mDisposable = RxTextView.textChangeEvents(mEditText) .debounce(500, TimeUnit.MILLISECONDS).filter(new Predicate場景十一:網絡數據緩存() { @Override public boolean test(@NonNull TextViewTextChangeEvent textViewTextChangeEvent) throws Exception { // 過濾,把輸入字符串長度為0時過濾掉 String key = textViewTextChangeEvent.text().toString(); //這里可以對key進行過濾的判斷邏輯 return key.trim().length() > 0; } }).flatMap(new Function >() { @Override public ObservableSource apply(@NonNull TextViewTextChangeEvent textViewTextChangeEvent) throws Exception { String key = textViewTextChangeEvent.text().toString(); Log.d("test", String.format("Searching for: %s", textViewTextChangeEvent.text().toString())); return EasyHttp.get("/ajax.php") .baseUrl("http://fy.iciba.com") .params("a", "fy") .params("f", "auto") .params("t", "auto") .params("w", key) //采用代理 .execute(new CallClazzProxy , Content2>(Content2.class) { }); } }).subscribeWith(new BaseSubscriber () { @Override protected void onStart() { } @Override public void onError(ApiException e) { mTextView.setText(e.getMessage()); } @Override public void onNext(@NonNull Content2 content) { mTextView.setText(content.toString()); } }); //取消請求 //EasyHttp.cancelSubscription(mDisposable);
網絡數據緩存在RxEasyHttp網絡庫中已經封裝了,也就是開發者在使用網絡庫過程中不必關心緩存具體的實現,通過RxEasyHttp庫文檔講解調用一些配置參數就可以實現需要的緩存功能了。這里主要講解的是不使用okhttp和Retrofit的緩存,而是介紹自己如何封裝緩存。RxEasyHttp網絡庫除了支持無緩存和默認的http緩存之外,又提供了其它6種場景緩存:
FIRSTREMOTE:先請求網絡,請求網絡失敗后再加載緩存
FIRSTCACHE:先加載緩存,緩存沒有再去請求網絡
ONLYREMOTE:僅加載網絡,但數據依然會被緩存
ONLYCACHE:只讀取緩存,緩存沒有會返回null
CACHEANDREMOTE:先使用緩存,不管是否存在,仍然請求網絡,CallBack會回調兩次.
CACHEANDREMOTEDISTINCT:先使用緩存,不管是否存在,仍然請求網絡,CallBack回調不一定是兩次,如果發現請求的網絡數據和緩存數據是一樣的,就不會再返回網絡的回調,既回調一次。否則不相同仍然會回調兩次。(目的是為了防止數據沒有發生變化,也需要回調兩次導致界面無用的重復刷新)
如果對這部分源碼敢興趣的請查看RxEasyHttp源碼,由于此部分涉及的知識點和內容比較多,篇幅問題,準備在下一篇文章中多帶帶介紹網絡數據緩存的各大場景實現,敬請期待!
到這里幾大常用的RxEasyHttp網絡庫結合Rxjava2場景的例子已經介紹完了,也證明了RxEasyHttp網絡庫與Rxjava2是可以完美結合的,在實際開發運用中的場景肯定有很多種,例子是怎么也舉不完的,需要靈活運用,如何寫這一個自己的場景呢,我覺得需要如下幾點:
充分了解自己的需求(需要的場景),明確目的(例如:我是想做一個網絡輪詢的,最終需要訂閱者給我返回什么);
熟悉Rxjava操作符,看看哪個操作符是符合需求場景的,或者多個操作符串聯起來是符合需求場景,了解每個操作符單個用法和訂閱返回的值;
準備好網絡請求,保證網絡請求對象返回的是Observable,這樣與Rxjava結合才能成為可能;
將Rxjava單個操作符或者多個操作符,與網絡庫的Observable進行結合,這里面可能是需要flatMap,Function等操作;
文章難免有疏漏之處,如果您有任何疑議,請反饋給我,謝謝!同時大家有與網絡相關更好更常用的場景請推薦給我,我會繼續完善。喜歡請點贊,謝謝!
源碼下載對場景應用感興趣的小伙伴們可以將Demo下載下來進行參考。
項目地址
https://github.com/zhou-you/RxEasyHttp歡迎加入我的圈子
引用文字
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/68024.html
摘要:本文主要是講與怎么結合的,也不會的操作符深入講解,不然就脫離了本文的重心廢話不多說了,一起來看看是如何使用的。延遲請求利用的操作符。 前言 之前在github開源過一個網絡庫RxEasyHttp,這是一款基于RxJava2+Retrofit2實現簡單易用的網絡請求框架。在這里對網絡庫的用法就不做過多介紹,感興趣的可以去了解下。在使用過程中一些網友反饋不知道怎么結合Rxjava2來實現一...
摘要:動態代理個經紀人如何代理個明星掘金在代理模式女朋友這么漂亮,你缺經紀人嗎中我們用寶強的例子介紹了靜態代理模式的概念。掘金使用從頭創建一個,這種方法比較簡單。 動態代理:1 個經紀人如何代理 N 個明星 - Android - 掘金在 代理模式:女朋友這么漂亮,你缺經紀人嗎? 中我們用寶強的例子介紹了靜態代理模式的概念。 本來我的目的是通過大家耳熟能詳的例子來加深理解,但是有些網友指責...
閱讀 3503·2021-11-24 09:39
閱讀 781·2019-08-30 14:22
閱讀 3031·2019-08-30 13:13
閱讀 2310·2019-08-29 17:06
閱讀 2918·2019-08-29 16:22
閱讀 1255·2019-08-29 10:58
閱讀 2427·2019-08-26 13:47
閱讀 1628·2019-08-26 11:39