Android Dex分包

最近項(xiàng)目apk方法數(shù)即將達(dá)到65536上限焙贷,雖然通過瘦身減少了一些方法數(shù),但是隨著更多sdk的接入,終究還是避免不了方法數(shù)突破限制斗幼,所以開始著手dex分包的工作。

之所以存在方法數(shù)不能超過65536的限制主要有兩個(gè)原因
1.dex文件格式的限制:dex文件中的方法個(gè)數(shù)使用short類型來存儲(chǔ)的抚垄,即2個(gè)字節(jié)蜕窿,最大值為65536,即單個(gè)dex文件的方法數(shù)上限為65536
2.系統(tǒng)對(duì)dex文件進(jìn)行優(yōu)化操作時(shí)分配的緩沖區(qū)大小的限制:在android2.x的系統(tǒng)上緩沖區(qū)只有5MB呆馁,android4.x為8MB或者16MB渠羞,如果方法數(shù)量超過緩沖區(qū)的大小時(shí),會(huì)造成dexopt崩潰

所以我們一般apk的方法數(shù)要控制在65536以內(nèi)智哀,如果超出次询,就要考慮dex分包。

簡(jiǎn)單案例

首先新建一個(gè)簡(jiǎn)單的工程


src.png

MainActivity中有一個(gè)button瓷叫,點(diǎn)擊后跳轉(zhuǎn)到OtherActivity屯吊,MainActivity中調(diào)用了HelperOne的方法

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    Button button = (Button)findViewById(R.id.btn);
    button.setOnClickListener(new OnClickListener() {

        @Override
        public void onClick(View arg0) {

            Intent intent = new Intent(MainActivity.this, OtherActivity.class);
            startActivity(intent);
        }
    });

    HelperOne helperOne = new HelperOne();
    helperOne.test();
}

OtherActivity也非常簡(jiǎn)單送巡,初始化了HelperTwo對(duì)象并調(diào)用其方法,而HelperTwo中又調(diào)用了HelperThree類的方法

下面先采用ant的方式來對(duì)該工程進(jìn)行多dex打包

dex分包——ant方式

ant腳本執(zhí)行順序

init-> clean-> dirs-> resource-src-> aidl-> compile -> dex-> package-res -> package-> copy_dex -> add-subdex-to-apk -> jarsigner ->zipalign -> debug

整個(gè)流程簡(jiǎn)單概括如下:

  1. init盒卸、clean骗爆、dirs 清理并創(chuàng)建輸出目錄
  2. resource-src 根據(jù)資源文件、manifest文件生成R.java
  3. aidl 對(duì)aidl文件進(jìn)行處理生成對(duì)應(yīng)的class文件
  4. compile 編譯java源文件(包括R.java)生成class文件
  5. dex 將編譯后的class文件和引入的jar包打包成dex文件蔽介,通過配置參數(shù)此處可以生成多個(gè)dex文件
  6. package-res 使用aapt工具處理資源文件摘投,生成.ap_文件,其中包括編譯后的資源文件虹蓄、資源索引表resources.arsc犀呼、manifest文件等
  7. package 使用apk-builder將上述步驟生成的dex文件(主dex文件)、.ap_文件等打包生成apk文件薇组,需要注意的是這里只能將主dex打包到apk中外臂,不支持同時(shí)將多個(gè)dex文件打包到apk,所以還需要以下的步驟
  8. copy_dex 將第5步生成的多個(gè)dex文件拷貝至指定目錄
  9. add-subdex-to-apk 通過aapt工具將上述除主dex外的其他dex文件添加到apk文件中
  10. jarsigner 對(duì)apk文件進(jìn)行簽名
  11. zipalign 對(duì)apk包進(jìn)行字節(jié)對(duì)齊等優(yōu)化操作

與普通的apk文件流程基本上一致律胀,需要注意的是5宋光、8、9這幾個(gè)步驟炭菌,下面來詳細(xì)看一下這幾個(gè)target

