我的Android組件化實(shí)踐

編譯時(shí)間越來(lái)越長(zhǎng),時(shí)間=生命琼腔,我要救命。

項(xiàng)目框架

最開(kāi)始項(xiàng)目只有一個(gè)app踱葛,項(xiàng)目結(jié)構(gòu)很簡(jiǎn)單丹莲,就是一個(gè)業(yè)務(wù)module加上一個(gè)通用的基礎(chǔ)庫(kù)。


圖1

隨著業(yè)務(wù)的開(kāi)展尸诽,有了第二個(gè)第三個(gè)乃至第N個(gè)app甥材,項(xiàng)目結(jié)構(gòu)變成如下樣子。

圖2

不同app有公共的功能性含,于是增加一個(gè)業(yè)務(wù)基礎(chǔ)庫(kù)洲赵,將公用部分移到里面。

這個(gè)框架維持了很長(zhǎng)一段時(shí)間商蕴,隨著業(yè)務(wù)的快速發(fā)展叠萍,開(kāi)發(fā)人員的增加,代碼也越來(lái)越臃腫绪商,一些問(wèn)題開(kāi)始出現(xiàn)苛谷。正好由于多個(gè)app不利于推廣,項(xiàng)目開(kāi)始向云平臺(tái)的方向發(fā)展格郁,需要一個(gè)融合的平臺(tái)app腹殿,將原先的app作為業(yè)務(wù)模塊加入到平臺(tái)app中。

問(wèn)題在哪里

舊框架最大問(wèn)題是業(yè)務(wù)基礎(chǔ)庫(kù)在膨脹例书,任意兩個(gè)app需要用到的公用功能锣尉,只能將代碼移入業(yè)務(wù)基礎(chǔ)庫(kù)。長(zhǎng)時(shí)間后决采,這個(gè)庫(kù)無(wú)法看了自沧,里面什么都有,所有app都直接引用树瞭,耦合嚴(yán)重拇厢,簡(jiǎn)單歸納幾個(gè)問(wèn)題:

  • 業(yè)務(wù)基礎(chǔ)庫(kù)只會(huì)越來(lái)越大筏勒,功能簡(jiǎn)單地用文件夾區(qū)分,沒(méi)人可以完全掌握旺嬉;
  • 代碼只增不減,編譯時(shí)間越來(lái)越長(zhǎng)厨埋;
  • 修改一個(gè)功能邪媳,不得不測(cè)試調(diào)用到這個(gè)功能的每個(gè)app,測(cè)試成本高荡陷;
  • 多名開(kāi)發(fā)人員對(duì)業(yè)務(wù)基礎(chǔ)庫(kù)進(jìn)行修改雨效,帶來(lái)較多代碼沖突,溝通成本高废赞;
  • 直接引用代碼徽龟,缺少接口化和封裝,業(yè)務(wù)迭代不夠靈活唉地。

一句話据悔,整個(gè)工程要拆。

組件化實(shí)踐

今時(shí)今日搜到的就是組件化和插件化兩種耘沼,兩者的討論分析很多极颓,插件化最大的好處是具備動(dòng)態(tài)修改代碼的能力。如果不需要這種動(dòng)態(tài)功能群嗤,建議不要考慮插件化菠隆。官方不推薦的東西,沒(méi)必要蹚渾水狂秘,支持插件化的庫(kù)骇径,都是國(guó)產(chǎn)廠商。

今次組件化的改造者春,最大目標(biāo)是減少代碼間的依賴破衔,讓各業(yè)務(wù)模塊相對(duì)獨(dú)立,原有業(yè)務(wù)app的開(kāi)發(fā)人員可以更加專注于自己的部分钱烟,不需要次次全工程編譯运敢。

拆拆拆,最后拆成這個(gè)樣子:


