android進(jìn)階——深入理解應(yīng)用程序的進(jìn)程是如何啟動(dòng)的

應(yīng)用程序的進(jìn)程啟動(dòng)

簡(jiǎn)介

當(dāng)我們打開android手機(jī)的時(shí)候逮光,不知道你是否想過(guò)app是如何啟動(dòng)的呢?

接下來(lái),我將從源碼角度進(jìn)行解析,當(dāng)然筑悴,本文作為上篇装盯,是介紹應(yīng)用程序的進(jìn)程啟動(dòng)過(guò)程,而不是應(yīng)用程序的啟動(dòng)過(guò)程绑谣,他們的區(qū)別就是煮飯前要準(zhǔn)備鍋具壤玫,沒(méi)有鍋具就無(wú)法煮飯班缎,本文就是準(zhǔn)備鍋具的,但是也不簡(jiǎn)單哦苛败。

文章將從兩個(gè)方面介紹,一個(gè)AMS發(fā)送請(qǐng)求,一個(gè)是Zygote接受請(qǐng)求绩衷。

AMS就是Activity Manager System低缩,管理Activity的,而Zygote就是創(chuàng)建進(jìn)程的一個(gè)進(jìn)程慨仿,所以AMS要想創(chuàng)建進(jìn)程必須從Zygote那里進(jìn)行申請(qǐng),一系列的故事,就此展開如筛。

AMS發(fā)送請(qǐng)求

image.png

從圖中可以簡(jiǎn)略看出做粤,首先AMS自己調(diào)用了自己的startProcessLocked方法,會(huì)創(chuàng)建相應(yīng)的用戶id,用戶id組刚夺,以及對(duì)應(yīng)的應(yīng)用程序進(jìn)程的主線程名——android.app.ActivityThread末捣。

private final void startProcessLocked(ProcessRecord app, String hostingType,
        String hostingNameStr, String abiOverride, String entryPoint, String[] entryPointArgs) {
    ...
    try {
        int uid = app.uid;
        int[] gids = null;
        int mountExternal = Zygote.MOUNT_EXTERNAL_NONE;
        if (!app.isolated) {
            int[] permGids = null;
            if (ArrayUtils.isEmpty(permGids)) {
                gids = new int[3];
            } else {
                gids = new int[permGids.length + 3];
                System.arraycopy(permGids, 0, gids, 3, permGids.length);
            }
            gids[0] = UserHandle.getSharedAppGid(UserHandle.getAppId(uid));
            gids[1] = UserHandle.getCacheAppGid(UserHandle.getAppId(uid));
            gids[2] = UserHandle.getUserGid(UserHandle.getUserId(uid));
        }
        String requiredAbi = (abiOverride != null) ? abiOverride : app.info.primaryCpuAbi;
        if (requiredAbi == null) {
            requiredAbi = Build.SUPPORTED_ABIS[0];
        }

        ...
        if (entryPoint == null) entryPoint = "android.app.ActivitThread";
        ...
        if (hostingType.equals("webview_service")) {
            ...
        } else {
            startResult = Process.start(entryPoint,
                    app.processName, uid, uid, gids, debugFlags, mountExternal,
                    app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
                    app.info.dataDir, invokeWith, entryPointArgs);
        }
        ...
}

這里面的start中有一個(gè)參數(shù)名為requireAbi邦邦,在下面會(huì)改成abi起到比對(duì)的重要作用。

startProcessLocked方法內(nèi)部會(huì)調(diào)用Process的start方法,而這個(gè)start方法只干了一件事,就是調(diào)用ZygoteProcessstart方法捉偏。

ZygoteProcess的作用在于保持與Zygote通信的狀態(tài)讹躯,在其start方法中锉罐,會(huì)調(diào)用StartViaZygote方法馆揉,這里的出現(xiàn)了下面很重要的abi。

public final Process.ProcessStartResult start(final String processClass,
                                                  final String niceName,
                                                  int uid, int gid, int[] gids,
                                                  ...
                                                  String abi,
                                                  ...
                                                  String[] zygoteArgs) {
        try {
            return startViaZygote(processClass, niceName, uid, gid, gids,
                    debugFlags, mountExternal, targetSdkVersion, seInfo,
                    abi, instructionSet, appDataDir, invokeWith, zygoteArgs);
       ...
    }

StartViaZygote方法會(huì)維護(hù)一個(gè)名為argsForZygote的列表抖拦,顧名思義升酣,所有能夠啟動(dòng)應(yīng)用程序的進(jìn)程的參數(shù),都會(huì)保存在這里态罪。然后這個(gè)列表作為一個(gè)參數(shù)噩茄,被傳入到ZygoteSendArgsAndGetResult方法中,這個(gè)方法也是在StartViaZygote方法中被調(diào)用.另外要注意的是复颈,openZygoteSocketIfNeeded方法也是作為參數(shù)傳入了ZygoteSendArgsAndGetResult方法中绩聘。

private Process.ProcessStartResult startViaZygote(final String processClass,
                                                  final String niceName,
                                                  final int uid, final int gid,
                                                  final int[] gids,
                                                  ...
                                                  String abi,
                                                  ...)
                                                  throws ZygoteStartFailedEx {
    ArrayList<String> argsForZygote = new ArrayList<String>();
    argsForZygote.add("--runtime-args");
    argsForZygote.add("--setuid=" + uid);
    argsForZygote.add("--setgid=" + gid);
    ...
    synchronized(mLock) {
        return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi), argsForZygote);
    }
}

