Architecting Android with RxJava

最近西土,我抽出了幾個晚上的時間,把咖啡和啤酒變成了代碼與文字捞蚂。

引子

三個月以來妇押,我翻譯了一些關(guān)于RxJava的文章,說實(shí)話這些翻譯姓迅,真的搞得我很頭疼敲霍,那么現(xiàn)在是時候回來寫點(diǎn)什么了。

最近丁存,我在看兩本書肩杈,《Learning Reactive Programming with Java 8》《RxJava Essentials》解寝,不過扩然,沒關(guān)系,我已經(jīng)買到了電子版聋伦,我會在文章結(jié)尾附上網(wǎng)盤鏈接和密碼夫偶,但我還是希望你將文章繼續(xù)讀下去,因?yàn)槟鞘俏恼陆Y(jié)尾的事觉增。

其實(shí)關(guān)于RxJava的文章和消息遠(yuǎn)不止我們能了解到的兵拢,但又拜英語所賜,所以它看起來又沒那么多逾礁。好在说铃,國內(nèi)有許多優(yōu)秀的開發(fā)專家hi大頭鬼hi
BlackSwift嘹履,程序亦非猿腻扇,Drakeet扔物線植捎,流火楓林等等在為之做著貢獻(xiàn)衙解,以及簡直不能更優(yōu)秀的文章《給 Android 開發(fā)者的 RxJava 詳解》

但是焰枢,現(xiàn)在蚓峦,我不得不再次做啰嗦一下舌剂,RxJava究竟會改變我們什么。

響應(yīng)式編程Reactive Programming

什么是響應(yīng)式編程呢暑椰?在Java程序中:

int a = 4;
int b = 5;
int c = a + b;
System.out.println(c); // 9
a = 6;
System.out.println(c);
// 9 again, but if 'c' was tracking the changes of 'a' and 'b',
// it would've been 6 + 5 = 11

當(dāng)我們改變“a”和“b”的值時霍转,“c”并沒有改變。換句話說一汽,“a”和“b”的改變并沒有響應(yīng)到“c”避消。這就是響應(yīng)式:程序以流的形式,傳遞數(shù)據(jù)的改變召夹。

那我岩喷,我們又為什么需要響應(yīng)式呢?

以下翻譯自《Learning Reactive Programming with Java 8》

10-15年前监憎,對于網(wǎng)站開發(fā)來說纱意,最平常的日常工作就是進(jìn)行維護(hù)和縮短響應(yīng)時間,那么今天鲸阔,一切程序都應(yīng)該保證七天二十四小時不間斷運(yùn)行偷霉,并且能夠極快的做出響應(yīng);如果你的網(wǎng)站響應(yīng)慢或者宕機(jī)褐筛,那么用戶將會對你們真愛一秒變備胎类少,轉(zhuǎn)而選擇其他網(wǎng)站服務(wù)。當(dāng)今的慢意味著不可用甚至是有故障的渔扎。如今的互聯(lián)網(wǎng)是在和大數(shù)據(jù)打交道硫狞,所以我們需要快速的處理數(shù)據(jù)。

過去的幾年中HTTP錯誤已經(jīng)不是什么新鮮事了赞警,但是現(xiàn)在妓忍,我們不得不進(jìn)行容錯機(jī)制,還要提供用戶易讀以及合理的消息更新愧旦。

在過去,我們寫簡單的桌面應(yīng)用定罢,但如今我們寫能夠做出快速響應(yīng)的Web應(yīng)用笤虫。多數(shù)情況下,這些應(yīng)用要與大量的遠(yuǎn)程服務(wù)器進(jìn)行數(shù)據(jù)傳遞祖凫。

如果我們想讓自己的軟件保持競爭性琼蚯,就不得不實(shí)現(xiàn)這些新需求,所以惠况,換言之就是我們應(yīng)該這樣做:

  • 模塊的/動態(tài)的:用這種方式遭庶,我們就能夠擁有一個七天二十四小時的系統(tǒng)了,因?yàn)檫@些模塊能夠在不停止整個系統(tǒng)的情況下進(jìn)行脫機(jī)和聯(lián)機(jī)稠屠。另外峦睡,隨著系統(tǒng)的不斷龐大翎苫,還能幫助我們更好地組織應(yīng)用結(jié)構(gòu),同時還能管理底層代碼榨了。
  • 可擴(kuò)展的:用這種方式煎谍,我們就能夠處理大量的數(shù)據(jù)和用戶請求了。
  • 容錯性:用這種方式龙屉,能夠?yàn)橛脩籼峁┓€(wěn)定的系統(tǒng)呐粘。
  • 響應(yīng)式:這不僅意味著快速,還意味著可用性強(qiáng)转捕。