5 target dex

  <target
        name="dex"
        depends="compile" >

        <echo>Converting compiled files and external libraries into ${outdir}/${dex-file}...</echo>
        <apply
            executable="${dx}"
            failonerror="true"
            parallel="true" >
            <arg value="--dex" />
            <!-- 多dex命令-->
            <arg value="--multi-dex" />
            <!-- 每個(gè)包最大的方法數(shù)為40000 -->
            <arg value="--set-max-idx-number=40000" />
            <arg value="--main-dex-list" />
            <!-- 主dex包含的class文件列表 -->
            <arg value="${main-dex-rule}" />
            <arg value="--minimal-main-dex" />
            <arg value="--output=${outdir}" />
            <arg value="${outdir}/classes" />
            <fileset
                dir="${external-libs}"
                includes="*.jar" />
        </apply>
    </target>

其中 --multi-dex 表明打包成多個(gè)dex文件
--set-max-idx-number=40000 表示每個(gè)dex文件中最多含有的方法數(shù)颈渊,可配置
--main-dex-list 指定需要打進(jìn)主dex文件中的類宫屠,參數(shù)為主dex中類的列表文件
--minimal-main-dex 只有在main-dex-list列表中的類才能打進(jìn)主dex
關(guān)于main-dex-list中的類的規(guī)則后面會(huì)講到肺稀。這里暫且先將multidexTest目錄下的文件放入主dex中禁筏,main-dex-rule內(nèi)容如下

com/example/multidextest/MainActivity.class
com/example/multidextest/HelperOne.class
com/example/multidextest/ApplicationLoader.class

8 copy_dex

 <!-- copy dex to dir -->
    <target
        name="copy_dex"
        depends="package" >
        <echo message="copy dex..." />
        <copy todir="${basedir}" >
            <fileset dir="bin" >
                <include name="classes*.dex" />
            </fileset>
        </copy>
    </target>

執(zhí)行上述步驟5之后,會(huì)在bin目錄下生成多個(gè)dex文件投储,copy_dex就是將該目錄下的dex文件復(fù)制到basedir中第练,方便打入apk

9 add-subdex-to-apk

    <target
        name="add-subdex-to-apk"
        depends="copy_dex" >

        <echo message="Add Subdex to Apk ..." />
        <foreach
            param="dir.name"
            target="aapt-add-dex" >
            <path>
                <fileset
                    dir="bin"
                    includes="classes*.dex" />
            </path>
        </foreach>
    </target>

遍歷bin目錄下的dex文件,并將其名稱作為參數(shù)傳遞給target aapt-add-dex玛荞,注意ant中使用for循環(huán)需要引入ant-contrib擴(kuò)展包

    <!-- 使用aapt命令添加dex文件 -->
    <target name="aapt-add-dex" >
        <echo message="${dir.name}" />
        <!-- 使用正則表達(dá)式獲取classes的文件名 -->
        <propertyregex
            casesensitive="false"
            input="${dir.name}"
            property="dexfile"
            regexp="classes(.*).dex"
            select="\0" />
        <!-- 這里不需要添加classes.dex文件 -->
        <if>
            <equals
                arg1="${dexfile}"
                arg2="classes.dex" />
            <then>
                <echo>${dexfile} is not handle</echo>
            </then>
            <else>
                <echo>${dexfile} is handle</echo>
                <exec
                    executable="${aapt}"
                    failonerror="true" >
                    <arg value="add" />
                    <arg value="${out-debug-package-ospath}" />
                    <arg value="${dexfile}" />
                </exec>
            </else>
        </if>
        <delete file="${basedir}\${dexfile}" />
    </target>

使用正則表達(dá)式獲取dex文件名稱娇掏,如果是主dex classes.dex 則不處理,因?yàn)橹鱠ex在target package中已經(jīng)由apk-builder打到apk中勋眯,
如果是其他從dex文件婴梧,則調(diào)用aapt工具將其添加到apk文件中