ZygoteSendArgsAndGetResult方法作用是將傳入的參數(shù)沥割,也就是argsForZygote中保存的數(shù)據(jù)寫入ZygoteState中,從圖中我們雖然將ZygoteStateZygoteProcess做了并列處理凿菩,但是ZygoteStateZygoteProcess的一個(gè)靜態(tài)內(nèi)部類机杜,上面提到的ZygoteProcess的作用在于保持與Zygote通信的狀態(tài),其功能的完成就是在ZygoteState中衅谷。

private static Process.ProcessStartResult zygoteSendArgsAndGetResult(
        ZygoteState zygoteState, ArrayList<String> args)
        throws ZygoteStartFailedEx {
    try {
     
        final BufferedWriter writer = zygoteState.writer;
        final DataInputStream inputStream = zygoteState.inputStream;

        writer.write(Integer.toString(args.size()));
        writer.newLine();

        for (int i = 0; i < sz; i++) {
            String arg = args.get(i);
            writer.write(arg);
            writer.newLine();
        }

        writer.flush();
        ...
        result.pid = inputStream.readInt();
        result.usingWrapper = inputStream.readBoolean();
        ...
}

從上面的方法看出椒拗,ZygoteSendArgsAndGetResult方法的第一個(gè)參數(shù)變成了ZygoteState,也就是說(shuō)获黔,openZygoteSocketIfNeeded方法的返回值是ZygoteState蚀苛。

openZygoteSocketIfNeeded方法也處于ZygoteProcess中,就寫在StartViaZygote下面玷氏,可以看到堵未,在第6行與Zygote進(jìn)程建立了連接。

private ZygoteState openZygoteSocketIfNeeded(String abi) throws ZygoteStartFailedE
    Preconditions.checkState(Thread.holdsLock(mLock), "ZygoteProcess lock not held");

    if (primaryZygoteState == null || primaryZygoteState.isClosed()) {
        try {
            primaryZygoteState = ZygoteState.connect(mSocket);//建立連接
        } catch (IOException ioe) {
            throw new ZygoteStartFailedEx("Error connecting to primary zygote", ioe);
        }
    }
    //這里primaryZygoteState是連接主模式后返回的ZygoteState,比對(duì)其是否與需要的abi匹配
    if (primaryZygoteState.matches(abi)) {
        return primaryZygoteState;
    }
    //如果主模式不成功盏触,連接輔模式
    if (secondaryZygoteState == null || secondaryZygoteState.isClosed()) {
        try {
            secondaryZygoteState = ZygoteState.connect(mSecondarySocket);
        } catch (IOException ioe) {
            throw new ZygoteStartFailedEx("Error connecting to secondary zygote", ioe);
        }
    }
    //匹配輔模式的abi
    if (secondaryZygoteState.matches(abi)) {
        return secondaryZygoteState;
    }

    throw new ZygoteStartFailedEx("Unsupported zygote ABI: " + abi);

為什么第6行可以連接Zygote呢渗蟹?因?yàn)閆ygote是最先創(chuàng)建的進(jìn)程,如果沒(méi)有Zygote耻陕,我們的應(yīng)用程序的進(jìn)程也無(wú)從談起拙徽,而Zygote為了創(chuàng)建其他進(jìn)程,他會(huì)設(shè)置一個(gè)Socket監(jiān)聽诗宣,就是通過(guò)這個(gè)Socket膘怕,完成與Zygote的連接。

