美團(tuán)的多渠道分包方案walle及懶人工具

前言:

前陣子剛好做完多渠道分包需求,剛好記錄下沾凄。之前網(wǎng)上也看了很多資料梗醇,自己也收藏了很多,主要是方便下次自己再看撒蟀。但是遇到很不幸的事情:

  • image.png

這個(gè)是美團(tuán)多渠道分包方案的技術(shù)連接叙谨,當(dāng)再次進(jìn)去的時(shí)候就變成這樣子。沒辦法保屯,好文章就應(yīng)該轉(zhuǎn)載記錄手负,萬一以后不見了呢?

其實(shí)姑尺,看過美團(tuán)的分包方案的文章的小伙伴竟终,也應(yīng)該明白是經(jīng)過多次迭代和技術(shù)優(yōu)化的。


image.png

本來在這篇文章講得很詳細(xì)的切蟋,但是進(jìn)不去了统捶,進(jìn)不去了!不過沒關(guān)系柄粹,那個(gè)方案在安卓7.0已經(jīng)面臨要被淘汰的風(fēng)險(xiǎn)了瘾境。咱們也不多講。想看使用的話可以參考這位道友的文章:

美團(tuán)多渠道打包方案初體驗(yàn)

美團(tuán)多渠道分包方案原理

PS:該原理版權(quán)歸美團(tuán)所有镰惦,本文只做記錄,方便保存而已犬绒。

在Android 7.0(Nougat)推出了新的應(yīng)用簽名方案APK Signature Scheme v2后旺入,之前快速生成渠道包的方式(美團(tuán)Android自動(dòng)化之旅—生成渠道包)已經(jīng)行不通了,在此應(yīng)用簽名方案下如何快速生成渠道包呢凯力?

本文會(huì)對(duì)新的應(yīng)用簽名方案APK Signature Scheme v2以及新一代渠道生成工具進(jìn)行詳細(xì)深入的介紹茵瘾。

新的應(yīng)用簽名方案APK Signature Scheme v2

Android 7.0(Nougat)引入一項(xiàng)新的應(yīng)用簽名方案APK Signature Scheme v2,它是一個(gè)對(duì)全文件進(jìn)行簽名的方案咐鹤,能提供更快的應(yīng)用安裝時(shí)間拗秘、對(duì)未授權(quán)APK文件的更改提供更多保護(hù),在默認(rèn)情況下祈惶,Android Gradle 2.2.0插件會(huì)使用APK Signature Scheme v2和傳統(tǒng)簽名方案來簽署你的應(yīng)用雕旨。

下面以 新的應(yīng)用簽名方案 來指APK Signature Scheme v2扮匠。

目前該方案不是強(qiáng)制性的,在 build.gradle 添加 v2SigningEnabled false 凡涩,就能使用傳統(tǒng)簽名方案來簽署我們的應(yīng)用(見下面的代碼片段)棒搜。

  android {
    ...
    defaultConfig { ... }
    signingConfigs {
      release {
        storeFile file("myreleasekey.keystore")
        storePassword "password"
        keyAlias "MyReleaseKey"
        keyPassword "password"
        v2SigningEnabled false
      }
    }
  }

但新的應(yīng)用簽名方案有著良好的向后兼容性,能完全兼容低于Android 7.0(Nougat)的版本活箕。對(duì)比舊簽名方案力麸,它有更快的驗(yàn)證速度和更安全的保護(hù),因此新的應(yīng)用簽名方案可能會(huì)被采納成一個(gè)強(qiáng)制配置育韩,筆者認(rèn)為現(xiàn)在有必要對(duì)現(xiàn)有的渠道包生成方式進(jìn)行檢查克蚂、升級(jí)或改造來支持新的應(yīng)用簽名方案。

新的簽名方案對(duì)已有的渠道生成方案有什么影響呢筋讨?下圖是新的應(yīng)用簽名方案和舊的簽名方案的一個(gè)對(duì)比:

apk-before-after-signing.png

新的簽名方案會(huì)在ZIP文件格式的 Central Directory 區(qū)塊所在文件位置的前面添加一個(gè)APK Signing Block區(qū)塊埃叭,下面按照ZIP文件的格式來分析新應(yīng)用簽名方案簽名后的APK包。