圖3
  • 基礎(chǔ)庫(kù)是業(yè)務(wù)無(wú)關(guān)的忠售,可以應(yīng)用到任意項(xiàng)目里传惠;
  • 業(yè)務(wù)基礎(chǔ)庫(kù)有個(gè)基礎(chǔ)的base module,引用基礎(chǔ)庫(kù)稻扬。然后base module下卦方,根據(jù)功能劃分幾個(gè)功能module。下一層根據(jù)自身需要泰佳,選擇性包含盼砍;
  • 業(yè)務(wù)app層是組件化主要改進(jìn)的地方尘吗,后面分析;
  • 平臺(tái)app里只有一個(gè)mainapp module浇坐,這是一個(gè)殼工程睬捶,沒(méi)有任何業(yè)務(wù)代碼,是最終業(yè)務(wù)app集成的載體近刘。
1擒贸、application和library

能夠獨(dú)立運(yùn)行的app,module的屬性是application觉渴,在build.gradle定義為:

apply plugin: "com.android.application"

不能獨(dú)立運(yùn)行介劫,提供其他module依賴的叫l(wèi)ibrary,在build.gradle定義為:

apply plugin: 'com.android.library'

看回上面的結(jié)構(gòu)圖案淋,基礎(chǔ)庫(kù)和業(yè)務(wù)基礎(chǔ)庫(kù)自然都是library座韵,mainapp是application。對(duì)于業(yè)務(wù)app踢京,則要區(qū)分開(kāi)發(fā)階段和集成階段誉碴。在開(kāi)發(fā)階段,希望業(yè)務(wù)app可以單獨(dú)運(yùn)行瓣距;集成階段翔烁,希望業(yè)務(wù)app搖身一變,以library形式整合到平臺(tái)app旨涝。

開(kāi)發(fā)階段和集成階段的切換蹬屹,可以通過(guò)在gradle定義全局變量,提供給module讀取白华。我定義了一個(gè)config.gradle慨默,在根build.gradle引入:

apply from: "config.gradle"

config.gradle的用途是管理配置、版本號(hào)和依賴庫(kù)弧腥,避免散落到各個(gè)module中厦取,方便集中管理。

ext {
    buildBizApp = false  //是否構(gòu)建單獨(dú)的業(yè)務(wù)app

    compileSdkVersion = 24
    buildToolsVersion = "26.0.1"
    minSdkVersion = 15
    targetSdkVersion = 19
    versionCode = 1
    versionName = "1.0.0"

    dependencies = [
            supportV4                    : 'com.android.support:support-v4:24.2.1',
            appcompatV7                  : 'com.android.support:appcompat-v7:24.2.1',
            recyclerviewV7               : 'com.android.support:recyclerview-v7:24.2.1',
            design                       : 'com.android.support:design:24.2.1'
    ]
}

在業(yè)務(wù)app的build.gradle管搪,通過(guò)判斷變量buildBizApp虾攻,達(dá)到自由切換的目的。

if (rootProject.ext.buildBizApp) {
    apply plugin: "com.android.application"
} else {
    apply plugin: 'com.android.library'
}
2更鲁、photo module

下面以拍照和看圖的一個(gè)module為例子霎箍,它提供CameraActivity和PhotoActivity兩個(gè)Activity。這是很基礎(chǔ)的功能澡为,如果是組件化之前的框架漂坏,我會(huì)毫不猶豫將功能扔進(jìn)業(yè)務(wù)基礎(chǔ)庫(kù),因?yàn)樗衋pp都需要用到。

module之間的直接引用用起來(lái)很方便顶别,但不利于長(zhǎng)遠(yuǎn)代碼的維護(hù)谷徙,畢竟只靠著包名劃分功能,代碼邊界很容易破壞驯绎。最好的方法是編譯上的隔離完慧,調(diào)用者和photo module之間沒(méi)有直接引用。

利用application和library切換的方法剩失,我們可以達(dá)到如下效果屈尼。

圖4