而對(duì)于主模式和輔助模式召庞,這里不細(xì)講岛心,就是有兩次連接并匹配abi的機(jī)會(huì)。

要匹配abi的原因在于篮灼,Zygote雖然可以fork自己不斷創(chuàng)建進(jìn)程忘古,但是畢竟是有限的,而且需要資源诅诱,只有啟動(dòng)某個(gè)應(yīng)用程序所必須的進(jìn)程才有被創(chuàng)建的意義髓堪,這時(shí)候就在一開始的AMS中給設(shè)置一個(gè)abi,如果與后來(lái)的Zygote匹配娘荡,證明是需要被創(chuàng)建的干旁。

當(dāng)匹配完成時(shí),就會(huì)返回一個(gè)primaryZygoteState炮沐,此時(shí)AMS發(fā)送請(qǐng)求這一流程就結(jié)束了争群,接下來(lái),就是Zygote如何接受請(qǐng)求并創(chuàng)建應(yīng)用程序的進(jìn)程的過(guò)程了大年。

Zygote接受請(qǐng)求

依然是時(shí)序圖换薄,甚至說(shuō)玉雾,有了時(shí)序圖,根本就不需要看后面的文章啦轻要。

image.png

看起來(lái)比上一個(gè)圖稍微復(fù)雜些复旬,不過(guò)可以發(fā)現(xiàn)還是很有規(guī)律的,Zygote先到ZygoteServer再回來(lái)冲泥,再到ZygoteConnection赢底,再回來(lái)……

所以,后面幾個(gè)方法都在ZygoteInit中被調(diào)用柏蘑,而且時(shí)間先后就是圖中所示。

在AMS發(fā)送請(qǐng)求完成后粹庞,Zygote會(huì)連接到一個(gè)ZygoteState咳焚,而這個(gè)ZygoteState在之前的ZygoteSendArgsAndGetResult方法中被寫入了argsForZygote所保存的數(shù)據(jù),這些數(shù)據(jù)就是請(qǐng)求的具體參數(shù)庞溜。

現(xiàn)在Zygote將會(huì)收到這些參數(shù)革半,并在ZygoteInit中進(jìn)行相應(yīng)操作。

ZygoteInit首先會(huì)執(zhí)行其main方法流码。

 public static void main(String argv[]) {
       ...
        try {
           ...
            //設(shè)置Socket監(jiān)聽
            zygoteServer.registerServerSocket(socketName);
            if (!enableLazyPreload) {
                bootTimingsTraceLog.traceBegin("ZygotePreload");
                EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_START,
                    SystemClock.uptimeMillis());
                preload(bootTimingsTraceLog);
                EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_END,
                    SystemClock.uptimeMillis());
                bootTimingsTraceLog.traceEnd(); // ZygotePreload
            } else {
                Zygote.resetNicePriority();
           ...

            if (startSystemServer) {
                startSystemServer(abiList, socketName, zygoteServer);//啟動(dòng)SystemServer進(jìn)程
            }

            Log.i(TAG, "Accepting command socket connections");
            zygoteServer.runSelectLoop(abiList);//等待AMS請(qǐng)求

            zygoteServer.closeServerSocket();
        ...
    }

這里最后一個(gè)注釋又官,就是一切都準(zhǔn)備好了,只等AMS發(fā)來(lái)請(qǐng)求漫试,現(xiàn)在我們看看RunSelectLoop方法是如何做的六敬,猜測(cè)既然是等待,那肯定有一個(gè)死循環(huán)while驾荣,這個(gè)函數(shù)就在ZygoteServer之中外构。