生成apk后解壓縮,可以看到apk中已經(jīng)包含了兩個(gè)dex: classes.dex和classes2.dex


apk_package.png

我們來運(yùn)行一下apk客蹋,可以發(fā)現(xiàn)報(bào)錯(cuò), 找不到OtherActivity


exceptioin.png

這是因?yàn)樵赿alvik虛擬機(jī)加載apk時(shí)只會(huì)主動(dòng)加載主dex塞蹭,并不會(huì)對(duì)其他從dex進(jìn)行處理, 我們?cè)诖虬鼤r(shí) 也沒有將OtherActivity等其他類也打到主dex中讶坯,并且也沒有去主動(dòng)加載從dex番电,所以導(dǎo)致程序運(yùn)行時(shí)找不到從dex中的類文件。而在art虛擬機(jī)上則沒有這個(gè)問題,因?yàn)槠鋵?duì)dex文件的處理又不一樣漱办,下節(jié)再詳細(xì)討論这刷。

針對(duì)dalvik虛擬機(jī),我們需要手動(dòng)加載從dex文件娩井,一般為了不影響程序使用暇屋,都是在application中去加載從dex。

dex文件的加載

以下的copyDex()方法和loadDex()方法需要在Application類onCreate中依次執(zhí)行洞辣,以在apk啟動(dòng)時(shí)加載好所有需要的類咐刨。

首先將apk中的subdex復(fù)制到私有目錄


    private void copyDex() throws Exception{
        Log.d(TAG, "copyDex");
        //獲取已安裝的apk文件
        File originalApk = new File(getApplicationInfo().sourceDir);
        //創(chuàng)建臨時(shí)apk文件,存放于/data/data/<application package>/files目錄下
        File newApk = new File(getFilesDir().getAbsoluteFile() + File.separator + "test.apk");
        if (!newApk.exists()) {
            newApk.createNewFile();
        }
        //拷貝apk
        copyFile(new FileInputStream(originalApk), new FileOutputStream(newApk));

        ZipFile apk = new ZipFile(newApk);

        Enumeration<? extends ZipEntry> enumeration = apk.entries();

        ZipEntry zipEntry = null;

        while (enumeration.hasMoreElements()) {
            zipEntry = (ZipEntry) enumeration.nextElement();
            //遍歷得到除主dex文件外的其他從dex文件
            if (!zipEntry.isDirectory() && zipEntry.getName().endsWith("dex")&& !"classes.dex".equals(zipEntry.getName())) {
                Log.d(TAG, "zip entry name " + zipEntry.getName() + " file size "+ zipEntry.getSize());
                InputStream is = apk.getInputStream(zipEntry);
                FileOutputStream fos = openFileOutput(zipEntry.getName(), MODE_PRIVATE);
                //從臨時(shí)apk文件中拷貝出從dex文件
                copyFile(is, fos);
            }

        }

        apk.close();
    }

    //拷貝文件
    private void copyFile(InputStream is, FileOutputStream fos) {
        try {
            int hasRead = 0;
            byte[] buf = new byte[1024];
            while((hasRead = is.read(buf)) > 0) {
                fos.write(buf, 0, hasRead);
            }
            fos.flush();
        } catch (Exception e) {
            Log.d(TAG, "copyFile error " + e);
        } finally {
            try {
                if (fos != null) {
                    fos.close();
                }
                if(is != null) {
                    is.close();
                }
            } catch (Exception e2) {
                Log.d(TAG, "copyFile close error " + e2);
            }
        }
    }

拷貝完了之后就可以加載對(duì)應(yīng)的從dex文件了