讓我們思考如何實(shí)現(xiàn)它:

  • 如果我們的系統(tǒng)是事件驅(qū)動型的作岖,那就把它模塊化。我們可以將系統(tǒng)分成多個彼此之間通過通知進(jìn)行交互的微服務(wù)/組件/模塊五芝。這樣痘儡,我們就能夠以通知為代表,響應(yīng)系統(tǒng)的數(shù)據(jù)流了与柑。
  • 可擴(kuò)展意味著能夠應(yīng)對日益增長的數(shù)據(jù)谤辜,在負(fù)載的情況下不會崩潰。
  • 對故障/錯誤做出及時的響應(yīng)价捧,能夠提高系統(tǒng)的容錯性丑念。
  • 響應(yīng)意味著對能夠?qū)τ脩舨僮骷皶r的做出反應(yīng)。

如果應(yīng)用是事件驅(qū)動型的结蟋,那么脯倚,它就能夠解耦成多個自包含組件。這能夠幫我們更好的實(shí)現(xiàn)擴(kuò)展性嵌屎,因?yàn)槲覀兛偸强梢栽诓煌5艋蛘叽驍嘞到y(tǒng)的情況下添加新組建或者移除舊組件推正。如果錯誤和故障傳遞給正確的組件,把它們當(dāng)做通知來處理并作出響應(yīng)宝惰,那么應(yīng)用能變得更具有容錯性和彈性植榕。所以,如果把系統(tǒng)構(gòu)建成事件驅(qū)動型的尼夺。我們可以更容易的實(shí)現(xiàn)擴(kuò)展性和容錯性尊残,而且一個具有擴(kuò)展性,低耦合和防錯的應(yīng)用能夠快速的響應(yīng)用戶操作淤堵。


Reactive Manifesto文檔定義了我們剛剛提到的四點(diǎn)響應(yīng)式準(zhǔn)則寝衫。每一個響應(yīng)式系統(tǒng)都應(yīng)該是消息驅(qū)動型(事件驅(qū)動型)的。這樣它不僅能變得低耦合拐邪,而且擴(kuò)展性和容錯性將更高慰毅,這就意味著它可靠和具有響應(yīng)式。

要注意的是扎阶,Reactive Manifesto只是描述了一個響應(yīng)式系統(tǒng)汹胃,并不是對響應(yīng)式編程的定義婶芭。當(dāng)然,你也可以不使用任何響應(yīng)式類庫或者語言统台,打造一款彈性可擴(kuò)展雕擂,具有消息驅(qū)動的響應(yīng)式應(yīng)用。

應(yīng)用程序中數(shù)據(jù)的變化贱勃,以通知的方式傳遞給正確的Handler井赌。所以,使用響應(yīng)式構(gòu)造應(yīng)用是符遵循Manifesto最簡單的方式贵扰。

回調(diào)地獄

如果你是一個能夠時刻保持頭腦清醒仇穗,邏輯清晰和思維縝密的人,是個Callback高手戚绕,善用并且能夠用好FutureTask纹坐。

那么在Android中你的代碼可能會頻繁的使用async+callbacks,或者service composition+ error handing 舞丛。

那么關(guān)于異步回調(diào)的邏輯耘子,你會寫成這樣getData(Callback<T>)、這樣Future<T> getData()球切,還是這樣Future<List<T>> getData()谷誓,甚至這樣Future<List<Future<T>>> getData(),嗷吨凑!拜托捍歪,我簡直不能再舉例下去了,這簡直就是Callback Hell鸵钝,這樣的程序或許寫起來很舒服糙臼,但是如何測試和維護(hù)呢。