整個(gè)APK(ZIP文件格式)會(huì)被分為以下四個(gè)區(qū)塊:

  1. Contents of ZIP entries(from offset 0 until the start of APK Signing Block)
  2. APK Signing Block
  3. ZIP Central Directory
  4. ZIP End of Central Directory
apk-sections.png

新應(yīng)用簽名方案的簽名信息會(huì)被保存在區(qū)塊2(APK Signing Block)中版仔, 而區(qū)塊1(Contents of ZIP entries)游盲、區(qū)塊3(ZIP Central Directory)、區(qū)塊4(ZIP End of Central Directory)是受保護(hù)的蛮粮,在簽名后任何對(duì)區(qū)塊1益缎、3、4的修改都逃不過新的應(yīng)用簽名方案的檢查然想。

之前的渠道包生成方案是通過在META-INF目錄下添加空文件莺奔,用空文件的名稱來作為渠道的唯一標(biāo)識(shí),之前在META-INF下添加文件是不需要重新簽名應(yīng)用的变泄,這樣會(huì)節(jié)省不少打包的時(shí)間令哟,從而提高打渠道包的速度。但在新的應(yīng)用簽名方案下META-INF已經(jīng)被列入了保護(hù)區(qū)了妨蛹,向META-INF添加空文件的方案會(huì)對(duì)區(qū)塊1屏富、3、4都會(huì)有影響蛙卤,新應(yīng)用簽名方案簽署的應(yīng)用經(jīng)過我們舊的生成渠道包方案處理后狠半,在安裝時(shí)會(huì)報(bào)以下錯(cuò)誤:

Failure [INSTALL_PARSE_FAILED_NO_CERTIFICATES: 
Failed to collect certificates from base.apk: META-INF/CERT.SF indicates base.apk is signed using APK Signature Scheme v2, 
but no such signature was found. Signature stripped?]

目前另外一種比較流行的渠道包快速生成方案(往APK中添加ZIP Comment)也因?yàn)樯鲜鲈颍瑹o法在新的應(yīng)用簽名方案下進(jìn)行正常工作颤难。

如果新的應(yīng)用簽名方案后續(xù)改成強(qiáng)制要求神年,那我們現(xiàn)有的生成渠道包的方式就會(huì)無法工作,那我們難道要退回到解放前行嗤,通過傳統(tǒng)的方式(例如:使用APKTool逆向工具已日、采用Flavor + BuildType等比較耗時(shí)的方案來進(jìn)行渠道包打包)來生成支持新應(yīng)用簽名方案的渠道包嗎?

如果只有少量渠道包的場(chǎng)景下栅屏,這種耗時(shí)時(shí)長還能夠勉強(qiáng)接受飘千。但是目前我們有將近900個(gè)渠道堂鲜,如果采用傳統(tǒng)方式打完所有的渠道包需要近3個(gè)小時(shí),這是不能接受的占婉。

那我們有沒有其他更好的渠道包生成方式泡嘴,既能支持新的應(yīng)用簽名方案,又能體驗(yàn)毫秒級(jí)的打包耗時(shí)呢逆济?我們來分析一下新方案中的區(qū)塊2——Block酌予。

可擴(kuò)展的APK Signature Scheme v2 Block

通過上面的描述,可以看出因?yàn)锳PK包的區(qū)塊1奖慌、3抛虫、4都是受保護(hù)的,任何修改在簽名后對(duì)它們的修改简僧,都會(huì)在安裝過程中被簽名校驗(yàn)檢測(cè)失敗建椰,而區(qū)塊2(APK Signing Block)是不受簽名校驗(yàn)規(guī)則保護(hù)的,那是否可以在這個(gè)不受簽名保護(hù)的區(qū)塊2(APK Signing Block)上做文章呢岛马?我們先來看看對(duì)區(qū)塊2格式的描述:

偏移 字節(jié)數(shù) 描述
@+0 8 這個(gè)Block的長度(本字段的長度不計(jì)算在內(nèi))
@+8 n 一組ID-value
@-24 8 這個(gè)Block的長度(和第一個(gè)字段一樣值)
@-16 16 魔數(shù) “APK Sig Block 42”

區(qū)塊2中APK Signing Block是由這幾部分組成:2個(gè)用來標(biāo)示這個(gè)區(qū)塊長度的8字節(jié) + 這個(gè)區(qū)塊的魔數(shù)(APK Sig Block 42)+ 這個(gè)區(qū)塊所承載的數(shù)據(jù)(ID-value)棉姐。