對(duì)于photo module,除了CameraActivity和PhotoActivity赴叹,還添加了DebugMainActivity。當(dāng)photo module單獨(dú)編譯成photo app時(shí)指蚜,以DebugMainActivity為主頁(yè)乞巧。當(dāng)需要編譯mainapp時(shí),photo module作為library和其他module打包進(jìn)mainapp摊鸡,注意绽媒,這個(gè)時(shí)候DebugMainActivity沒(méi)有用了,只有橙色框部分需要免猾。紅色箭頭跳轉(zhuǎn)不能再使用顯式Intent是辕,可以用隱式Intent跳轉(zhuǎn),或者使用后面介紹的路由跳轉(zhuǎn)猎提。

好處顯而易見(jiàn)获三,模塊間完全解耦了。在開(kāi)發(fā)階段锨苏,可以單獨(dú)編譯photo app疙教,在DebugMainActivity中調(diào)試拍照和看圖,省掉編譯完整app和在app中點(diǎn)擊測(cè)試的時(shí)間伞租。

上面模式的實(shí)現(xiàn)贞谓,需要對(duì)配置進(jìn)行改造,接下來(lái)一步步來(lái)講解葵诈。

3裸弦、AndroidManifest合并

每一個(gè)module都有AndroidManifest.xml,很明顯作喘,當(dāng)module分別處于application和library時(shí)理疙,它需要的AndroidManifest.xml是不同的。

  • application:定義CameraActivity泞坦、PhotoActivity沪斟、DebugMainActivity,其中DebugMainActivity定義為啟動(dòng)頁(yè)。
  • library:只需要定義CameraActivity主之、PhotoActivity择吊,最終合并到mainapp的AndroidManifest.xml,描述了photo module提供了什么頁(yè)面槽奕。
sourceSets {
    main {
        if (rootProject.ext.buildBizApp) {
            manifest.srcFile 'src/main/debug/AndroidManifest.xml'
        } else {
            manifest.srcFile 'src/main/AndroidManifest.xml'
            //移除debug資源
            java {
                exclude 'debug/**'
            }
        }
    }
}

AndroidManifest.xml的內(nèi)容無(wú)什么特別几睛,就不貼了,然后配置build.gradle粤攒,區(qū)分開(kāi)發(fā)模式和集成模式對(duì)應(yīng)AndroidManifest.xml文件位置所森。對(duì)于類似DebugMainActivity正式發(fā)布不需要的測(cè)試文件,可以放入java/debug文件夾夯接,然后在集成模式排除焕济。

4、Application處理

類似于AndroidManifest.xml盔几,最終運(yùn)行時(shí)Application只有一個(gè)晴弃,Application類也要區(qū)分開(kāi)發(fā)模式和集成模式。

  • 開(kāi)發(fā)模式:photo module有自己的Application逊拍,就叫PhotoApplication上鞠,里面初始化第三方庫(kù)或者添加其他一些操作。對(duì)應(yīng)地PhotoApplication需要定義到debug/AndroidManifest.xml芯丧,PhotoApplication文件放進(jìn)java/debug中芍阎。
  • 集成模式:PhotoApplication是photo module單獨(dú)編譯時(shí)才有用的。集成后缨恒,需要在mainapp中定義MainApplication作為最終唯一的Application谴咸。

很容易想到,這個(gè)時(shí)候還需要一個(gè)BaseApplication骗露,作為PhotoApplication和MainApplication的父類寿冕,提供公有的初始化方法和全局Context的獲取。

5椒袍、路由跳轉(zhuǎn)

由于模塊的拆分驼唱,頁(yè)面間無(wú)法使用顯式Intent跳轉(zhuǎn)。隱式Intent可以用驹暑,但是書(shū)寫(xiě)比較麻煩玫恳,一些面向切面的功能難以實(shí)現(xiàn),所以我不用优俘。

我使用了支持路由功能的這個(gè)庫(kù)alibaba/ARouter京办。項(xiàng)目有詳細(xì)的文檔和demo,我就不復(fù)制粘貼了帆焕,下面說(shuō)說(shuō)我怎樣用惭婿。

首先為CameraActivity定義地址不恭,直接對(duì)class添加注解:

@Route(path = "/photo/activity/camera")

然后在需要調(diào)用的地方這樣寫(xiě):

Bundle bundle = new Bundle();
//set bundle