//加載dex
private void loadDex(){
        Log.d(TAG, "loadDex");
        File[] files = getFilesDir().listFiles();
        if (null != files) {
        //遍歷files目錄下的dex文件
            for(File file: files){
                String fileName = file.getName();
                if (fileName.endsWith("dex") && !"classes.dex".equals(fileName)) {
                    injectDex(file.getAbsolutePath());
                }
            }
        }
    }

調(diào)用injectDex方法加載從dex文件

private String injectDex(String dexPath){
        boolean hasBaseDexClassLoaded = true;
        try {
            Class.forName("dalvik.system.BaseDexClassLoader");
        } catch (Exception e) {
            Log.d(TAG, "load BaseDexClassLoader fail " + e);
            hasBaseDexClassLoaded = false;
        }

        if (hasBaseDexClassLoaded) {
        //獲取PathClassLoader
            PathClassLoader pathClassLoader = (PathClassLoader)getClassLoader();
            //通過DexClassLoader加載指定路徑的dex文件
            DexClassLoader dexClassLoader = new DexClassLoader(dexPath, getDir("dex", 0).getAbsolutePath(), dexPath, getClassLoader());
            try {
            //通過pathClassLoader獲取已經(jīng)加載dex文件信息扬霜,即BaseDexClassLoader的DexPathList屬性定鸟,而DexPathList中的dexElements屬性用于保存加載的dex文件相關(guān)信息
            //獲取DexClassLoader加載的從dex文件信息,并與已經(jīng)加載的dex文件信息合并到一起
                Object dexElements = combineArray(getDexElements(getPathList(pathClassLoader)), getDexElements(getPathList(dexClassLoader)));
                //獲取pathList屬性
                Object pathList = getPathList(pathClassLoader);
                //然后通過反射方式設(shè)置為dexElements屬性值
                setField(pathList,  pathList.getClass(), "dexElements", dexElements);
                return "SUCCESS";
            } catch (Exception e) {
                Log.d(TAG, " " + e);
            }
        }
        return "";
    }

//通過反射獲取BaseDexClassLoader的pathList屬性
public Object getPathList(Object baseDexClassLoader)
            throws IllegalArgumentException, NoSuchFieldException, IllegalAccessException, ClassNotFoundException {
        return getField(baseDexClassLoader, Class.forName("dalvik.system.BaseDexClassLoader"), "pathList");
    }

    public Object getField(Object obj, Class<?> cl, String field)
            throws NoSuchFieldException, IllegalArgumentException, IllegalAccessException {
        Field localField = cl.getDeclaredField(field);
        localField.setAccessible(true);
        return localField.get(obj);
    }

    public static void setField(Object obj, Class<?> cl, String field, Object value)
            throws NoSuchFieldException, IllegalArgumentException, IllegalAccessException {

        Field localField = cl.getDeclaredField(field);
        localField.setAccessible(true);
        localField.set(obj, value);
    }

    //獲取DexPathList的dexElements屬性畜挥,dexElements用于存放已經(jīng)加載的dex文件
    public Object getDexElements(Object paramObject)
            throws IllegalArgumentException, NoSuchFieldException, IllegalAccessException {
        return getField(paramObject, paramObject.getClass(), "dexElements");
    }


    public static Object combineArray(Object arrayLhs, Object arrayRhs) {
        Class<?> localClass = arrayLhs.getClass().getComponentType();
        int i = Array.getLength(arrayLhs);
        int j = i + Array.getLength(arrayRhs);
        Object result = Array.newInstance(localClass, j);
        for (int k = 0; k < j; ++k) {
            if (k < i) {
                Array.set(result, k, Array.get(arrayLhs, k));
            } else {
                Array.set(result, k, Array.get(arrayRhs, k - i));
            }
        }
        return result;
    }

關(guān)于DexClassLoader和PathClassLoader的區(qū)別以及dex文件加載的過程會(huì)在后面的文章中詳細(xì)講到,這里不再贅述婴谱。

dalvik VS art 在multidex方面的區(qū)別