我們重點(diǎn)來看一下這個(gè)ID-value,它由一個(gè)8字節(jié)的長度標(biāo)示+4字節(jié)的ID+它的負(fù)載組成啦逆。V2的簽名信息是以ID(0x7109871a)的ID-value來保存在這個(gè)區(qū)塊中伞矩,不知大家有沒有注意這是一組ID-value,也就是說它是可以有若干個(gè)這樣的ID-value來組成夏志,那我們是不是可以在這里做一些文章呢乃坤?

為了驗(yàn)證我們的想法,先來看看新的應(yīng)用簽名方案是怎么驗(yàn)證簽名信息的沟蔑,見下圖:

apk-validation-process.png

通過上圖可以看出新的應(yīng)用簽名方案的驗(yàn)證過程:

  1. 尋找APK Signing Block湿诊,如果能夠找到,則進(jìn)行驗(yàn)證瘦材,驗(yàn)證成功則繼續(xù)進(jìn)行安裝厅须,如果失敗了則終止安裝
  2. 如果未找到APK Signing Block,則執(zhí)行原來的簽名驗(yàn)證機(jī)制食棕,也是驗(yàn)證成功則繼續(xù)進(jìn)行安裝九杂,如果失敗了則終止安裝

那Android應(yīng)用在安裝時(shí)新的應(yīng)用簽名方案是怎么進(jìn)行校驗(yàn)的呢?筆者通過翻閱Android相關(guān)部分的源碼宣蠕,發(fā)現(xiàn)下面代碼段是用來處理上面所說的ID-value的:

    public static ByteBuffer findApkSignatureSchemeV2Block(
            ByteBuffer apkSigningBlock,
            Result result) throws SignatureNotFoundException {
        checkByteOrderLittleEndian(apkSigningBlock);
        // FORMAT:
        // OFFSET       DATA TYPE  DESCRIPTION
        // * @+0  bytes uint64:    size in bytes (excluding this field)
        // * @+8  bytes pairs
        // * @-24 bytes uint64:    size in bytes (same as the one above)
        // * @-16 bytes uint128:   magic
        ByteBuffer pairs = sliceFromTo(apkSigningBlock, 8, apkSigningBlock.capacity() - 24);

        int entryCount = 0;
        while (pairs.hasRemaining()) {
            entryCount++;
            if (pairs.remaining() < 8) {
                throw new SignatureNotFoundException(
                        "Insufficient data to read size of APK Signing Block entry #" + entryCount);
            }
            long lenLong = pairs.getLong();
            if ((lenLong < 4) || (lenLong > Integer.MAX_VALUE)) {
                throw new SignatureNotFoundException(
                        "APK Signing Block entry #" + entryCount
                                + " size out of range: " + lenLong);
            }
            int len = (int) lenLong;
            int nextEntryPos = pairs.position() + len;
            if (len > pairs.remaining()) {
                throw new SignatureNotFoundException(
                        "APK Signing Block entry #" + entryCount + " size out of range: " + len
                                + ", available: " + pairs.remaining());
            }
            int id = pairs.getInt();
            if (id == APK_SIGNATURE_SCHEME_V2_BLOCK_ID) {
                return getByteBuffer(pairs, len - 4);
            }
            result.addWarning(Issue.APK_SIG_BLOCK_UNKNOWN_ENTRY_ID, id);
            pairs.position(nextEntryPos);
        }

        throw new SignatureNotFoundException(
                "No APK Signature Scheme v2 block in APK Signing Block");
    }

上述代碼中關(guān)鍵的一個(gè)位置是 if (id == APK_SIGNATURE_SCHEME_V2_BLOCK_ID) {return getByteBuffer(pairs, len - 4);},通過源代碼可以看出Android是通過查找ID為 APK_SIGNATURE_SCHEME_V2_BLOCK_ID = 0x7109871a 的ID-value甥捺,來獲取APK Signature Scheme v2 Block抢蚀,對(duì)這個(gè)區(qū)塊中其他的ID-value選擇了忽略。

APK Signature Scheme v2中沒有看到對(duì)無法識(shí)別的ID镰禾,有相關(guān)處理的介紹皿曲。

當(dāng)看到這里時(shí)唱逢,我們可不可以設(shè)想一下,提供一個(gè)自定義的ID-value并寫入該區(qū)域屋休,從而為快速生成渠道包服務(wù)呢坞古?