如果哪天你的程序出了問題而必須馬上修復(fù)恩商,但你卻不能馬上趕來或者需要別人協(xié)助(這在很多公司是很常見的)变逃,或者當(dāng)他人在review你的代碼時,那么怠堪,是時候拿出這張圖了韧献。

然而使用RxJava的操作符,我們可以避免這些煩人甚至糟糕的回調(diào)研叫,讓結(jié)構(gòu)和思路看起來更清晰,通過組合API璧针,只需要約定最終的結(jié)果Observable<T>就行了嚷炉。

并且scheduler的出現(xiàn),不僅解放了線程的切換探橱,讓UI線程與工作線程間的跳轉(zhuǎn)變得簡單申屹,而且绘证,它的API很豐,也提供了很多使用場景的建議哗讥,比如嚷那,適用計算任務(wù)的Schedulers.computation(?);處理密集IO任務(wù)的Schedulers.io(?)杆煞;以及Schedulers.trampoline(?)能夠有效避免StackOverflowError魏宽,所以非常適合函數(shù)的遞歸調(diào)用。好了决乎,我不再舉例了队询,因?yàn)?a target="_blank" rel="nofollow">官方文檔已經(jīng)給出了很詳細(xì)的解釋了,但是值得一提的是构诚,如果使用Schedulers的工廠方法創(chuàng)建的Worker蚌斩,一旦任務(wù)執(zhí)行完畢,都應(yīng)該調(diào)用worker.unsubscribe( )方法范嘱,然后轉(zhuǎn)向之前定義的Scheduler實(shí)例上來送膳。

當(dāng)然RxJava的出現(xiàn)并不僅僅是為了解決回調(diào)地獄的。

這是我通過學(xué)習(xí)和不斷地練習(xí)丑蛤,一路走來很辛苦叠聋,總結(jié)的一些經(jīng)驗(yàn),分享給大家:

1 . error handling

2 . lifecycle changes

3 . caching (roation)

   @Override public void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setRetainInstance(true);

      /*.cache()操作符:
        當(dāng)?shù)谝粋€subscribe訂閱的時候盏阶,才會連接原始Observable晒奕,緩存事件,重發(fā)給后續(xù)訂閱的subscribe
        值得注意的事,它和使用了.replay()操作符的ConnectableObservable的不同名斟。
        另外脑慧,為了避免內(nèi)存開銷,不建議緩存大量事件*/
      cacheObservable = weatherManager.getWeather().cache();
    }

    @Override public void onViewCreated(View view, Bundle savedInstanceState) {
      super.onViewCreated(view, savedInstanceState);
      cacheObservable.subscribe(/*your subscribe*/);
    }

4 . composing multiple calls

5 . more robust interface than asyncTask

6 . easy to do complex threading

7 . functional nature is more expressive

    /*一個數(shù)組砰盐,每個元素乘以2闷袒,然后篩選小于10的元素,放入集合中*/
    Integer[] integers = { 0, 1, 2, 3, 4, 5 };

    /*一般寫法岩梳,看上去并不是那么的“函數(shù)”*/
    Integer[] doubles = new Integer[integers.length];
    for (int i = 0; i < integers.length; i++) {
      doubles[i] = integers[i] * 2;
    }
    List<Integer> integerList = new ArrayList<>(doubles.length);
    for (Integer integer : doubles) {
      if (integer < 10) integerList.add(integer);
    }


    /*Observable寫法囊骤,一切都好多了*/
    List<Integer> funactionalList = Observable.from(integers).map(new Func1<Integer, Integer>() {
      @Override public Integer call(Integer integer) {
        return integer * 2;
      }
    }).filter(new Func1<Integer, Boolean>() {
      @Override public Boolean call(Integer integer) {
        return integer < 10;
      }
    }).toList().toBlocking().first();

8 . async unit testing

9 . fluent API

10 . easy debugging

//值得一提的是,關(guān)于@RxLogSubscriber要放在繼承自Subscriber的類上
@RxLogSubscriber class MySubscriber extends Subscriber<Void> {
        @Override public void onCompleted() {
        }
        @Override public void onError(Throwable e) {
        }

        @Override public void onNext(Void aVoid) {
        }
      }

//而不是實(shí)現(xiàn)Observer接口的類上
@RxLogSubscriber class MySubscriber implements Observer<Void> {
        @Override public void onCompleted() {
        }
        @Override public void onError(Throwable e) {
        }

