RxJava之五—— observeOn 與subscribeOn 的詳解
分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow
也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!
轉載請標明出處:http://blog.csdn.net/xx326664162/article/details/51967967
你也可以檢視我的其他同類文章,也會讓你有一定的收貨!
關於RxJava,從表面上看起來很容易使用,但是如果理解不夠深刻,使用過程中,往往會出現一些問題,所以我寫了五篇文章,從入門到精通,從簡單的使用到部分原始碼詳解,希望能給讀者一個質的飛躍:
1、RxJava之一——一次性學會使用RxJava RxJava簡單的使用和使用它的好處
2、RxJava之二——Single和Subject 與Observable舉足輕重的類,雖然用的少,但應該知道
3、RxJava之三——RxJava 2.0 全部操作符示例
4、RxJava之四—— Lift()詳解
5、RxJava之五—— observeOn()與subscribeOn()的詳解Scheduler執行緒切換的原理
6、RxJava之六——RxBus 通過RxJava來替換EventBus
為什麼多次呼叫subscribeOn()卻只有第一個起作用?
為什麼多次呼叫observeOn()卻可以切換到不同執行緒
observeOn()後能不能再次呼叫subscribeOn()?
如果你有這些疑問,那接下來的內容必定能解決你心頭的疑惑
subscribeOn()和observeOn()的區別
subscribeOn()和observeOn()都是用來切換執行緒用的
- subscribeOn()改變呼叫它之前程式碼的執行緒
- observeOn()改變呼叫它之後程式碼的執行緒
這裡給出下面示例中用到的兩個函式
//用指定的名稱新建一個執行緒public static Scheduler getNamedScheduler(final String name) { return Schedulers.from(Executors.newCachedThreadPool(new ThreadFactory() { @Override public Thread newThread(@android.support.annotation.NonNull Runnable runnable) { return new Thread(runnable, name); } })); }//列印當前執行緒的名稱public static void threadInfo(String caller) { System.out.println(caller + " => " + Thread.currentThread().getName()); }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
一、subscribeOn()
在講解他的原理之前,先來一個簡單的例子,有個感性認識,學起來更輕鬆
先說結論:subscribeOn 作用於該操作符之前的 Observable 的建立操符作以及 doOnSubscribe 操作符 ,換句話說就是 doOnSubscribe 以及 Observable 的建立操作符總是被其之後最近的 subscribeOn 控制 。沒看懂不要緊,看下面程式碼和圖你就懂了。
Observable .create(new Observable.OnSubscribe<String>() { @Override public void call(Subscriber<? super String> subscriber) { threadInfo("OnSubscribe.call()"); subscriber.onNext("RxJava"); } }) .subscribeOn(getNamedScheduler("create之後的subscribeOn")) .doOnSubscribe(() -> threadInfo(".doOnSubscribe()-1")) .subscribeOn(getNamedScheduler("doOnSubscribe1之後的subscribeOn")) .doOnSubscribe(() -> threadInfo(".doOnSubscribe()-2")) .subscribe(s -> { threadInfo(".onNext()"); System.out.println(s + "-onNext"); });
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
結果如下:
.doOnSubscribe()-2 => main.doOnSubscribe()-1 => doOnSubscribe1之後的subscribeOnOnSubscribe.call() => create之後的subscribeOn.onNext() => create之後的subscribeOnRxJava-onNext
- 1
- 2
- 3
- 4
- 5
3號框中的.doOnSubscribe(() -> threadInfo(“.doOnSubscribe()-2”)) 的之後由於沒有subscribeOn操作符所以回撥到該段程式碼被呼叫的執行緒(即主執行緒)
由於 subscribe 之前 沒有 使用observeOn 指定Scheduler,所以.onNext()的執行緒是和OnSubscribe.call()使用相同的Scheduler 。
下面通過原始碼來分析一下:
1、示例程式碼:
Observable .create(new Observable.OnSubscribe<String>() { @Override public void call(Subscriber<? super String> subscriber) { subscriber.onNext("a"); subscriber.onNext("b"); subscriber.onCompleted(); } }) .subscribeOn(Schedulers.io()) .subscribe(new Observer<String>() { @Override public void onCompleted() { } @Override public void onError(Throwable e) { } @Override public void onNext(String integer) { System.out.println(integer); } });
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
執行如下:
ab
- 1
- 2
2、subscribeOn()原始碼
public final Observable<T> subscribeOn(Scheduler scheduler) { if (this instanceof ScalarSynchronousObservable) { return ((ScalarSynchronousObservable<T>)this).scalarScheduleOn(scheduler); } return create(new OperatorSubscribeOn<T>(this, scheduler)); }
- 1
- 2
- 3
- 4
- 5
- 6
很明顯,會走if之外的方法。
在這裡我們可以看到,又建立了一個OperatorSubscribeOn物件,但建立時傳入的引數為OperatorSubscribeOn(this,scheduler),我們看一下此物件以及其對應的構造方法
3、create()的原始碼:
public static <T> Observable<T> create(OnSubscribe<T> f) { return new Observable<T>(hook.onCreate(f)); }
- 1
- 2
- 3
我們看到這個方法,使用OperatorSubscribeOn這個類,來建立一個新的Observable,那就把它叫做Observable_2,把原來的Observable叫做Observable_1
4、OperatorSubscribeOn類的原始碼:
public final class OperatorSubscribeOn<T> implements OnSubscribe<T> { final Scheduler scheduler; final Observable<T> source; public OperatorSubscribeOn(Observable<T> source, Scheduler scheduler) { this.scheduler = scheduler; this.source = source; } @Override public void call(final Subscriber<? super T> subscriber) { final Worker inner = scheduler.createWorker(); subscriber.add(inner); inner.schedule(new Action0() { @Override public void call() { final Thread t = Thread.currentThread(); Subscriber<T> s = new Subscriber<T>(subscriber) { @Override public void onNext(T t) { subscriber.onNext(t); } @Override public void onError(Throwable e) { try { subscriber.onError(e); } finally { inner.unsubscribe(); } } @Override public void onCompleted() { try { subscriber.onCompleted(); } finally { inner.unsubscribe(); } } @Override public void setProducer(final Producer p) { subscriber.setProducer(new Producer() { @Override public void request(final long n) { if (t == Thread.currentThread()) { p.request(n); } else { inner.schedule(new Action0() { @Override public void call() { p.request(n); } }); } } }); } }; source.unsafeSubscribe(s); } }); }}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- OperatorSubscribeOn類implements 了Onsubscribe介面,並實現call()方法
- OperatorSubscribeOn的構造方法,
- 儲存了Observable物件,就是呼叫了subscribeOn()方法的Observable物件
- 並儲存了Scheduler物件。
這裡做個總結。
把Observable.create()建立的稱之為Observable_1,OnSubscribe_1。
把subscribeOn()建立的稱之為Observable_2,OnSubscribe_2
Observable_1是由示例程式碼的第1、2行建立的
OperatorSubscribeOn類是implements Onsubscribe介面的,所以可以當做Onsubscribe類使用。(OnSubscribe_2)
並且OnSubscribe_2中儲存了Observable_1的應用,即source。(在OperatorSubscribeOn原始碼的第8行)
在subscribeOn()原始碼的倒數第二行,
create(new OperatorSubscribeOn<T>(this, scheduler))
返回新建立的Observable_2物件。
4.1、分析call()方法。
- inner.schedule()改變了執行緒,此時Action的call()執行在指定的執行緒中。
- 把示例程式碼中的Subscriber包裝了一層,賦給物件S(Subscriber_2)。見上面程式碼21行。
- source.unsafeSubscribe(s);,
- 注意:source是Observable_1物件,這裡的s就是Subscriber_2
- 因為呼叫過subscribeOn(Schedulers.io())後,返回Observable_2物件,所以示例程式碼第13行程式碼的subscribe()就是Observable_2.subscribe(),也就是執行OnSubscribe_2的call()方法(即OperatorSubscribeOn類的原始碼的第12行)。
4.2 看一下source.unsafeSubscribe(s);(第65行)程式碼都做了什麼
這裡的source就是Observable_1,s是Subscriber_2
unsafeSubscribe()原始碼:
public final Subscription unsafeSubscribe(Subscriber<? super T> subscriber) { try { // new Subscriber so onStart it subscriber.onStart(); // allow the hook to intercept and/or decorate hook.onSubscribeStart(this, onSubscribe).call(subscriber); return hook.onSubscribeReturn(subscriber); } catch (Throwable e) { // special handling for certain Throwable/Error/Exception types Exceptions.throwIfFatal(e); // if an unhandled error occurs executing the onSubscribe we will propagate it try { subscriber.onError(hook.onSubscribeError(e)); } catch (Throwable e2) { Exceptions.throwIfFatal(e2); // if this happens it means the onError itself failed (perhaps an invalid function implementation) // so we are unable to propagate the error correctly and will just throw RuntimeException r = new RuntimeException("Error occurred attempting to subscribe [" + e.getMessage() + "] and then again while trying to pass to onError.", e2); // TODO could the hook be the cause of the error in the on error handling. hook.onSubscribeError(r); // TODO why aren't we throwing the hook's return value. throw r; } return Subscriptions.unsubscribed(); } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
關鍵程式碼:
hook.onSubscribeStart(this, onSubscribe).call(subscriber);
- 1
該方法即呼叫了OnSubscribe_1.call()方法。
注意,此時的call()方法在我們指定的執行緒中執行。起到了改變執行緒的作用。
對於以上執行緒,我們可以總結,其有如下流程:
Observable.create() : 建立了Observable_1和OnSubscribe_1;
subscribeOn(): 建立Observable_2和OperatorSubscribeOn(OnSubscribe_2),同時OperatorSubscribeOn儲存了Observable_1的引用。
示例程式碼中的subscribe(Observer) 實際上就是呼叫Observable_2.subscribe(Observer):
- 呼叫OperatorSubscribeOn的call()。call()改變了執行緒的執行,並且呼叫了Observable_1.unsafeSubscribe(s);
- Observable_1.unsafeSubscribe(s);,該方法的實現中呼叫了OnSubscribe_1的call()。
這樣就實現了在指定執行緒執行OnSubscribe的call()函式,無論我們的subscribeOn()放在哪裡,他改變的是subscribe()的過程,而不是onNext()的過程。
那麼如果有多個subscribeOn(),那麼執行緒會怎樣執行呢。如果按照我們的邏輯,有以下程式
Observable.just("ss") .subscribeOn(Schedulers.io()) // ----1--- .subscribeOn(Schedulers.newThread()) //----2---- .subscribe(new Action1<String>() { @Override public void call(String s) { } });
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
那麼,我們根據之前的原始碼分析其執行邏輯。
Observable.just(“ss”),建立Observable,OnSubscribe
Observable_1.subscribeOn(Schedulers.io()):建立Observable_1,OperatorSubscribeOn_1並儲存Observable的引用。
Observable_2.subscribeOn(Schedulers.newThread()):建立Observable_2,OperatorSubscribeOn_2並儲存Observable_1的引用。
Observable_3.subscribe():
- 呼叫OperatorSubscribeOn_2.call(),改變執行緒為Schedulers.newThread()。
- 呼叫OperatorSubscribeOn_1.call(),改變執行緒為Schedulers.io()。
- 呼叫OnSubscribe.call(),此時call()執行在Schedulers.io()。
根據以上邏輯分析,會按照1的執行緒進行執行。
二、observeOn()
先說結論:observeOn作用於該操作符之後操作符直到出現新的observeOn操作符
舉個例子:
Observable.just("RxJava") .observeOn(getNamedScheduler("map之前的observeOn")) .map(s -> { threadInfo(".map()-1"); return s + "-map1"; }) .map( s -> { threadInfo(".map()-2"); return s + "-map2"; }) .observeOn(getNamedScheduler("subscribe之前的observeOn")) .subscribe(s -> { threadInfo(".onNext()"); System.out.println(s + "-onNext"); });
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
結果如下:
.map()-1 => map之前的observeOn.map()-2 => map之前的observeOn.onNext() => subscribe之前的observeOnRxJava-map1-map2-onNext
- 1
- 2
- 3
- 4
下面通過原始碼來進行分析:
1、observeOn()原始碼
public final Observable<T> observeOn(Scheduler scheduler) { return observeOn(scheduler, RxRingBuffer.SIZE); }public final Observable<T> observeOn(Scheduler scheduler, int bufferSize) { return observeOn(scheduler, false, bufferSize); }public final Observable<T> observeOn(Scheduler scheduler, boolean delayError, int bufferSize) { if (this instanceof ScalarSynchronousObservable) { return ((ScalarSynchronousObservable<T>)this).scalarScheduleOn(scheduler); } return lift(new OperatorObserveOn<T>(scheduler, delayError, bufferSize)); }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
這裡引出了lift()函式
public final <R> Observable<R> lift(final Operator<? extends R, ? super T> operator) { return new Observable<R>(new OnSubscribeLift<T, R>(onSubscribe, operator)); }
- 1
- 2
- 3
關於lift的詳細介紹,如果不明白lift的原理,參考這裡:RxJava 之二—— Lift()詳解
用OperatorObserveOn物件,建立OnSubscribeLift物件(實現了OnSubscribe介面),接著建立Observable物件。為了加以區分,這裡我們把OnSubscribeLift叫做OnSubscribe_2,Observable叫做Observable_2。
2、OperatorObserveOn程式碼:
public final class OperatorObserveOn<T> implements Operator<T, T> { private final Scheduler scheduler; private final boolean delayError; private final int bufferSize; /** * @param scheduler the scheduler to use * @param delayError delay errors until all normal events are emitted in the other thread? */ public OperatorObserveOn(Scheduler scheduler, boolean delayError) { this(scheduler, delayError, RxRingBuffer.SIZE); } /** * @param scheduler the scheduler to use * @param delayError delay errors until all normal events are emitted in the other thread? * @param bufferSize for the buffer feeding the Scheduler workers, defaults to {@code RxRingBuffer.MAX} if <= 0 */ public OperatorObserveOn(Scheduler scheduler, boolean delayError, int bufferSize) { this.scheduler = scheduler; this.delayError = delayError; this.bufferSize = (bufferSize > 0) ? bufferSize : RxRingBuffer.SIZE; } @Override public Subscriber<? super T> call(Subscriber<? super T> child) { if (scheduler instanceof ImmediateScheduler) { // avoid overhead, execute directly return child; } else if (scheduler instanceof TrampolineScheduler) { // avoid overhead, execute directly return child; } else { ObserveOnSubscriber<T> parent = new ObserveOnSubscriber<T>(scheduler, child, delayError, bufferSize); parent.init(); return parent; } } public static <T> Operator<T, T> rebatch(final int n) { return new Operator<T, T>() { @Override public Subscriber<? super T> call(Subscriber<? super T> child) { ObserveOnSubscriber<T> parent = new ObserveOnSubscriber<T>(Schedulers.immediate(), child, false, n); parent.init(); return parent; } }; } /** Observe through individual queue per observer. */ static final class ObserveOnSubscriber<T> extends Subscriber<T> implements Action0 { final Subscriber<? super T> child; final Scheduler.Worker recursiveScheduler; final NotificationLite<T> on; final boolean delayError; final Queue<Object> queue; /** The emission threshold that should trigger a replenishing request. */ final int limit; // the status of the current stream volatile boolean finished; final AtomicLong requested = new AtomicLong(); final AtomicLong counter = new AtomicLong(); /** * The single exception if not null, should be written before setting finished (release) and read after * reading finished (acquire). */ Throwable error; /** Remembers how many elements have been emitted before the requests run out. */ long emitted; // do NOT pass the Subscriber through to couple the subscription chain ... unsubscribing on the parent should // not prevent anything downstream from consuming, which will happen if the Subscription is chained public ObserveOnSubscriber(Scheduler scheduler, Subscriber<? super T> child, boolean delayError, int bufferSize) { this.child = child; this.recursiveScheduler = scheduler.createWorker(); this.delayError = delayError; this.on = NotificationLite.instance(); int calculatedSize = (bufferSize > 0) ? bufferSize : RxRingBuffer.SIZE; // this formula calculates the 75% of the bufferSize, rounded up to the next integer this.limit = calculatedSize - (calculatedSize >> 2); if (UnsafeAccess.isUnsafeAvailable()) { queue = new SpscArrayQueue<Object>(calculatedSize); } else { queue = new SpscAtomicArrayQueue<Object>(calculatedSize); } // signal that this is an async operator capable of receiving this many request(calculatedSize); } void init() { // don't want this code in the constructor because `this` can escape through the // setProducer call Subscriber<? super T> localChild = child; localChild.setProducer(new Producer() { @Override public void request(long n) { if (n > 0L) { BackpressureUtils.getAndAddRequest(requested, n); schedule(); } } }); localChild.add(recursiveScheduler); localChild.add(this); } @Override public void onNext(final T t) { if (isUnsubscribed() || finished) { return; } if (!queue.offer(on.next(t))) { onError(new MissingBackpressureException()); return; } schedule(); } @Override public void onCompleted() { if (isUnsubscribed() || finished) { return; } finished = true; schedule(); } @Override public void onError(final Throwable e) { if (isUnsubscribed() || finished) { RxJavaHooks.onError(e); return; } error = e; finished = true; schedule(); } protected void schedule() { if (counter.getAndIncrement() == 0) { recursiveScheduler.schedule(this); } } // only execute this from schedule() @Override public void call() { long missed = 1L; long currentEmission = emitted; // these are accessed in a tight loop around atomics so // loading them into local variables avoids the mandatory re-reading // of the constant fields final Queue<Object> q = this.queue; final Subscriber<? super T> localChild = this.child; final NotificationLite<T> localOn = this.on; // requested and counter are not included to avoid JIT issues with register spilling // and their access is is amortized because they are part of the outer loop which runs // less frequently (usually after each bufferSize elements) for (;;) { long requestAmount = requested.get(); while (requestAmount != currentEmission) { boolean done = finished; Object v = q.poll(); boolean empty = v == null; if (checkTerminated(done, empty, localChild, q)) { return; } if (empty) { break; } localChild.onNext(localOn.getValue(v)); currentEmission++; if (currentEmission == limit) { requestAmount = BackpressureUtils.produced(requested, currentEmission); request(currentEmission); currentEmission = 0L; } } if (requestAmount == currentEmission) {
相關推薦
RxJava之五—— observeOn()與subscribeOn()的詳解
你也可以檢視我的其他同類文章,也會讓你有一定的收貨!
為什麼多次呼叫subscribeOn()卻只有第一個起作用?
為什麼多次呼叫observeOn()卻可以切換到不同執行緒
observeOn()後能不能再次呼叫subscribeOn()?
如
RxJava之五—— observeOn 與subscribeOn 的詳解
分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow
也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!
 
RxJava之三—— observeOn()與subscribeOn()的詳解
你也可以檢視我的其他同類文章,也會讓你有一定的收貨!
為什麼多次呼叫subscribeOn()卻只有第一個起作用?
為什麼多次呼叫observeOn()卻可以切換到不同執行緒
observeOn()後能不能再次呼叫subscribeOn()?
java資料結構之:ArrayList與LinkedList詳解
前篇博文講到了Java底層怎麼知道ArrayList和LinkedList哪個是隨機訪問哪個是順序訪問的,本片博文主要介紹各自的資料特點。
ArrayList:
從他的這個建構函式我們可以知道他的底層實現就是一個Object物件陣列。
第二個建構函式構造一個空的
MySQL高階 之 索引失效與優化詳解
案例所用的表結構、索引、與資料如下:
索引失效與優化
1、全值匹配我最愛
2、最佳左字首法則(帶頭索引不能死,中間索引不能斷)
如果索引了多個列,要遵守最佳左字首法則。指的是查詢從索引的最左前列開始 並且 不跳過索引中的列。
正確的示例參考上圖。
錯誤的示例:
.Neter玩轉Linux系列之五:crontab使用詳解和Linux的程序管理以及網路狀態監控
基礎篇
實戰篇
一、crontab使用詳解
概述:任務排程:是指系統在某個時間執行的特定的命令或程式。
任務排程分類:
(1)系統工作:有些重要的工作必須周而 復始地執行。
(2)個別使用者工作:個別使用者可能希望執 行某些程式。
Android UI之五種基本佈局詳解
Android佈局是應用介面開發的重要一環,在Android中,共有五種佈局方式,分別是:
LinearLayout (線性佈局)
RelativeLayout(相對佈局)
FrameLayout(幀佈局)
TableLayout(表格佈局)GridLay
[js高手之路]原型對象(prototype)與原型鏈相關屬性與方法詳解
隱式 之前 username tar uname create pro getproto .get 一,instanceof:
instanceof檢測左側的__proto__原型鏈上,是否存在右側的prototype原型. 我在之前的兩篇文章
[js高手之路]構造函數的基
MyBatis之Mapper XML 文件詳解(五)-自動映射查詢結果
java mybatis 正如你在前面一節看到的,在簡單的場景下,MyBatis可以替你自動映射查詢結果。 如果遇到復雜的場景,你需要構建一個result map。 但是在本節你將看到,你也可以混合使用這兩種策略。 讓我們到深一點的層面上看看自動映射是怎樣工作的。 當自動映射查詢結果時,MyBatis
SpringMVC 框架系列之組件概述與配置詳解
align 概述 handle ont htm 配置文件 掃描器 springmvc 解析 在上一篇文章 SpringMVC 框架系列之初識與入門實例 的實例中,我們已經知道,SpringMVC 框架是一個 web 層的框架,本篇文章就詳細解釋一下 SpringMVC 框架
一看就懂!【英雄聯盟銳雯】與 Python 詳解設計模式之門面模式
【網路配圖】
設計模式(Design Pattern)是一套被反覆使用、多數人知曉的、經過分類的、程式碼設計經驗的總結。使用設計模式的目的:為了程式碼可重用性、讓程式碼更容易被他人理解、保證程式碼可靠性。設計模式使程式碼編寫真正工程化;設計模式是軟體工程的基石脈絡,如同大廈的結構一樣
android 繪圖之Path與Paint詳解[轉]
/** * Paint類介紹 * * Paint即畫筆,在繪圖過程中起到了極其重要的作用,畫筆主要儲存了顏色, * 樣式等繪製資訊,指定了如何繪製文字和圖形,畫筆物件有很多設定方法, * 大體上可以分為兩類,一類與圖形繪製相關,一類與文字繪製相關。 * * * 1.圖形繪製 * setARG
OSI七層與TCP/IP五層網路架構詳解
(1)OSI七層模型
OSI中的層 功能 TCP/IP協議族
應用層 檔案傳輸,電子郵件,檔案服務,虛擬終端 TFTP,HTTP,SNMP,FTP,SMTP,DNS,Telnet
表示層 資料格式化,程式碼轉換,資料加密 沒有協議
會話層 解除或建立與別的接點的聯絡 沒有協
netty原始碼分析之-SimpleChannelInboundHandler與ChannelInboundHandlerAdapter詳解(6)
每一個Handler都一定會處理出站或者入站(也可能兩者都處理)資料,例如對於入站的Handler可能會繼承SimpleChannelInboundHandler或者ChannelInboundHandlerAdapter,而SimpleChannelIn
常用註解之@PathVariable與@RequestParam詳解
@PathVariable和@RequestParam
首先說一下Request瞭解一下請求引數是什麼
在訪問各種各樣網站時,經常會發現網站的URL的最後一部分形如:?xxxx=yyyy&zzzz=wwww。這就是HTTP協議中的Request引數,它有什麼用呢?先來看一個例子:
RxJava observeOn()與subscribeOn()的關係
RxJava系列教程:
observeOn和subscribeOn都是對observable的一種操作,區別就是subscribeOn改變了observable本身產生事件的schedule以及發出事件後相關處理事件的程式所在的scheduler,而o
計網--OSI七層與TCP/IP五層網路架構詳解
OSI和TCP/IP是很基礎但又非常重要的網路基礎知識,理解得透徹對運維工程師來說非常有幫助。今天偶又複習了一下:
(1)OSI七層模型
OSI中的層 功能 TCP/IP協議族
應用層 檔案傳輸,電子郵件,檔案服務,虛擬終端 TFTP,HTTP,SNMP,FT
JavaWeb之檔案上傳與下載詳解
檔案上傳
檔案上傳概述
實現web開發中的檔案上傳功能,需完成如下二步操作:
(1)在web頁面中新增上傳輸入項;
(2)在servlet中讀取上傳檔案的資料,並儲存到本地硬碟中。
如何在web頁面中新增上傳輸入項?<input type
Android 開發的 RxJava 與RxAndroid詳解以及RXbus
這篇文章的目的有兩個: 1. 給對 RxJava 感興趣的人一些入門的指引 2. 給正在使用 RxJava 但仍然心存疑惑的人一些更深入的解析在正文開始之前的最後,放上 GitHub 連結和引入依賴的 gradle 程式碼: Github: https://github.co
Java io流 之FileOutputStream與FileInputStream 詳解
FileOutputStream
檔案輸出流
方法程式碼詳解:
public class Demo01 {
public static void main(String[] a