怎么向ID-value中添加信息呢?通過閱讀ZIP的文件格式和APK Signing Block格式的描述劫樟,筆者通過編寫下面的代碼片段進(jìn)行驗(yàn)證痪枫,發(fā)現(xiàn)通過在已經(jīng)被新的應(yīng)用簽名方案簽名后的APK中添加自定義的ID-value,是不需要再次經(jīng)過簽名就能安裝的叠艳,下面是部分代碼片段奶陈。

  public void writeApkSigningBlock(DataOutput dataOutput) {
        long length = 24;
        for (int index = 0; index < payloads.size(); ++index) {
            ApkSigningPayload payload = payloads.get(index);
            byte[] bytes = payload.getByteBuffer();
            length += 12 + bytes.length;
        }

        ByteBuffer byteBuffer = ByteBuffer.allocate(Long.BYTES);
        byteBuffer.order(ByteOrder.LITTLE_ENDIAN);
        byteBuffer.putLong(length);
        dataOutput.write(byteBuffer.array());

        for (int index = 0; index < payloads.size(); ++index) {
            ApkSigningPayload payload = payloads.get(index);
            byte[] bytes = payload.getByteBuffer();

            byteBuffer = ByteBuffer.allocate(Integer.BYTES);
            byteBuffer.order(ByteOrder.LITTLE_ENDIAN);
            byteBuffer.putInt(payload.getId());
            dataOutput.write(byteBuffer.array());

            dataOutput.write(bytes);
        }
        ...
    }

新一代渠道包生成工具

到這里為止一個(gè)新的渠道包生成方案逐步清晰了起來,下面是新一代渠道包生成工具的描述:

  1. 對(duì)新的應(yīng)用簽名方案生成的APK包中的ID-value進(jìn)行擴(kuò)展附较,提供自定義ID-value(渠道信息)吃粒,并保存在APK中
  2. 而APK在安裝過程中進(jìn)行的簽名校驗(yàn),是忽略我們添加的這個(gè)ID-value的拒课,這樣就能正常安裝了
  3. 在App運(yùn)行階段徐勃,可以通過ZIP的EOCD(End of central directory)Central directory等結(jié)構(gòu)中的信息(會(huì)涉及ZIP格式的相關(guān)知識(shí)早像,這里不做展開描述)找到我們自己添加的ID-value僻肖,從而實(shí)現(xiàn)獲取渠道信息的功能

新一代渠道包生成工具完全是基于ZIP文件格式和APK Signing Block存儲(chǔ)格式而構(gòu)建,基于文件的二進(jìn)制流進(jìn)行處理扎酷,有著良好的處理速度和兼容性檐涝,能夠滿足不同的語言編寫的要求,目前筆者采用的是Java+Groovy開發(fā)法挨, 該工具主要有四部分組成:

  1. 用于寫入ID-value信息的Java類庫
  2. Gradle構(gòu)建插件用來和Android的打包流程進(jìn)行結(jié)合
  3. 用于讀取ID-value信息的Java類庫
  4. 用于供com.android.application使用的讀取渠道信息的AAR

這樣谁榜,每打一個(gè)渠道包只需復(fù)制一個(gè)APK,然后在APK中添加一個(gè)ID-value即可凡纳,這種打包方式速度非城灾玻快,對(duì)一個(gè)30M大小的APK包只需要100多毫秒(包含文件復(fù)制時(shí)間)就能生成一個(gè)渠道包荐糜,而在運(yùn)行時(shí)獲取渠道信息只需要大約幾毫秒的時(shí)間巷怜。

這個(gè)項(xiàng)目我們?nèi)∶麨閃alle(瓦力),已經(jīng)開源暴氏,項(xiàng)目的Github地址是: https://github.com/Meituan-Dianping/walle (求Issue延塑、PR、Star)答渔。希望業(yè)內(nèi)有類似需求的團(tuán)隊(duì)能夠在APK Signature Scheme V2簽名下愉快地生成渠道包关带,同時(shí)也期待大家一起對(duì)該項(xiàng)目進(jìn)行完善和優(yōu)化。

總結(jié)

以上就是我們對(duì)新的應(yīng)用簽名方案進(jìn)行的分析沼撕,并根據(jù)它所帶來的文件存儲(chǔ)格式上的變化宋雏,找到了可以利用的ID-value芜飘,然后基于這個(gè)ID-value來構(gòu)建我們新一代渠道包生成工具。