        @Override public void onNext(Void aVoid) {
        }
      }

當(dāng)然冀值,隨著學(xué)習(xí)的深入也物,你會發(fā)現(xiàn),收益不止如此列疗。

在響應(yīng)式編程中滑蚯,應(yīng)該牢記以下兩點(diǎn):

  • everything is a stream(一切皆流)

  • don't break the chain(不要打斷鏈?zhǔn)浇Y(jié)構(gòu))

談?wù)凚ackpressure

Android這種嵌入式系統(tǒng),尤其是生產(chǎn)者-消費(fèi)者(producer-consumer)模式中,一定要小心Backpressure(背壓告材,反壓)的出現(xiàn)坤次。一個寬泛的解釋就是:事件產(chǎn)生的速度比消費(fèi)快。一旦發(fā)生overproducing斥赋,當(dāng)你的鏈?zhǔn)浇Y(jié)構(gòu)不能承受數(shù)據(jù)壓力的時候缰猴,就會拋出MissingBackpressureException異常。

在Android中最容易出現(xiàn)的Backpressure就是連續(xù)快速點(diǎn)擊跳轉(zhuǎn)界面疤剑、數(shù)據(jù)庫查詢滑绒、文件掃面、鍵盤輸入骚露,甚至聯(lián)網(wǎng)等操作都有可能造成Backpressure蹬挤,可能有些情況并不會導(dǎo)致程序崩潰,但是會造成一些我們不想見到的小麻煩棘幸。那么一起來看看如何用RxJava解決Backpressure焰扳,OK,讓我們的程序變得健壯起來吧误续。

groupBy操作符

在寫這篇文章的時候吨悍,剛好看到一段代碼,看來有必要說一說這個操作符了蹋嵌。

.groupBy( )育瓜,分組操作符,雖然目前這個項(xiàng)目中沒有用到栽烂,但是我還是蠻喜歡它的躏仇,而且我看到很多人在使用,將原始Observable根據(jù)不同的key分組成多個GroupedObservable腺办,由原始Observable發(fā)射(原始Observable的泛型將變成這樣Observable<GroupedObservable<K, T>>)焰手,每一個GroupedObservable既是事件本身也是一個獨(dú)立的Observable,每一個GroupedObservable發(fā)射一組原始Observable的事件子集怀喉。

引用自:GroupBy中文翻譯

注意:groupBy將原始Observable分解為一個發(fā)射多個GroupedObservable的Observable书妻,一旦有訂閱,每個GroupedObservable就開始緩存數(shù)據(jù)躬拢。因此躲履,如果你忽略這些GroupedObservable中的任何一個,這個緩存可能形成一個潛在的內(nèi)存泄露聊闯。因此工猜,如果你不想觀察,也不要忽略GroupedObservable菱蔬。你應(yīng)該使用像take(0)這樣會丟棄自己的緩存的操作符域慷。
如果你取消訂閱一個GroupedObservable,那個Observable將會終止。如果之后原始的Observable又發(fā)射了一個與這個Observable的Key匹配的數(shù)據(jù)犹褒,groupBy將會為這個Key創(chuàng)建一個新的GroupedObservable。

那么問題恰恰出在.take(n)操作符上弛针。


只返回前面指定的n項(xiàng)數(shù)據(jù)叠骑,然后發(fā)送完成通知,忽略后面的事件削茁。

那么看一下這個例子:

Observable.just(0, 1, 2, 3, 4, 5).groupBy(new Func1<Integer, Boolean>() {
          @Override public Boolean call(Integer integer) {
            return integer % 2 == 0;
          }
        }).flatMap(new Func1<GroupedObservable<Boolean, Integer>, Observable<Integer>>() {
          @Override
          public Observable<Integer> call(GroupedObservable<Boolean, Integer> groupedObservable) {

            return groupedObservable.getKey() ? groupedObservable.take(1) : groupedObservable;
          }
        }).subscribe(new Action1<Integer>() {
          @Override public void call(Integer i) {
            System.out.println(i);
          }
        });

輸出結(jié)果:

0
1
2
3
4
5