void runSelectLoop(String abiList) throws Zygote.MethodAndArgsCaller {
    ArrayList<FileDescriptor> fds = new ArrayList<FileDescriptor>();
    ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>();
    fds.add(mServerSocket.getFileDescriptor());
    peers.add(null);
    while (true) {
        ...
        for (int i = pollFds.length - 1; i >= 0; --i) {
            if ((pollFds[i].revents & POLLIN) == 0) {
                continue;
            }
            if (i == 0) {
                ZygoteConnection newPeer = acceptCommandPeer(abiList);
                peers.add(newPeer);
                fds.add(newPeer.getFileDesciptor());
            } else {
                //一個(gè)連接一個(gè)連接的運(yùn)行
                boolean done = peers.get(i).runOnce(this);
                if (done) {
                    peers.remove(i);
                    fds.remove(i);
                }
            }
        }
    }
}

果然有一個(gè)while(true),哈哈播掷,獎(jiǎng)勵(lì)自己晚上早睡十分鐘审编。

可以看到這里維護(hù)了一個(gè)ZygoteConnection型的列表,每當(dāng)連接不夠時(shí)歧匈,就會(huì)增加垒酬,然后一個(gè)一個(gè)連接的進(jìn)行執(zhí)行,執(zhí)行玩一個(gè)連接件炉,就移除一個(gè)勘究。

runOnce方法的作用就是讀取argsForZygote所保存的數(shù)據(jù),完成交接妻率,這個(gè)方法顯然乱顾,在ZygoteConnection中。

boolean runOnce(ZygoteServer zygoteServer) throws Zygote.MethodAndArgsCaller {
    String args[];
    Arguments parsedArgs = null;
    FileDescriptor[] descriptors;
    try {
        args = readArgumentList(); //讀取之前寫入的數(shù)據(jù)
        descriptors = mSocket.getAncillaryFileDescriptors();
    } catch (IOException ex) {
        Log.w(TAG, "IOException on command socket " + ex.getMessage());
        closeSocket();
        return true;
    }
    ...
    try {
        parsedArgs = new Arguments(args);
        ...
        //創(chuàng)建應(yīng)用程序進(jìn)程
        pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid, parsedArgs.gids,
                parsedArgs.debugFlags, rlimits, parsedArgs.mountExternal, parsedArgs.seInfo,
                parsedArgs.niceName, fdsToClose, fdsToIgnore, parsedArgs.instructionSet,
                parsedArgs.appDataDir);
    }
    ...
    try {
        if (pid == 0) {//當(dāng)前邏輯運(yùn)行在子程序中
            zygoteServer.closeServerSocket();
            IoUtils.closeQuietly(serverPipeFd);
            serverPipeFd = null;
            handleChildProc(parsedArgs, descriptors, childPipeFd, newStderr);

            return true;
        }
        ...
    } 
}

如果在18行處的pid返回值為0宫静,那么恭喜走净,此時(shí)的子進(jìn)程已經(jīng)創(chuàng)建完成券时,各種id和參數(shù)都傳進(jìn)去了,此時(shí)就會(huì)調(diào)用handleChildProc方法來(lái)處理出現(xiàn)的應(yīng)用程序的進(jìn)程(還是要提醒伏伯,本文創(chuàng)建的是應(yīng)用程序的進(jìn)程橘洞,而非應(yīng)用程序本身)。

handleChildProc方法回調(diào)了ZygoteInit中的zygoteInit方法说搅,注意炸枣,這里的z是小寫!

private void handleChildProc(Arguments parsedArgs,
        FileDescriptor[] descriptors, FileDescriptor pipeFd, PrintStream newStderr)
        throws Zygote.MethodAndArgsCaller {
    ...
    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
    if (parsedArgs.invokeWith != null) {
        WrapperInit.execApplication(parsedArgs.invokeWith,
                parsedArgs.niceName, parsedArgs.targetSdkVersion,
                VMRuntime.getCurrentInstructionSet(),
                pipeFd, parsedArgs.remainingArgs);
    } else {
        ZygoteInit.zygoteInit(parsedArgs.targetSdkVersion,
                parsedArgs.remainingArgs, null /* classLoader */);
    }
}

現(xiàn)在我們回到ZygoteInit中弄唧,看看zygoteInit是怎么寫的适肠。

public static final void zygoteInit(int targetSdkVersion, String[] argv,
        ClassLoader classLoader) throws Zygote.MethodAndArgsCaller {
    if (RuntimeInit.DEBUG) {
        Slog.d(RuntimeInit.TAG, "RuntimeInit: Starting application from zygote");
    }

    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ZygoteInit");
    RuntimeInit.redirectLogStreams();

    RuntimeInit.commonInit();
    ZygoteInit.nativeZygoteInit();
    RuntimeInit.applicationInit(targetSdkVersion, argv, classLoader);
}