新一代渠道包生成工具能夠滿足新應(yīng)用簽名方案對(duì)安全性的要求磨总,同時(shí)也能滿足對(duì)渠道包打包時(shí)間的要求嗦明,至此大家生成渠道包的方式需要升級(jí)了!

懶人工具

哈哈蚪燕,根據(jù)開源項(xiàng)目walle娶牌,在項(xiàng)目中實(shí)際應(yīng)用是通過命令行的方式來實(shí)現(xiàn)。在代碼中只是將walle的讀取channel的代碼集成到項(xiàng)目中了而已邻薯,寫入部分是通過命令行的方式寫入裙戏。既減少對(duì)三方庫的依賴,也方便不是開發(fā)人員同事使用厕诡。在下做了個(gè)小小的懶人工具累榜,方便自己偷懶。相關(guān)原理是批命令處理灵嫌,可自行百度查閱資料壹罚。

image.png

關(guān)于walle命令行的使用可以參考這里:
walle/walle-cli/README.md

懶人工具原理:批命令處理

1、獲取需分包的包體寿羞,同時(shí)截取文件名

rem 輸入需要分包的apk文件
echo 請(qǐng)將apk文件拖拽進(jìn)來:
@set /p apkFile=
echo\

rem 截取apk文件名
for /f "delims=" %%i in ("dir /b %apkFile%") do (
set apkFileName=%%~ni
)
echo apk文件名稱:%apkFileName%
echo\

2猖凛、獲取簽名文件信息

rem 獲取簽名文件信息
@CALL %currentWorkPath%sign\readconfig keyAlias keyAliasValue
@CALL %currentWorkPath%sign\readconfig storeFile storeFileValue
@CALL %currentWorkPath%sign\readconfig storePassword storePasswordValue
@CALL %currentWorkPath%sign\readconfig keyPassword keyPasswordValue
echo\

3、執(zhí)行包體的簽名(使用V2簽名)

rem apk執(zhí)行簽名(使用V2簽名)
@call java -jar %currentWorkPath%lib\apksigner.jar sign --ks %currentWorkPath%sign%storeFileValue% --ks-key-alias %keyAliasValue% --ks-pass pass:%storePasswordValue% --key-pass pass:%keyPasswordValue% --out %currentWorkPath%apkoutput%apkFileName%_sign.apk %apkFile%
if ERRORLEVEL 0 (
echo 簽名成功,簽名包體路徑:
echo %currentWorkPath%apkoutput%apkFileName%_sign.apk
)
echo\

4绪穆、檢測(cè)當(dāng)前包體的簽名方式

rem 檢測(cè)apk包體的簽名方式
@call java -jar %currentWorkPath%lib\apksigner.jar verify -v %currentWorkPath%apkoutput%apkFileName%_sign.apk
echo\

5辨泳、美團(tuán)渠道分包命令

rem 美團(tuán)渠道分包
@call java -jar %currentWorkPath%lib\walle-cli-all.jar batch -f %currentWorkPath%channel %currentWorkPath%apkoutput%apkFileName%_sign.apk %currentWorkPath%apkoutput\apks

PS:有優(yōu)化的地方可自行優(yōu)化。

使用條件

1玖院、適用于windows系統(tǒng)
2菠红、需安裝和配置Java環(huán)境(自行百度安裝)

使用說明

1、在channel中配置需打包的渠道id

示例說明:

meituan # 美團(tuán)
samsungapps #三星
hiapk
anzhi
xiaomi # 小米
91com

2难菌、在sign目錄中配置簽名文件信息:

在config.ini文件配置應(yīng)用簽名,同時(shí)將簽名文件放到該目錄下
注意:config.ini配置簽名信息時(shí)试溯,第一行留一行空白

示例說明:


keyAlias=xxx
keyPassword=xxx
storeFile=xxx
storePassword=xxx

3、雙擊運(yùn)行start.bat后郊酒,拖入需簽名包體apk即可

效果圖:


image.png
image.png

項(xiàng)目地址:
BuildChannelApkTool

后記

1遇绞、關(guān)于jdk版本問題:
V2方式的簽名是android7.0后才出來的,所以只有版本>25的SDK\build-tools\中才能找到對(duì)應(yīng)的apksigner.jar燎窘。 但是實(shí)際應(yīng)用中發(fā)現(xiàn)摹闽,這個(gè)版本的jar要求jdk1.8的版本,安裝jdk1.7會(huì)報(bào)錯(cuò)褐健。