然而在1.0.0-RC5之前的版本中宙枷,在GroupedObservable上使用.take(n)操作符將會在發(fā)送完n個事件后,對GroupedObservable進(jìn)行unsubscribe茧跋。并且GroupedObservable內(nèi)部將會記錄這個unsubscribed狀態(tài)慰丛,然后忽略后面的事件。所以輸出結(jié)果將是這樣的:

0
1
3
5

而在這之后的版本瘾杭,使用.take(n)操作符诅病,雖然也會發(fā)生unsubscribe,但是當(dāng)原始Observable再次發(fā)送一個滿足key的事件后粥烁,將會重新創(chuàng)建一個GroupedObservable贤笆,然后發(fā)送這個GroupedObservable,不會發(fā)生之前那樣的讨阻,忽略后續(xù)事件的現(xiàn)象芥永。

當(dāng)然,不要忘記钝吮,對不感興趣的GroupedObservable使用.take(0)埋涧,來避免泄露。

所以奇瘦,我的建議是棘催,在使用RxJava之前看看官方文檔或者change log。

關(guān)于RxWeather

我盡量減少對這個工程的文字描述链患。因?yàn)榇a才是最好的老師巧鸭。

通過對Android技術(shù)棧,1#架構(gòu)譯文)和Android架構(gòu)演化之路譯文)的解讀和學(xué)習(xí)麻捻,按照架構(gòu)和思路進(jìn)行了實(shí)現(xiàn)纲仍,并且加入了RxBus。

關(guān)于REST API贸毕,我選擇了和風(fēng)天氣郑叠,而放棄了Openweathermap的理由如下:

  1. Openweathermap免費(fèi)用戶所在的服務(wù)器不穩(wěn)定。

  2. 付費(fèi)方面明棍,和風(fēng)天氣更經(jīng)濟(jì)實(shí)惠乡革。

但是和風(fēng)天氣目前并不支持同時查詢多個地區(qū)的天氣預(yù)報,也不支持根據(jù)經(jīng)緯度查詢天氣預(yù)報。但是以后的事情誰又能說的準(zhǔn)呢沸版?

由于應(yīng)用并不支持動態(tài)的上拉加載嘁傀。所以,所有的列表展示結(jié)果视粮,取決于city.txt文件细办。

我從Openweathermap給出的資源(下載city.list.json)中,整理需要的城市Json字符串蕾殴,整合了經(jīng)緯度笑撞,以備不時之需。

找到了一個通過Location查詢所在地的API钓觉。

就這樣基本實(shí)現(xiàn)了列表展示頁ListActivity的功能:

  1. 根據(jù)Loaction查詢所在地城市名稱茴肥,然后查詢當(dāng)?shù)靥鞖狻?/p>

  2. 讀取domain->assets->city.txt,然后依次查詢每個城市的天氣荡灾,所以瓤狐,這個文件不建議放入太多json。

  3. 整合1和2的并發(fā)請求結(jié)果卧晓,顯示界面芬首。

詳情頁DetailActivity通過RxBus發(fā)送黏性事件接收列表頁傳遞過來的數(shù)據(jù),然后進(jìn)行展示逼裆。這里會有七天內(nèi)的天氣以及穿衣建議郁稍。由于我么并沒有找到一個正確的算法,所以當(dāng)進(jìn)入詳情頁后胜宇,旋轉(zhuǎn)屏幕之后的退出動畫會有所不同耀怜。這個類涉及的代碼大部分都是動畫(注意Hardware Layer的使用)以及對屏幕旋轉(zhuǎn)的處理,所以代碼看起有點(diǎn)多桐愉。ForkView使用了一個簡單的自定義Behavior财破。