dalvik和art重要的區(qū)別就是dalvik執(zhí)行的是dex字節(jié)碼蟹但,而art虛擬機(jī)執(zhí)行的是本地機(jī)器馬。dalvik采用的是JIT(Just-in-Time)解釋器在程序運(yùn)行時(shí)動(dòng)態(tài)的將dex字節(jié)碼翻譯成本地機(jī)器碼谭羔,并且在程序每次重新運(yùn)行的時(shí)候都需要重復(fù)這一步驟华糖,所以dalvik的執(zhí)行效率要低于art。

android 4.4以后開始引入art瘟裸,art虛擬機(jī)執(zhí)行的是本地機(jī)器碼客叉,而之前dalvik虛擬機(jī)上運(yùn)行的app中包含的都是dex文件,所以就需要一個(gè)將dex文件翻譯成本地機(jī)器碼的過程话告。ART采用的是AOT(Ahead-of-Time),即在程序運(yùn)行之前就將dex文件翻譯成本地機(jī)器碼兼搏,時(shí)機(jī)就在app安裝的時(shí)候。

app在安裝的過程中沙郭,PackageManagerService會(huì)請(qǐng)求守護(hù)進(jìn)程installd來執(zhí)行一次dexopt操作佛呻,即對(duì)dex字節(jié)碼進(jìn)行優(yōu)化或者翻譯,如果系統(tǒng)使用的是dalvik虛擬機(jī)病线,則會(huì)調(diào)用run_dexopt來將dex文件優(yōu)化成odex文件,此處只會(huì)對(duì)classes.dex文件即主dex文件進(jìn)行優(yōu)化操作 吓著,如果系統(tǒng)使用的是art虛擬機(jī),則會(huì)調(diào)用run_dex2oat來將dex文件翻譯成本地機(jī)器碼并保存在oat文件中送挑,如果apk中含有多個(gè)dex文件绑莺,則會(huì)對(duì)多個(gè)dex文件都會(huì)進(jìn)行解釋處理,并保存到oat文件中惕耕。

所以在程序運(yùn)行時(shí)纺裁,如果是dalvik虛擬機(jī),則只會(huì)加載主dex的odex文件司澎,而從dex文件需要通過multidex的方式手動(dòng)進(jìn)行dexopt和加載操作对扶,如果是art虛擬機(jī)区赵,則直接加載oat文件即可。

main-dex-list規(guī)則

上述例子中浪南,我們是手動(dòng)指定了主dex中的類文件笼才,但是一般工程中有太多的文件,不可能靠手動(dòng)來指定哪些類來打入主dex络凿,所以google再android api21版本之上的sdk build-tools中加入了mainDexClasses 腳本來自動(dòng)遍歷指定路徑中符合規(guī)則的文件名骡送,并輸出到指定文件中,這個(gè)輸出文件就是上面提到的main-dex-rule絮记。

在sdk build-tools下有如下三個(gè)文件摔踱,其中mainDexClasses為linux下的腳本,mainDexClasses.bat為windows環(huán)境下的bat腳本怨愤,mainDexClasses.rules為過濾規(guī)則派敷,只有符合規(guī)則的類才能添加到main-dex-rule中。


maindexclasses.png

對(duì)應(yīng)的命令格式為

mainDexClasses [--output <output file>] <application path>

output file 即輸出的文件撰洗, application path為輸入的文件組篮愉,可以包括compile之后的classes文件或者其他jar包

通過查看mainDexClasses.bat可以發(fā)現(xiàn)整個(gè)處理過程大概分為三步

  1. 環(huán)境變量檢查、命令參數(shù)校驗(yàn)等 包括proguard環(huán)境變量

  2. 通過使用proguard的shrink功能差导,根據(jù)mainDexClasses.rules中定義的規(guī)則來對(duì)傳入的文件進(jìn)行裁剪试躏,去掉無關(guān)的類,最后生成tmp.jar包
    mainDexClasses.rules文件其實(shí)就是proguard的規(guī)則文件设褐,如圖


    proguard.png