QQ截圖20180814000316.png

解決方式:java環(huán)境安裝為jdk1.8的就好了钩骇。你說它咋就好了呢。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市倘屹,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌慢叨,老刑警劉巖纽匙,帶你破解...
    沈念sama閱讀 207,113評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異拍谐,居然都是意外死亡烛缔,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門轩拨,熙熙樓的掌柜王于貴愁眉苦臉地迎上來践瓷,“玉大人,你說我怎么就攤上這事亡蓉≡未洌” “怎么了?”我有些...
    開封第一講書人閱讀 153,340評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵砍濒,是天一觀的道長淋肾。 經(jīng)常有香客問我,道長爸邢,這世上最難降的妖魔是什么樊卓? 我笑而不...
    開封第一講書人閱讀 55,449評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮杠河,結(jié)果婚禮上碌尔,老公的妹妹穿的比我還像新娘。我一直安慰自己券敌,他們只是感情好唾戚,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,445評(píng)論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著陪白,像睡著了一般颈走。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上咱士,一...
    開封第一講書人閱讀 49,166評(píng)論 1 284
  • 那天立由,我揣著相機(jī)與錄音,去河邊找鬼序厉。 笑死锐膜,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的弛房。 我是一名探鬼主播道盏,決...
    沈念sama閱讀 38,442評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了荷逞?” 一聲冷哼從身側(cè)響起媒咳,我...
    開封第一講書人閱讀 37,105評(píng)論 0 261
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎种远,沒想到半個(gè)月后涩澡,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,601評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡坠敷,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,066評(píng)論 2 325
  • 正文 我和宋清朗相戀三年妙同,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片膝迎。...
    茶點(diǎn)故事閱讀 38,161評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡粥帚,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出限次,到底是詐尸還是另有隱情芒涡,我是刑警寧澤,帶...
    沈念sama閱讀 33,792評(píng)論 4 323
  • 正文 年R本政府宣布掂恕,位于F島的核電站拖陆,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏懊亡。R本人自食惡果不足惜依啰,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,351評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望店枣。 院中可真熱鬧速警,春花似錦、人聲如沸鸯两。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽疚脐。三九已至,卻和暖如春家夺,著一層夾襖步出監(jiān)牢的瞬間钝侠,已是汗流浹背该园。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評(píng)論 1 261
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留帅韧,地道東北人里初。 一個(gè)月前我還...
    沈念sama閱讀 45,618評(píng)論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像忽舟,于是被迫代替她去往敵國和親双妨。 傳聞我的和親對(duì)象是個(gè)殘疾皇子淮阐,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,916評(píng)論 2 344

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

  • 背景:目前項(xiàng)目在打渠道包的時(shí)候,采用的是AndroidManifest.xml配置渠道號(hào)刁品,上線前一個(gè)個(gè)構(gòu)建出來泣特,全...
    karlsu閱讀 1,127評(píng)論 0 6
  • 關(guān)于作者: 李濤,騰訊Android工程師挑随,14年加入騰訊SNG增值產(chǎn)品部群扶,期間主要負(fù)責(zé)手Q動(dòng)漫、企鵝電競(jìng)等項(xiàng)目的...
    稻草人_3e17閱讀 3,595評(píng)論 0 10
  • 筆者現(xiàn)在在負(fù)責(zé)一個(gè)新的Android項(xiàng)目镀裤,前期功能不太復(fù)雜,安裝包的體積小缴饭,渠道要求也較少暑劝,所以打渠道包使用And...
    WinDin閱讀 1,492評(píng)論 0 3
  • 《把握你的職業(yè)發(fā)展方向》,262頁 識(shí)別颗搂、列舉自己的技能担猛,并整合起來向?qū)Ψ秸故荆@是一回事丢氢。證明自己擁有這些技能是...
    穎子_11d6閱讀 316評(píng)論 0 0
  • 昨天參加百日共修3班的月度總結(jié)會(huì)傅联,前一天聽張梅說接龍報(bào)名的沒幾個(gè)人參加,頓時(shí)心里有點(diǎn)小小的失落感疚察,為什么有...
    鄢瑜含閱讀 227評(píng)論 0 4