搜索界面SearchActivity,輸入的關(guān)鍵字請不要以市、區(qū)結(jié)尾从诲,例如左痢,北京而不是北京市,因?yàn)锳PI不支持系洛,我也沒辦法 :( 俊性。

啟動頁

我認(rèn)為,出彩的引導(dǎo)頁是對細(xì)節(jié)的重視描扯,但是我實(shí)在不能忍受定页,在啟動頁等太久。注意:不要混淆這兩種場景绽诚。

所以典徊,我在看了正確使用啟動頁之后杭煎,決定采取這種方式實(shí)現(xiàn)SplashActivity。而且不建議使用大圖卒落,一個icon足以羡铲。

Code

所有代碼都可以從Github上獲得。

片尾Tips:

文章開頭提到的資料导绷,需要pdf或者kindle版本的請自行選擇下載犀勒,不得用于商業(yè)用途。

Learning Reactive Programming with Java 8 - pdf版妥曲,提取密碼:2d88。

Learning Reactive Programming with Java 8 - kindle版钦购,提取密碼:5nec檐盟。

RxJava Essentials - pdf版,提取密碼:z3r8押桃。

RxJava Essentials - kindle版葵萎,提取密碼:l67e。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末唱凯,一起剝皮案震驚了整個濱河市羡忘,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌磕昼,老刑警劉巖卷雕,帶你破解...
    沈念sama閱讀 206,126評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異票从,居然都是意外死亡漫雕,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評論 2 382
  • 文/潘曉璐 我一進(jìn)店門峰鄙,熙熙樓的掌柜王于貴愁眉苦臉地迎上來浸间,“玉大人,你說我怎么就攤上這事吟榴】猓” “怎么了?”我有些...
    開封第一講書人閱讀 152,445評論 0 341
  • 文/不壞的土叔 我叫張陵吩翻,是天一觀的道長兜看。 經(jīng)常有香客問我,道長仿野,這世上最難降的妖魔是什么铣减? 我笑而不...
    開封第一講書人閱讀 55,185評論 1 278
  • 正文 為了忘掉前任,我火速辦了婚禮脚作,結(jié)果婚禮上葫哗,老公的妹妹穿的比我還像新娘缔刹。我一直安慰自己,他們只是感情好劣针,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,178評論 5 371
  • 文/花漫 我一把揭開白布校镐。 她就那樣靜靜地躺著,像睡著了一般捺典。 火紅的嫁衣襯著肌膚如雪鸟廓。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 48,970評論 1 284
  • 那天襟己,我揣著相機(jī)與錄音引谜,去河邊找鬼。 笑死擎浴,一個胖子當(dāng)著我的面吹牛员咽,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播贮预,決...
    沈念sama閱讀 38,276評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼贝室,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了仿吞?” 一聲冷哼從身側(cè)響起滑频,我...
    開封第一講書人閱讀 36,927評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎唤冈,沒想到半個月后峡迷,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,400評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡务傲,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,883評論 2 323
  • 正文 我和宋清朗相戀三年凉当,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片售葡。...
    茶點(diǎn)故事閱讀 37,997評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡看杭,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出挟伙,到底是詐尸還是另有隱情楼雹,我是刑警寧澤,帶...
    沈念sama閱讀 33,646評論 4 322
  • 正文 年R本政府宣布尖阔,位于F島的核電站贮缅,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏介却。R本人自食惡果不足惜谴供,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,213評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望齿坷。 院中可真熱鬧桂肌,春花似錦数焊、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至谭跨,卻和暖如春干厚,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背螃宙。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評論 1 260
  • 我被黑心中介騙來泰國打工蛮瞄, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人谆扎。 一個月前我還...
    沈念sama閱讀 45,423評論 2 352
  • 正文 我出身青樓裕坊,卻偏偏與公主長得像,于是被迫代替她去往敵國和親燕酷。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,722評論 2 345

推薦閱讀更多精彩內(nèi)容

  • 本篇文章介主要紹RxJava中操作符是以函數(shù)作為基本單位周瞎,與響應(yīng)式編程作為結(jié)合使用的苗缩,對什么是操作、操作符都有哪些...
    嘎啦果安卓獸閱讀 2,837評論 0 10
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,498評論 25 707
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理声诸,服務(wù)發(fā)現(xiàn)酱讶,斷路器,智...
    卡卡羅2017閱讀 134,599評論 18 139
  • 前幾日薛之謙的演唱會罷,微博上炸了鍋慰照,人們都說他來了一出出其不意的浪漫灶挟。這場浪漫,既突兀又圓滿毒租。 什么叫浪漫爸上场?就...
    咖啡罐不見了閱讀 573評論 0 2
  • 作者:fin 1 系統(tǒng)相關(guān) useradd/userdel 添加用戶/刪除用戶 su 切換用戶命令 ls ...
    Autotestplat閱讀 416評論 2 9