默認(rèn)的mainDexClasses.rules只是保留了常用的入口類以及其直接引用類颠蕴,如Instrumentation,Application助析,Activity犀被,Service ,Receiver 外冀,ContentProvider弱判,直接引用類就是這些入口類中各個(gè)方法、變量引用锥惋、依賴的類昌腰,而這些被引用類中所引用到的其他類則被車位入口類的間接引用類,這些間接引用類是不會(huì)添加到main-dex-rule中的膀跌。

3.調(diào)用MainDexListBuilder類根據(jù)tem.jar包中的類生成主dex的文件列表遭商,即main-dex-rule

所以需要修改target dex, 在multidex打包之前先自動(dòng)生成main-dex-rule文件捅伤,然后再執(zhí)行dx操作

     <target
        name="dex"
        depends="compile" >
        <echo>dex:Converting compiled files and external libraries into ${outdir}...</echo>
        //生成main-dex-rule文件
        <path id="inputdir">
            <pathelement location="${outdir-classes}"/>
        </path>
        <property name="files" refid="inputdir"/>
        <condition property="realfiles" value=""${files}"" else="${files}" ><os family="windows"/>
        </condition>
        <exec executable="${mainDexClasses}" failonerror="true" >
            <arg value="--disable-annotation-resolution-workaround"/>
            <arg line="--output ${main-dex-rule}"/>
            <arg value="${realfiles}"/>
        </exec>

        //dex操作
        <apply
            executable="${dx}"
            failonerror="true"
            parallel="true" >
            <arg value="--dex" />
            <!-- 多dex命令-->
            <arg value="--multi-dex" />
            <!-- 每個(gè)包最大的方法數(shù)為10000 -->
            <arg value="--set-max-idx-number=10000" />
            <arg value="--main-dex-list" />
            <!-- 主dex包含的class文件列表 -->
            <arg value="${main-dex-rule}" />
            <arg value="--minimal-main-dex" />
            <arg value="--output=${outdir}" />
            <arg value="${outdir}/classes" />
            <fileset
                dir="${external-libs}"
                includes="*.jar" />
        </apply>
    </target>

這里有個(gè)問題需要注意一下劫流,在執(zhí)行mainDexClasses時(shí)多添加了一個(gè)參數(shù) --disable-annotation-resolution-workaround

如果不加該參數(shù),會(huì)產(chǎn)生如下的異常,導(dǎo)致生成的main-dex-rule文件內(nèi)容為空


dex_error.png

通過查看MainDexListBuilder的源碼祠汇,可以發(fā)現(xiàn)main中對(duì)傳入的參數(shù)進(jìn)行了驗(yàn)證仍秤,如果沒有--disable-annotation-resolution-workaround,則直接報(bào)錯(cuò)退出


main.png

關(guān)于--disable-annotation-resolution-workaround的官方解釋如下


disable.png

加入該參數(shù)后構(gòu)建正常通過可很,生成的main-dex-rule文件內(nèi)容如下


rule.png

入口類以及直接引用類已包含在內(nèi)诗力。

構(gòu)建出apk文件后解壓縮,可以看到主dex中包含的類即為main-dex-rule中含有的類我抠,


main_dex.png

從dex中包含其他間接引用類和第三方j(luò)ar包中的類


second.png

針對(duì)main-dex-list生成的規(guī)則苇本,各大互聯(lián)網(wǎng)公司都有自己的解決方案〔送兀可以參考
《Android拆分與加載Dex的多種方案對(duì)比》一文

到這里瓣窄,一個(gè)簡(jiǎn)單的dex分包示例就介紹完了,雖然是簡(jiǎn)單的例子纳鼎,但是在一般較大的工程中進(jìn)行分包操作也是同樣的方式和注意事項(xiàng)俺夕。后續(xù)也會(huì)對(duì)比gradle的分包方式。