注意到這個(gè)方法是一個(gè)靜態(tài)的final方法,在12行代碼處候引,會(huì)執(zhí)行RuntimeInitapplicationInit方法侯养。

protected static void applicationInit(int targetSdkVersion, String[]
        throws Zygote.MethodAndArgsCaller {
    ...
    final Arguments args;
    try {
        args = new Arguments(argv);
    } catch (IllegalArgumentException ex) {
        Slog.e(TAG, ex.getMessage());
        // let the process exit
        return;
    }
    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
    invokeStaticMain(args.startClass, args.startArgs, classLoader);
}

同樣在最后一行,調(diào)用了invokeStaticMain方法澄干,這里傳入了三個(gè)參數(shù)逛揩,第一個(gè)參數(shù)args.startClass就是本文一開始說(shuō)的那個(gè)主線程的名字android.app.MainThread

終于我們到了最后一站麸俘!現(xiàn)在我們進(jìn)入invokeStaticMain方法辩稽。

private static void invokeStaticMain(String className, String[] argv, ClassLoader
        throws Zygote.MethodAndArgsCaller {
    Class<?> cl;

    try {
        cl = Class.forName(className, true, classLoader);//反射獲取MainThread
    } catch (ClassNotFoundException ex) {
        throw new RuntimeException(
                "Missing class when invoking static main " + className,
                ex);
    }

    Method m;
    try {
        m = cl.getMethod("main", new Class[] { String[].class });//獲得main
    } catch (NoSuchMethodException ex) {
        throw new RuntimeException(
                "Missing static main on " + className, ex);
    } catch (SecurityException ex) {
        throw new RuntimeException(
                "Problem getting static main on " + className, ex);
   ...
    throw new Zygote.MethodAndArgsCaller(m, argv);
}

這里的className就是我們剛剛提到的args.startClass,也就是主線程名字从媚,通過(guò)第6行的反射獲取到對(duì)應(yīng)的類逞泄,命名為c1,然后再通過(guò)15行代碼獲取主方法main静檬,并將main傳入到MethodAndArgsCaller方法中炭懊,當(dāng)做異常拋出。

為什么這里要用拋出異常的方法呢拂檩?

聯(lián)想到這是最后一步侮腹,拋出異常的方式將會(huì)清除設(shè)置過(guò)程中需要的堆棧幀,僅僅留下一個(gè)main方法稻励,使得main方法看起來(lái)像一個(gè)入口方法父阻,畢竟,前面那么多的工作望抽,那么多類的流血犧牲加矛,最后都是為了成就他。當(dāng)他出現(xiàn)時(shí)煤篙,前面的東西斟览,也就沒(méi)有必要存在了。

現(xiàn)在異常拋出了辑奈,誰(shuí)來(lái)接受呢苛茂?當(dāng)然已烤,是main了,main位于ZygoteInit中妓羊。

public static void main(String argv[]) {
    ...
        zygoteServer.closeServerSocket();
    } catch (Zygote.MethodAndArgsCaller caller) {
        caller.run();
    } catch (Throwable ex) {
        Log.e(TAG, "System zygote died with exception", ex);
        zygoteServer.closeServerSocket();
        throw ex;
    }
}

可以看到胯究,當(dāng)出現(xiàn)了一個(gè)MethodAndArgsCaller型的異常的時(shí)候,main會(huì)捕捉到躁绸,并且執(zhí)行他的run方法裕循,這個(gè)run方法在MethodAndArgsCaller中,他是一個(gè)靜態(tài)類净刮。

public static class MethodAndArgsCaller extends Exception
        implements Runnable {
    ...
    public void run() {
        try {
            mMethod.invoke(null, new Object[] { mArgs });
        } catch (IllegalAccessException ex) {
            throw new RuntimeException(ex);
        } catch (InvocationTargetException ex) {
           ...
        }
    }
}

調(diào)用了invoke方法后剥哑,ActivityThread里面的main方法就會(huì)被動(dòng)態(tài)調(diào)用,應(yīng)用程序的進(jìn)程就進(jìn)入了這一main方法中淹父。

經(jīng)過(guò)上面的種種操作星持,我們創(chuàng)建了應(yīng)用程序的進(jìn)程并且運(yùn)行了ActivityThread。

小結(jié)

簡(jiǎn)單來(lái)說(shuō)就是要想創(chuàng)建應(yīng)用程序的進(jìn)程弹灭,需要AMS發(fā)送請(qǐng)求,傳入主線程名揪垄,這一請(qǐng)求必須符合Zygote的abi穷吮,并且將請(qǐng)求參數(shù)寫在ZygoteState中。

然后Zygote讀取參數(shù)饥努,fork自身去生成新的進(jìn)程捡鱼,如果返回的pid為0,那么就完成了應(yīng)用程序的進(jìn)程的創(chuàng)建酷愧。

然后通過(guò)反射將主線程名轉(zhuǎn)換為主線程的類驾诈,獲得其main方法,通過(guò)拋出一個(gè)異常清除掉之前的堆棧幀溶浴,在main方法里面接受這個(gè)異常乍迄,并執(zhí)行run方法以激活main方法,使得應(yīng)用程序的進(jìn)程進(jìn)入到main方法中得以運(yùn)行士败。

寫了一早上闯两,呼~,準(zhǔn)備吃飯啦谅将,還有什么不懂的可以評(píng)論區(qū)提問(wèn)哦漾狼。

本文取材自《Android進(jìn)階解密》,資源在微信公眾號(hào)【小松漫步】饥臂,一起交流學(xué)習(xí)吧逊躁。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市隅熙,隨后出現(xiàn)的幾起案子稽煤,更是在濱河造成了極大的恐慌核芽,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,194評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件念脯,死亡現(xiàn)場(chǎng)離奇詭異狞洋,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)绿店,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,058評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門吉懊,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人假勿,你說(shuō)我怎么就攤上這事借嗽。” “怎么了转培?”我有些...
    開封第一講書人閱讀 156,780評(píng)論 0 346
  • 文/不壞的土叔 我叫張陵恶导,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我浸须,道長(zhǎng)惨寿,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,388評(píng)論 1 283
  • 正文 為了忘掉前任删窒,我火速辦了婚禮裂垦,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘肌索。我一直安慰自己蕉拢,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,430評(píng)論 5 384
  • 文/花漫 我一把揭開白布诚亚。 她就那樣靜靜地躺著晕换,像睡著了一般。 火紅的嫁衣襯著肌膚如雪站宗。 梳的紋絲不亂的頭發(fā)上闸准,一...
    開封第一講書人閱讀 49,764評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音梢灭,去河邊找鬼恕汇。 笑死,一個(gè)胖子當(dāng)著我的面吹牛或辖,可吹牛的內(nèi)容都是我干的瘾英。 我是一名探鬼主播,決...
    沈念sama閱讀 38,907評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼颂暇,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼缺谴!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,679評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤湿蛔,失蹤者是張志新(化名)和其女友劉穎膀曾,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體阳啥,經(jīng)...
    沈念sama閱讀 44,122評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡添谊,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,459評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了察迟。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片斩狱。...
    茶點(diǎn)故事閱讀 38,605評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖扎瓶,靈堂內(nèi)的尸體忽然破棺而出所踊,到底是詐尸還是另有隱情,我是刑警寧澤概荷,帶...
    沈念sama閱讀 34,270評(píng)論 4 329
  • 正文 年R本政府宣布秕岛,位于F島的核電站,受9級(jí)特大地震影響误证,放射性物質(zhì)發(fā)生泄漏继薛。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,867評(píng)論 3 312
  • 文/蒙蒙 一愈捅、第九天 我趴在偏房一處隱蔽的房頂上張望惋增。 院中可真熱鬧,春花似錦改鲫、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,734評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至壶冒,卻和暖如春缕题,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背胖腾。 一陣腳步聲響...
    開封第一講書人閱讀 31,961評(píng)論 1 265
  • 我被黑心中介騙來(lái)泰國(guó)打工烟零, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人咸作。 一個(gè)月前我還...
    沈念sama閱讀 46,297評(píng)論 2 360
  • 正文 我出身青樓锨阿,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親记罚。 傳聞我的和親對(duì)象是個(gè)殘疾皇子墅诡,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,472評(píng)論 2 348