ARouter.getInstance()
       .build("/photo/activity/camera")
       .with(bundle)
       .navigation(activity, BizConstant.RequestCode.TAKE_PHOTO);

定義好路徑、參數(shù)和requestCode财饥,很簡(jiǎn)單地實(shí)現(xiàn)了一次路由跳轉(zhuǎn)换吧。

跳轉(zhuǎn)一定成功嗎?如果在開(kāi)發(fā)階段單獨(dú)編譯一個(gè)業(yè)務(wù)app钥星,photo module不存在沾瓦,前置登錄的login module也不存在,如下圖所示:

圖5

photo module不存在比較好辦谦炒,Arouter提供了一個(gè)Callback函數(shù):

public abstract class NavCallback implements NavigationCallback {
    public NavCallback() {
    }

    public void onFound(Postcard postcard) {
    }

    public void onLost(Postcard postcard) {
    }

    public abstract void onArrival(Postcard var1);

    public void onInterrupt(Postcard postcard) {
    }
}
  1. 跳轉(zhuǎn)失敗時(shí)贯莺,可以直接返回一些測(cè)試數(shù)據(jù),photo module應(yīng)該是他人維護(hù)的穩(wěn)定組件宁改,不需要在開(kāi)發(fā)階段浪費(fèi)點(diǎn)擊時(shí)間缕探。
  2. 如果需要測(cè)試調(diào)用photo module,只能啟用集成模式还蹲,不過(guò)可以手動(dòng)修改mainapp的配置爹耗,因?yàn)闃I(yè)務(wù)app層的module可以任意組合,只需要包括用到的秽誊,最大限度減少編譯時(shí)間鲸沮。

至于前置的login琳骡,那是必須得有锅论,要輸入賬號(hào)密碼好煩啊,而且login module在開(kāi)發(fā)階段我不想集成楣号,有什么辦法最易?

回想之前每個(gè)業(yè)務(wù)app層的module在開(kāi)發(fā)階段都有自己的Application,完全可以把模擬登陸過(guò)程放在里面炫狱。這是一個(gè)思路藻懒,寫(xiě)一次,受益幾個(gè)月视译。

6嬉荆、依賴注入

依賴注入大家應(yīng)該要很熟悉,這是一種很好的代碼解耦方式酷含,不了解的請(qǐng)自行學(xué)習(xí)鄙早。

Android有dagger這個(gè)出名的依賴注入框架,不過(guò)我沒(méi)有用椅亚,Arouter也帶了依賴注入功能限番,夠用了。

圖6

項(xiàng)目采用MVP模式呀舔,view和presenter是一一對(duì)應(yīng)弥虐,所以直接在Activity里new出Presenter對(duì)象,沒(méi)弄什么花樣。

Model層根據(jù)業(yè)務(wù)分為各種service霜瘪,比如TaskService珠插、UserService、SettingService粥庄,對(duì)外只暴露接口丧失。這個(gè)時(shí)候使用依賴注入就很合適,Presenter只需要持有service的引用惜互,實(shí)例由Arouter負(fù)責(zé)注入布讹。

類似Activity定義路徑,為service實(shí)現(xiàn)類定義注解:

@Route(path = "/test/service/task")
public class TaskServiceImpl implements TaskService {}

然后在Presenter训堆,用Autowired注解需要被注入的Service描验。例子里有兩種方式,一種是全局注入坑鱼,一種是單個(gè)注入膘流,根據(jù)實(shí)際情況使用。

@Autowired
TaskService taskService;
@Autowired
UserService userService;

public MyPresenter() {
    ARouter.getInstance().inject(this);
    //taskService = ARouter.getInstance().navigation(PollingTaskService.class);
    //userService = ARouter.getInstance().navigation(UserService.class);
}

上面無(wú)非是省略了new的過(guò)程鲁沥,下面再舉個(gè)復(fù)雜一點(diǎn)的例子呼股。

圖7

select user展示了user列表,提供選擇user的功能画恰,但是user列表的生成方法彭谁,只有對(duì)應(yīng)的caller知道。在caller和select user已經(jīng)組件化的情況下允扇,可以使用依賴注入簡(jiǎn)化代碼缠局。