參考文章:
《Android APK DEX分包總結(jié)》
《美團(tuán)Android DEX自動(dòng)拆包及動(dòng)態(tài)加載簡(jiǎn)介》
《MultiDex》

附demo下載
MultiDexTest

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末贱鄙,一起剝皮案震驚了整個(gè)濱河市劝贸,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌贰逾,老刑警劉巖悬荣,帶你破解...
    沈念sama閱讀 218,755評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件菠秒,死亡現(xiàn)場(chǎng)離奇詭異疙剑,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)践叠,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門言缤,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人禁灼,你說我怎么就攤上這事管挟。” “怎么了弄捕?”我有些...
    開封第一講書人閱讀 165,138評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵僻孝,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我守谓,道長(zhǎng)穿铆,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,791評(píng)論 1 295
  • 正文 為了忘掉前任斋荞,我火速辦了婚禮荞雏,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己凤优,他們只是感情好悦陋,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著筑辨,像睡著了一般俺驶。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上挖垛,一...
    開封第一講書人閱讀 51,631評(píng)論 1 305
  • 那天痒钝,我揣著相機(jī)與錄音,去河邊找鬼痢毒。 笑死送矩,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的哪替。 我是一名探鬼主播栋荸,決...
    沈念sama閱讀 40,362評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼凭舶!你這毒婦竟也來了晌块?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,264評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤帅霜,失蹤者是張志新(化名)和其女友劉穎匆背,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體身冀,經(jīng)...
    沈念sama閱讀 45,724評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡钝尸,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了搂根。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片珍促。...
    茶點(diǎn)故事閱讀 40,040評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖剩愧,靈堂內(nèi)的尸體忽然破棺而出猪叙,到底是詐尸還是另有隱情,我是刑警寧澤仁卷,帶...
    沈念sama閱讀 35,742評(píng)論 5 346
  • 正文 年R本政府宣布穴翩,位于F島的核電站,受9級(jí)特大地震影響锦积,放射性物質(zhì)發(fā)生泄漏芒帕。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評(píng)論 3 330
  • 文/蒙蒙 一充包、第九天 我趴在偏房一處隱蔽的房頂上張望副签。 院中可真熱鬧遥椿,春花似錦、人聲如沸淆储。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽本砰。三九已至碴裙,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間点额,已是汗流浹背舔株。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評(píng)論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留还棱,地道東北人载慈。 一個(gè)月前我還...
    沈念sama閱讀 48,247評(píng)論 3 371
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像珍手,于是被迫代替她去往敵國和親办铡。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評(píng)論 2 355

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

  • 為什么需要對(duì)Dex進(jìn)行分包 Android在安裝應(yīng)用的過程中琳要,系統(tǒng)會(huì)運(yùn)行一個(gè)名為DexOpt的程序?yàn)樵搼?yīng)用在當(dāng)前機(jī)...
    Boreas_su閱讀 4,289評(píng)論 0 9
  • 上篇文章講到了ant方式進(jìn)行dex分包《Android Dex分包》寡具,本篇文章再來看一下采用gradle方式進(jìn)行d...
    the_q閱讀 9,904評(píng)論 1 17
  • 一、背景 隨著業(yè)務(wù)規(guī)模發(fā)展稚补,不斷的加入新的功能童叠,添加新的類庫,app的方法數(shù)已經(jīng)超過65535课幕,這樣的情況下就會(huì)遇...
    jiantao閱讀 18,383評(píng)論 6 50
  • 當(dāng)一個(gè)app的功能越來越復(fù)雜厦坛,代碼量越來越多,也許有一天便會(huì)突然遇到下列現(xiàn)象: 1. 生成的apk在2.3以前的機(jī)...
    jimmy_Hu閱讀 1,467評(píng)論 0 3
  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,162評(píng)論 25 707