首先,在它們倆共同的業(yè)務(wù)庫(kù)層定義一個(gè)接口BaseSelectUserService考润,里面有一個(gè)方法listUser()狭园。caller1和caller2分別實(shí)現(xiàn)BaseSelectUserService接口,完成各自listUser()的邏輯糊治。

BaseSelectUserService selectUserService = 
(BaseSelectUserService) ARouter.getInstance().build(servicePath).navigation();

最關(guān)鍵是為select user注入合適的SelectUserServiceImpl對(duì)象唱矛,其中servicePath是頁(yè)面跳轉(zhuǎn)傳遞過(guò)來(lái)的參數(shù),這樣就可以正確地調(diào)用到對(duì)應(yīng)caller的listUser()井辜。如果有第三個(gè)caller绎谦,完全不用管select user,只需要依葫蘆畫(huà)瓢實(shí)現(xiàn)BaseSelectUserService接口并傳遞service路徑抑胎。

7燥滑、數(shù)據(jù)庫(kù)

項(xiàng)目的數(shù)據(jù)庫(kù)使用sqlite,orm框架是greenDAO阿逃。組件化之前铭拧,各業(yè)務(wù)app維護(hù)自己的數(shù)據(jù)庫(kù)赃蛛,集成后就要考慮各數(shù)據(jù)庫(kù)之間的關(guān)系。

數(shù)據(jù)庫(kù)表分為兩類搀菩,一是公共的表呕臂,比如用戶表、資源表肪跋;二是各業(yè)務(wù)app自身的業(yè)務(wù)表歧蒋。組件化之后,有三種方向:

  1. 業(yè)務(wù)app依舊各自維護(hù)數(shù)據(jù)庫(kù)州既;
  2. 提取公共表到上層的業(yè)務(wù)基礎(chǔ)層谜洽,統(tǒng)一管理;
  3. 所有表放在業(yè)務(wù)基礎(chǔ)層吴叶。

第二種是在模塊劃分上是理想的阐虚,公共表在業(yè)務(wù)基礎(chǔ)層,業(yè)務(wù)表維護(hù)在各自業(yè)務(wù)app中蚌卤,合情合理实束,不過(guò)有攔路虎。greenDAO通過(guò)@Entity將對(duì)象定義為表逊彭,在編譯時(shí)咸灿,不同module中的表會(huì)分別生成DaoMaster和DaoSession,換言之侮叮,每個(gè)module都有一個(gè)數(shù)據(jù)庫(kù)避矢。跨數(shù)據(jù)庫(kù)的多表查詢難搞签赃,不用第二種了谷异。

第一種改動(dòng)最少分尸,但是由于公共表不是定義在業(yè)務(wù)基礎(chǔ)庫(kù)锦聊,所有公共表的邏輯都需要在業(yè)務(wù)app中實(shí)現(xiàn)一遍,我不能接受咯箩绍。

剩下第三種孔庭,雖然所有業(yè)務(wù)表需要定義在業(yè)務(wù)基礎(chǔ)庫(kù),感覺(jué)不太好材蛛,但也僅僅是表定義圆到,增刪改查的邏輯還是在業(yè)務(wù)app中,在不修改數(shù)據(jù)庫(kù)品種的情況下卑吭,不好也先用著芽淡。

8、資源沖突

多個(gè)module由多名開(kāi)發(fā)人員并行開(kāi)發(fā)豆赏,無(wú)可避免會(huì)出現(xiàn)資源名稱的重復(fù)挣菲。在最終合并到mainapp時(shí)富稻,肯定會(huì)出現(xiàn)沖突,最好的方法是為資源定義一個(gè)前綴白胀。例如photo module中所有資源都加個(gè)前綴photo_椭赋。

build.gradle可以增加一個(gè)配置,強(qiáng)制資源指定前綴或杠。

android{
       resourcePrefix "photo_"
}

這個(gè)配置只能限制xml文件里的資源哪怔,對(duì)于圖片資源,養(yǎng)成習(xí)慣添加吧向抢。

結(jié)束語(yǔ)

上面做了很多工作认境,但還是處于組件化的“初級(jí)階段”。組件服務(wù)暴露挟鸠、代碼徹底隔離元暴、組件生命周期、組件通信兄猩、組件測(cè)試等還有一大堆可以改進(jìn)的方向茉盏,后續(xù)會(huì)一一實(shí)踐。

多謝很多網(wǎng)上大神的努力和無(wú)私分享枢冤,獲益良多凯旋。遇到疑問(wèn)或者有更好的方法,歡迎交流油湖。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末纠永,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子核蘸,更是在濱河造成了極大的恐慌巍糯,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,544評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件客扎,死亡現(xiàn)場(chǎng)離奇詭異祟峦,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)徙鱼,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,430評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén)宅楞,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人袱吆,你說(shuō)我怎么就攤上這事厌衙。” “怎么了绞绒?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,764評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵婶希,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我蓬衡,道長(zhǎng)喻杈,這世上最難降的妖魔是什么拐揭? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,193評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮奕塑,結(jié)果婚禮上堂污,老公的妹妹穿的比我還像新娘。我一直安慰自己龄砰,他們只是感情好盟猖,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,216評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著换棚,像睡著了一般式镐。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上固蚤,一...
    開(kāi)封第一講書(shū)人閱讀 51,182評(píng)論 1 299
  • 那天娘汞,我揣著相機(jī)與錄音,去河邊找鬼夕玩。 笑死你弦,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的燎孟。 我是一名探鬼主播禽作,決...
    沈念sama閱讀 40,063評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼揩页!你這毒婦竟也來(lái)了旷偿?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 38,917評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤爆侣,失蹤者是張志新(化名)和其女友劉穎萍程,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體兔仰,經(jīng)...
    沈念sama閱讀 45,329評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡茫负,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,543評(píng)論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了斋陪。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片朽褪。...
    茶點(diǎn)故事閱讀 39,722評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡置吓,死狀恐怖无虚,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情衍锚,我是刑警寧澤友题,帶...
    沈念sama閱讀 35,425評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站戴质,受9級(jí)特大地震影響度宦,放射性物質(zhì)發(fā)生泄漏踢匣。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,019評(píng)論 3 326
  • 文/蒙蒙 一戈抄、第九天 我趴在偏房一處隱蔽的房頂上張望离唬。 院中可真熱鬧,春花似錦划鸽、人聲如沸输莺。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,671評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)嫂用。三九已至,卻和暖如春丈冬,著一層夾襖步出監(jiān)牢的瞬間嘱函,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,825評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工埂蕊, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留往弓,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,729評(píng)論 2 368
  • 正文 我出身青樓蓄氧,卻偏偏與公主長(zhǎng)得像亮航,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子匀们,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,614評(píng)論 2 353

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

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,077評(píng)論 25 707
  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理缴淋,服務(wù)發(fā)現(xiàn),斷路器泄朴,智...
    卡卡羅2017閱讀 134,652評(píng)論 18 139
  • 不怕跌倒重抖,所以飛翔 組件化開(kāi)發(fā) 參考資源 Android組件化方案 為什么要組件化開(kāi)發(fā) 解決問(wèn)題 實(shí)際業(yè)務(wù)變化非常...
    筆墨Android閱讀 2,982評(píng)論 0 0
  • 一 事情要從去年(或者是前年,我也記不清了)學(xué)《等離子體動(dòng)理學(xué)》朗道阻尼那部分說(shuō)起祖灰,王老師的課件中出現(xiàn)這么一個(gè)公式...
    lishucai閱讀 2,686評(píng)論 0 1
  • 任可和豆葵是兩個(gè)熱血青年钟沛。 他倆年齡不大,做事總是三分鐘熱度局扶,還常常想著為家里人分憂恨统,怎奈身無(wú)一技之長(zhǎng),因此總是吃...
    路索閱讀 504評(píng)論 0 1