Android ActivityManagerService--07:Activity啟動過程(二)

本文轉(zhuǎn)載自:深度詳解 Android R(11.0)Activity 啟動過程

本文基于Android 11.0源碼分析

前言

??繼續(xù)上一篇分析Activity啟動過程纲熏。

3.AMS向Zygote進程發(fā)送創(chuàng)建應用進程ActivityThread的過程

3.1 時序圖

AMS13.png

3.2 ActivityTaskManagerService啟動進程

// frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
    void startProcessAsync(ActivityRecord activity, boolean knownToBeDead, boolean isTop,
            String hostingType) {
        try {
            if (Trace.isTagEnabled(TRACE_TAG_WINDOW_MANAGER)) {
                Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "dispatchingStartProcess:"
                        + activity.processName);
            }
            // Post message to start process to avoid possible deadlock of calling into AMS with the
            // ATMS lock held.
            // 發(fā)送Handler消息來啟動進程秃诵,以避免在持有ATMS鎖的情況下調(diào)用AMS時可能發(fā)生的死鎖
            final Message m = PooledLambda.obtainMessage(ActivityManagerInternal::startProcess,
                    mAmInternal, activity.processName, activity.info.applicationInfo, knownToBeDead,
                    isTop, hostingType, activity.intent.getComponent());
            mH.sendMessage(m);
        } finally {
            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
        }
    }

方法中需要發(fā)送Handler消息來啟動進程,在獲取Message消息對象時跟我們平時的使用方式不太一樣弯菊,這里是用到PooledLambda#obtainMessage()函數(shù)疮丛,代碼如下:

// frameworks/base/core/java/com/android/internal/util/function/pooled/PooledLambda.java
    static <A, B, C, D, E, F> Message obtainMessage(
            HexConsumer<? super A, ? super B, ? super C, ? super D, ? super E, ? super F> function,
            A arg1, B arg2, C arg3, D arg4, E arg5, F arg6) {
        synchronized (Message.sPoolSync) {
            PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool,
                    function, 6, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, null, null,
                    null, null, null);
            return Message.obtain().setCallback(callback.recycleOnUse());
        }
    }

可以看到這是一個靜態(tài)函數(shù)焕数,可以在接口外部直接被調(diào)用,這是Java 1.8的新特性跪楞,被static或default修飾的接口方法可以有默認實現(xiàn)缀去,感興趣的童鞋可以自行查閱哈。接口里面使用了經(jīng)典的lambda定義方式甸祭,這個泛型的定義巧妙的將我們的函數(shù)以及需要傳入的參數(shù)類型連接起來缕碎,第一個傳入的lambda接口跟后面的參數(shù)類型緊密有關(guān),調(diào)用這個函數(shù)的時候直接將需要傳入的參數(shù)一并傳入池户。這里需要提到的是lambda里面的中間接口是一種隱藏式的存在咏雌,我們在調(diào)用的過程中可以直接匿名忽略掉,上面的調(diào)用實例就是如此校焦,直接使用了::符號 直接鏈接到目標函數(shù) startProcess()赊抖。

??所以語法含義是調(diào)用ActivityManagerInternal類的startProcess() 方法,后面的那些變量就是傳遞到startProcess()方法的入?yún)⒄洹>唧w的再說就偏離主題了氛雪,感興趣的可以自行查閱,簡要說明就是這里通過acquire()函數(shù)獲取到一個PooledRunnable實例耸成,又通過它的recycleOnUse()函數(shù)得到一個PooledLambdaImpl(實現(xiàn)了PooledLambda接口)的實例报亩,所以當我們后續(xù)再調(diào)用sendMessage()的時候這個PooledRunnable的run()方法會得到執(zhí)行,也即ActivityManagerInternal#startProcess()方法墓猎。

??ActivityManagerInternal是一個抽象類,它是Activity管理本地服務(wù)接口的赚楚,它的實現(xiàn)為AMS的內(nèi)部類LocalService毙沾,在AMS啟動的過程,通過LocalServices#addService()注冊到LocalServices宠页,此類的使用方式與ServiceManager相似左胞,不同之處在于寇仓,此處注冊的服務(wù)不是Binder對象,并且只能在同一進程(system_server進程)中使用烤宙。也就是說ActivityManagerInternal實現(xiàn)類LocalService是system_server進程的本地服務(wù)Service遍烦,通過本地服務(wù)注冊到LocalServices中,而AMS也是運行在system_server進程躺枕,因此可以直接使用LocalService服猪。

??LocalServices可以理解為是一個公開緩存池,內(nèi)部使用ArrayMap來存儲本地服務(wù)對象拐云。system_server進程中每個服務(wù)都可以通過LocalServices#addService()注冊到LocalServices中罢猪,需要使用存儲的LocalService時通過 LocalServices#getService()獲取注冊的本地服務(wù)。

3.3 ActivityManagerService

// frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
public class ActivityManagerService extends IActivityManager.Stub
        implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {

    public final class LocalService extends ActivityManagerInternal {
        ......
        @Override
        public void startProcess(String processName, ApplicationInfo info, boolean knownToBeDead,
                boolean isTop, String hostingType, ComponentName hostingName) {
            try {
                if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "startProcess:"
                            + processName);
                }
                synchronized (ActivityManagerService.this) {
                    // If the process is known as top app, set a hint so when the process is
                    // started, the top priority can be applied immediately to avoid cpu being
                    // preempted by other processes before attaching the process of top app.
                    startProcessLocked(processName, info, knownToBeDead, 0 /* intentFlags */,
                            new HostingRecord(hostingType, hostingName, isTop),
                            ZYGOTE_POLICY_FLAG_LATENCY_SENSITIVE, false /* allowWhileBooting */,
                            false /* isolated */, true /* keepIfLarge */);
                }
            } finally {
                Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
            }
        }
        ......
    }
---------------------------------------------
    final ProcessRecord startProcessLocked(String processName,
            ApplicationInfo info, boolean knownToBeDead, int intentFlags,
            HostingRecord hostingRecord, int zygotePolicyFlags, boolean allowWhileBooting,
            boolean isolated, boolean keepIfLarge) {
        return mProcessList.startProcessLocked(processName, info, knownToBeDead, intentFlags,
                hostingRecord, zygotePolicyFlags, allowWhileBooting, isolated, 0 /* isolatedUid */,
                keepIfLarge, null /* ABI override */, null /* entryPoint */,
                null /* entryPointArgs */, null /* crashHandler */);
    }
}

startProcess()方法調(diào)用AMS#startProcessLocked()方法叉瘩,并返回ProcessRecord實例記錄管理啟動進程的信息膳帕。

3.4 ProcessList

// frameworks/base/services/core/java/com/android/server/am/ProcessList.java
    final ProcessRecord startProcessLocked(String processName, ApplicationInfo info,
            boolean knownToBeDead, int intentFlags, HostingRecord hostingRecord,
            int zygotePolicyFlags, boolean allowWhileBooting, boolean isolated, int isolatedUid,
            boolean keepIfLarge, String abiOverride, String entryPoint, String[] entryPointArgs,
            Runnable crashHandler) {
        ......
        // ProcessRecord記錄每個進程的信息,進程名薇缅、uid等
        ProcessRecord app = getProcessRecordLocked(processName, info.uid, keepIfLarge);
        checkSlow(startTime, "startProcess: stepping in to startProcess");
        final boolean success =
                startProcessLocked(app, hostingRecord, zygotePolicyFlags, abiOverride);
        checkSlow(startTime, "startProcess: done starting proc!");
        return success ? app : null;
    }
---------------------------------------------------------------------
    final boolean startProcessLocked(ProcessRecord app, HostingRecord hostingRecord,
            int zygotePolicyFlags, String abiOverride) {
        return startProcessLocked(app, hostingRecord, zygotePolicyFlags,
                false /* disableHiddenApiChecks */, false /* disableTestApiChecks */,
                false /* mountExtStorageFull */, abiOverride);
    }
---------------------------------------------------------------------
    boolean startProcessLocked(ProcessRecord app, HostingRecord hostingRecord,
            int zygotePolicyFlags, boolean disableHiddenApiChecks, boolean disableTestApiChecks,
            boolean mountExtStorageFull, String abiOverride) {
                  ......
        try {
            app.gids = gids;
            app.setRequiredAbi(requiredAbi);
            app.instructionSet = instructionSet;

            // Start the process.  It will either succeed and return a result containing
            // the PID of the new process, or else throw a RuntimeException.
            // 配置新創(chuàng)建進程的啟動文件:ActivityThread危彩,經(jīng)過層層封裝后經(jīng)Socket傳輸?shù)絑ygote進程
            // Zygote進程fork出新進程后要加載啟動的類文件名
            final String entryPoint = "android.app.ActivityThread";
            return startProcessLocked(hostingRecord, entryPoint, app, uid, gids,
                    runtimeFlags, zygotePolicyFlags, mountExternal, seInfo, requiredAbi,
                    instructionSet, invokeWith, startTime);
        }
        ......
    }
---------------------------------------------------------------------
    boolean startProcessLocked(HostingRecord hostingRecord, String entryPoint, ProcessRecord app,
            int uid, int[] gids, int runtimeFlags, int zygotePolicyFlags, int mountExternal,
            String seInfo, String requiredAbi, String instructionSet, String invokeWith,
            long startTime) {
        ......
        if (mService.mConstants.FLAG_PROCESS_START_ASYNC) {
            ......
        } else {
            try {
                final Process.ProcessStartResult startResult = startProcess(hostingRecord,
                        entryPoint, app,
                        uid, gids, runtimeFlags, zygotePolicyFlags, mountExternal, seInfo,
                        requiredAbi, instructionSet, invokeWith, startTime);
                handleProcessStartedLocked(app, startResult.pid, startResult.usingWrapper,
                        startSeq, false);
            }
            ......
            return app.pid > 0;
        }
    }
---------------------------------------------------------------------
    private Process.ProcessStartResult startProcess(HostingRecord hostingRecord, String entryPoint,
            ProcessRecord app, int uid, int[] gids, int runtimeFlags, int zygotePolicyFlags,
            int mountExternal, String seInfo, String requiredAbi, String instructionSet,
            String invokeWith, long startTime) {
        try {
            ......
            final Process.ProcessStartResult startResult;
            if (hostingRecord.usesWebviewZygote()) {
                startResult = startWebView(entryPoint,
                        app.processName, uid, uid, gids, runtimeFlags, mountExternal,
                        app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
                        app.info.dataDir, null, app.info.packageName, app.mDisabledCompatChanges,
                        new String[]{PROC_START_SEQ_IDENT + app.startSeq});
            } else if (hostingRecord.usesAppZygote()) {
                final AppZygote appZygote = createAppZygoteForProcessIfNeeded(app);
                // We can't isolate app data and storage data as parent zygote already did that.
                startResult = appZygote.getProcess().start(entryPoint,
                        app.processName, uid, uid, gids, runtimeFlags, mountExternal,
                        app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
                        app.info.dataDir, null, app.info.packageName,
                        /*zygotePolicyFlags=*/ ZYGOTE_POLICY_FLAG_EMPTY, isTopApp,
                        app.mDisabledCompatChanges, pkgDataInfoMap, whitelistedAppDataInfoMap,
                        false, false,
                        new String[]{PROC_START_SEQ_IDENT + app.startSeq});
            } else {
                // 啟動進程
                startResult = Process.start(entryPoint,
                        app.processName, uid, uid, gids, runtimeFlags, mountExternal,
                        app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
                        app.info.dataDir, invokeWith, app.info.packageName, zygotePolicyFlags,
                        isTopApp, app.mDisabledCompatChanges, pkgDataInfoMap,
                        whitelistedAppDataInfoMap, bindMountAppsData, bindMountAppStorageDirs,
                        new String[]{PROC_START_SEQ_IDENT + app.startSeq});
            }
            return startResult;
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        }
    }

經(jīng)過調(diào)用4個startProcessLocked()方法后,調(diào)用到了startProcess()方法泳桦,然后在startProcess()方法里做了一個判斷汤徽,根據(jù)不同的參數(shù)調(diào)用不同的方法啟動進程,這里跟進Process#start()繼續(xù)追蹤源碼蓬痒。

3.5 Process

// frameworks/base/core/java/android/os/Process.java
    public static final ZygoteProcess ZYGOTE_PROCESS = new ZygoteProcess();

        public static ProcessStartResult start(@NonNull final String processClass,
                                           @Nullable final String niceName,
                                           int uid, int gid, @Nullable int[] gids,
                                           int runtimeFlags,
                                           int mountExternal,
                                           int targetSdkVersion,
                                           @Nullable String seInfo,
                                           @NonNull String abi,
                                           @Nullable String instructionSet,
                                           @Nullable String appDataDir,
                                           @Nullable String invokeWith,
                                           @Nullable String packageName,
                                           int zygotePolicyFlags,
                                           boolean isTopApp,
                                           @Nullable long[] disabledCompatChanges,
                                           @Nullable Map<String, Pair<String, Long>>
                                                   pkgDataInfoMap,
                                           @Nullable Map<String, Pair<String, Long>>
                                                   whitelistedDataInfoMap,
                                           boolean bindMountAppsData,
                                           boolean bindMountAppStorageDirs,
                                           @Nullable String[] zygoteArgs) {
        return ZYGOTE_PROCESS.start(processClass, niceName, uid, gid, gids,
                    runtimeFlags, mountExternal, targetSdkVersion, seInfo,
                    abi, instructionSet, appDataDir, invokeWith, packageName,
                    zygotePolicyFlags, isTopApp, disabledCompatChanges,
                    pkgDataInfoMap, whitelistedDataInfoMap, bindMountAppsData,
                    bindMountAppStorageDirs, zygoteArgs);
    }

ZYGOTE_PROCESS是ZygoteProcess在Process類中的靜態(tài)實例泻骤,所以流程調(diào)用走到Process#start()方法繼續(xù)進程的啟動流程。

3.6 ZygoteProcess

// frameworks/base/core/java/android/os/ZygoteProcess.java
    public final Process.ProcessStartResult start(@NonNull final String processClass,
                                                  final String niceName,
                                                  int uid, int gid, @Nullable int[] gids,
                                                  int runtimeFlags, int mountExternal,
                                                  int targetSdkVersion,
                                                  @Nullable String seInfo,
                                                  @NonNull String abi,
                                                  @Nullable String instructionSet,
                                                  @Nullable String appDataDir,
                                                  @Nullable String invokeWith,
                                                  @Nullable String packageName,
                                                  int zygotePolicyFlags,
                                                  boolean isTopApp,
                                                  @Nullable long[] disabledCompatChanges,
                                                  @Nullable Map<String, Pair<String, Long>>
                                                          pkgDataInfoMap,
                                                  @Nullable Map<String, Pair<String, Long>>
                                                          whitelistedDataInfoMap,
                                                  boolean bindMountAppsData,
                                                  boolean bindMountAppStorageDirs,
                                                  @Nullable String[] zygoteArgs) {
        // TODO (chriswailes): Is there a better place to check this value?
        if (fetchUsapPoolEnabledPropWithMinInterval()) {
            informZygotesOfUsapPoolStatus();
        }
        try {
            // 繼續(xù)調(diào)用startViaZygote梧奢,即通過Zygote來啟動進程
            return startViaZygote(processClass, niceName, uid, gid, gids,
                    runtimeFlags, mountExternal, targetSdkVersion, seInfo,
                    abi, instructionSet, appDataDir, invokeWith, /*startChildZygote=*/ false,
                    packageName, zygotePolicyFlags, isTopApp, disabledCompatChanges,
                    pkgDataInfoMap, whitelistedDataInfoMap, bindMountAppsData,
                    bindMountAppStorageDirs, zygoteArgs);
        } 
        ......
    }
---------------------------------------------------------------------
    private Process.ProcessStartResult startViaZygote(@NonNull final String processClass,
                                                      @Nullable final String niceName,
                                                      final int uid, final int gid,
                                                      @Nullable final int[] gids,
                                                      int runtimeFlags, int mountExternal,
                                                      int targetSdkVersion,
                                                      @Nullable String seInfo,
                                                      @NonNull String abi,
                                                      @Nullable String instructionSet,
                                                      @Nullable String appDataDir,
                                                      @Nullable String invokeWith,
                                                      boolean startChildZygote,
                                                      @Nullable String packageName,
                                                      int zygotePolicyFlags,
                                                      boolean isTopApp,
                                                      @Nullable long[] disabledCompatChanges,
                                                      @Nullable Map<String, Pair<String, Long>>
                                                              pkgDataInfoMap,
                                                      @Nullable Map<String, Pair<String, Long>>
                                                              whitelistedDataInfoMap,
                                                      boolean bindMountAppsData,
                                                      boolean bindMountAppStorageDirs,
                                                      @Nullable String[] extraArgs)
                                                      throws ZygoteStartFailedEx {
        // 創(chuàng)建字符串列表argsForZygote狱掂,將應用進程的啟動參數(shù)保存在argsForZygote中
        // 包括uid、gid亲轨、targetSdkVersion趋惨、應用程序進程啟動文件:android.app.ActivityThread等參數(shù)
        ArrayList<String> argsForZygote = new ArrayList<>();
                ...... // 添加各種參數(shù)值
                argsForZygote.add(processClass);
                ......
        synchronized(mLock) {
            // The USAP pool can not be used if the application will not use the systems graphics
            // driver.  If that driver is requested use the Zygote application start path.
            // 調(diào)用zygoteSendArgsAndGetResult(),將傳入的應用進程的啟動參數(shù)argsForZygote寫入到ZygoteState中
            return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi),
                                              zygotePolicyFlags, argsForZygote);
        }
    }

ZygoteProcess#start()方法中惦蚊,繼續(xù)調(diào)用 ZygoteProcess#startViaZygote() 方法器虾,即通過Zygote來啟動進程,方法流程如下:

  • 建字符串列表argsForZygote蹦锋,將應用進程的啟動參數(shù)保存在argsForZygote列表中兆沙,包括uid、gid莉掂、targetSdkVersion葛圃、應用進程的啟動文件:android.app.ActivityThread等參數(shù)。

  • 調(diào)用ZygoteProcess#openZygoteSocketIfNeeded() 方法,如果與Zygote進程的socket連接未開啟库正,則嘗試開啟曲楚,可能會產(chǎn)生阻塞和重試。連接調(diào)用的是ZygoteState#connect() 方法褥符,ZygoteState是ZygoteProcess的內(nèi)部類龙誊。

  • 調(diào)用ZygoteProcess#zygoteSendArgsAndGetResult() 方法,向Zygote進程發(fā)送參數(shù)列表喷楣,啟動一個新的子進程并返回子進程的pid趟大。注意:當前實現(xiàn)將參數(shù)列表中的換行符替換為空格。

3.7 ZygoteProcess開啟socket連接

// frameworks/base/core/java/android/os/ZygoteProcess.java
    private ZygoteState openZygoteSocketIfNeeded(String abi) throws ZygoteStartFailedEx {
        try {
            //與Zygote進程建立Socket連接
            attemptConnectionToPrimaryZygote();
            if (primaryZygoteState.matches(abi)) {
                return primaryZygoteState;
            }
            if (mZygoteSecondarySocketAddress != null) {
                // The primary zygote didn't match. Try the secondary.
                attemptConnectionToSecondaryZygote();
                if (secondaryZygoteState.matches(abi)) {
                    return secondaryZygoteState;
                }
            }
        } catch (IOException ioe) {
            throw new ZygoteStartFailedEx("Error connecting to zygote", ioe);
        }
        throw new ZygoteStartFailedEx("Unsupported zygote ABI: " + abi);
    }
-------------------------------------------------------
    private static class ZygoteState implements AutoCloseable {
        ......
        // 上面Primary抡蛙、Secondary都是調(diào)用ZygoteState.connect()方法來創(chuàng)建一個使用給定Zygote socket 地址的 Socket 連接
        static ZygoteState connect(@NonNull LocalSocketAddress zygoteSocketAddress,
                @Nullable LocalSocketAddress usapSocketAddress)
                throws IOException {

            DataInputStream zygoteInputStream;
            BufferedWriter zygoteOutputWriter;
            final LocalSocket zygoteSessionSocket = new LocalSocket();

            if (zygoteSocketAddress == null) {
                throw new IllegalArgumentException("zygoteSocketAddress can't be null");
            }

            try {
                zygoteSessionSocket.connect(zygoteSocketAddress);
                zygoteInputStream = new DataInputStream(zygoteSessionSocket.getInputStream());
                zygoteOutputWriter =
                        new BufferedWriter(
                                new OutputStreamWriter(zygoteSessionSocket.getOutputStream()),
                                Zygote.SOCKET_BUFFER_SIZE);
            } catch (IOException ex) {
                try {
                    zygoteSessionSocket.close();
                } catch (IOException ignore) { }
                throw ex;
            }
            // socket护昧、DataInputStream、BufferedWriter封裝成 ZygoteState 對象供外部調(diào)用
            return new ZygoteState(zygoteSocketAddress, usapSocketAddress,
                                   zygoteSessionSocket, zygoteInputStream, zygoteOutputWriter,
                                   getAbiList(zygoteOutputWriter, zygoteInputStream));
        }
    }

ZygoteProcess#openZygoteSocketIfNeeded()方法粗截,打開與Zygote進程的socket連接惋耙,如果連接未建立,則嘗試調(diào)用ZygoteState#connect()方法創(chuàng)建一個使用給定Zygote socket地址的Socket連接熊昌,然后連接到Zygote的遠程服務(wù)端绽榛,同時創(chuàng)建BufferedWriter和DataInputStream進行參數(shù)數(shù)據(jù)的傳輸與讀取,最后將socket婿屹、DataInputStream灭美、BufferedWriter等封裝成ZygoteState對象供外部調(diào)用。

3.8 ZygoteProcess發(fā)送請求參數(shù)

// frameworks/base/core/java/android/os/ZygoteProcess.java
    private Process.ProcessStartResult zygoteSendArgsAndGetResult(
            ZygoteState zygoteState, int zygotePolicyFlags, @NonNull ArrayList<String> args)
            throws ZygoteStartFailedEx {
        ......
        if (shouldAttemptUsapLaunch(zygotePolicyFlags, args)) {
            try {
                return attemptUsapSendArgsAndGetResult(zygoteState, msgStr);
            } catch (IOException ex) {
                // If there was an IOException using the USAP pool we will log the error and
                // attempt to start the process through the Zygote.
                Log.e(LOG_TAG, "IO Exception while communicating with USAP pool - "
                        + ex.getMessage());
            }
        }

        return attemptZygoteSendArgsAndGetResult(zygoteState, msgStr);
    }
-----------------------------------
    // 用來fork出一個新的應用進程
    private Process.ProcessStartResult attemptZygoteSendArgsAndGetResult(
            ZygoteState zygoteState, String msgStr) throws ZygoteStartFailedEx {
        try {
            final BufferedWriter zygoteWriter = zygoteState.mZygoteOutputWriter;
            final DataInputStream zygoteInputStream = zygoteState.mZygoteInputStream;

            zygoteWriter.write(msgStr);
            zygoteWriter.flush();

            // Always read the entire result from the input stream to avoid leaving
            // bytes in the stream for future process starts to accidentally stumble
            // upon.
            // 創(chuàng)建進程
            Process.ProcessStartResult result = new Process.ProcessStartResult();
            result.pid = zygoteInputStream.readInt();
            result.usingWrapper = zygoteInputStream.readBoolean();

            if (result.pid < 0) {
                 // 進程創(chuàng)建失敗
                throw new ZygoteStartFailedEx("fork() failed");
            }
            return result;
        } catch (IOException ex) {
            zygoteState.close();
            Log.e(LOG_TAG, "IO Exception while communicating with Zygote - "
                    + ex.toString());
            throw new ZygoteStartFailedEx(ex);
        }
    }

ZygoteProcess#attemptZygoteSendArgsAndGetResult()方法中使用創(chuàng)建的ZygoteState中保存的BufferedWriter和DataInputStream來進行Socket通信昂利,通過它們進行數(shù)據(jù)流的傳輸與讀取操作届腐。system_server 進程通過BufferedWriter將參數(shù)寫給Zygote進程的socket的server端,然后阻塞等待Zygote進程的socket返回pid和usingWrapper后封裝到 ProcessStartResult蜂奸。

4.Zygote進程接收請求fork并啟動應用進程ActivityThread的過程

4.1 時序圖

AMS14.png

4.2 Zygote進程啟動犁苏、解析Socket傳入的參數(shù)

??Android系統(tǒng)底層是基于Linux 的,和Linux一樣扩所,init進程是Linux系統(tǒng)用戶進程的第一個進程围详,它是由Linux內(nèi)核(kenerl)啟動的,用來啟動屬性服務(wù)(類似Windows中的注冊表)祖屏、啟動進程助赞。其它所有的用戶進程都是init 進程的子進程,我們接下來分析的Zygote進程也是由init進程而創(chuàng)建的袁勺,Zygote如何啟動這里暫不討論雹食,Zygote啟動之后會調(diào)用ZygoteInit#main()方法,所以我們先從main()方法來看socket創(chuàng)建和消息讀取期丰。代碼如下:

// frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
public static void main(String argv[]) {
    ZygoteServer zygoteServer = null;
    ......
    Runnable caller;
    try {
        ......
        // 解析參數(shù)
        boolean startSystemServer = false;
        String zygoteSocketName = "zygote";
        String abiList = null;
        boolean enableLazyPreload = false;
        for (int i = 1; i < argv.length; i++) {
            if ("start-system-server".equals(argv[i])) {
                startSystemServer = true;
            } else if ("--enable-lazy-preload".equals(argv[i])) {
                enableLazyPreload = true;
            } else if (argv[i].startsWith(ABI_LIST_ARG)) {
                abiList = argv[i].substring(ABI_LIST_ARG.length());
            } else if (argv[i].startsWith(SOCKET_NAME_ARG)) {
                zygoteSocketName = argv[i].substring(SOCKET_NAME_ARG.length());
            } else {
                throw new RuntimeException("Unknown command line argument: " + argv[i]);
            }
        }
        ......
        zygoteServer = new ZygoteServer(isPrimaryZygote);
        ......
        // 調(diào)用 runSelectLoop() 開啟Loop循環(huán)來監(jiān)聽client socket發(fā)來的消息
        caller = zygoteServer.runSelectLoop(abiList);
    } catch (Throwable ex) {
        Log.e(TAG, "System zygote died with exception", ex);
        throw ex;
    } finally {
        if (zygoteServer != null) {
            zygoteServer.closeServerSocket();
        }
    }

    // We're in the child process and have exited the select loop. Proceed to execute the command.
    // 子進程的啟動
    if (caller != null) {
        caller.run();
    }
}

方法中新建ZygoteServer實例對象群叶,調(diào)用ZygoteServer#runSelectLoop()方法開啟loop無限循環(huán)來監(jiān)聽client socket發(fā)來的消息漠嵌,當接收到創(chuàng)建新進程的請求時,立即喚醒并執(zhí)行相應工作盖呼。如果fork出系統(tǒng)進程,則加入到列表化撕,然后繼續(xù)阻塞等待几晤;如果fork出子進程,則退出loop循環(huán)植阴,返回創(chuàng)建的應用子進程蟹瘾,并執(zhí)行子進程的啟動。

4.3 ZygoteServer開啟Loop循環(huán)監(jiān)聽Socket

// frameworks/base/core/java/com/android/internal/os/ZygoteServer.java
    ZygoteServer(boolean isPrimaryZygote) {
        mUsapPoolEventFD = Zygote.getUsapPoolEventFD();

        if (isPrimaryZygote) {
            mZygoteSocket = Zygote.createManagedSocketFromInitSocket(Zygote.PRIMARY_SOCKET_NAME);
            mUsapPoolSocket =
                    Zygote.createManagedSocketFromInitSocket(
                            Zygote.USAP_POOL_PRIMARY_SOCKET_NAME);
        } else {
            mZygoteSocket = Zygote.createManagedSocketFromInitSocket(Zygote.SECONDARY_SOCKET_NAME);
            mUsapPoolSocket =
                    Zygote.createManagedSocketFromInitSocket(
                            Zygote.USAP_POOL_SECONDARY_SOCKET_NAME);
        }

        mUsapPoolSupported = true;
        fetchUsapPoolPolicyProps();
    } 
--------------------------------------------------------------
    Runnable runSelectLoop(String abiList) {
        ArrayList<FileDescriptor> socketFDs = new ArrayList<>();
        ArrayList<ZygoteConnection> peers = new ArrayList<>();
        // mZygoteSocket是服務(wù)端的socket對象掠手,也就是Zygote進程所在socket放在fds[0]位置
        socketFDs.add(mZygoteSocket.getFileDescriptor());
        // 剛開始默認 peers[0] = null
        peers.add(null);

        mUsapPoolRefillTriggerTimestamp = INVALID_TIMESTAMP;
        // 開啟輪詢等待
        while (true) {
            fetchUsapPoolPolicyPropsWithMinInterval();
            mUsapPoolRefillAction = UsapPoolRefillAction.NONE;
            int[] usapPipeFDs = null;
            StructPollfd[] pollFDs;

            if (mUsapPoolEnabled) {
                usapPipeFDs = Zygote.getUsapPipeFDs();
                pollFDs = new StructPollfd[socketFDs.size() + 1 + usapPipeFDs.length];
            } else {
                pollFDs = new StructPollfd[socketFDs.size()];
            }
            int pollIndex = 0;
            ......
            int pollTimeoutMs;
            if (mUsapPoolRefillTriggerTimestamp == INVALID_TIMESTAMP) {
                pollTimeoutMs = -1;
            } else {
                long elapsedTimeMs = System.currentTimeMillis() - mUsapPoolRefillTriggerTimestamp;
                if (elapsedTimeMs >= mUsapPoolRefillDelayMs) {
                    pollTimeoutMs = -1;
                } else if (elapsedTimeMs <= 0) {
                    pollTimeoutMs = mUsapPoolRefillDelayMs;
                } else {
                    pollTimeoutMs = (int) (mUsapPoolRefillDelayMs - elapsedTimeMs);
                }
            }

            int pollReturnValue;
            try {
                 // 處理輪詢狀態(tài)憾朴,當pollFds有事件到來則往下執(zhí)行,否則阻塞在這里
                pollReturnValue = Os.poll(pollFDs, pollTimeoutMs);
            } catch (ErrnoException ex) {
                throw new RuntimeException("poll failed", ex);
            }

            if (pollReturnValue == 0) {
                mUsapPoolRefillTriggerTimestamp = INVALID_TIMESTAMP;
                mUsapPoolRefillAction = UsapPoolRefillAction.DELAYED;
            } else {
                boolean usapPoolFDRead = false;
                // 倒序處理喷鸽,即優(yōu)先處理已建立連接的信息众雷,后處理新建連接的請求
                while (--pollIndex >= 0) {
                        // 采用I/O多路復用 epoll 機制,當接收到客戶端請求到來做祝,則往下執(zhí)行砾省;否則跳出本次循環(huán)
                    if ((pollFDs[pollIndex].revents & POLLIN) == 0) {
                        continue;
                    }

                    if (pollIndex == 0) {
                        // Zygote server socket
                        // pollIndex==0 表示有新的客戶端請求連接到來,調(diào)用server socket端的 accpet 函數(shù)建立通信連接
                        // zygote 進程與 system server 進程建立了連接
                        ZygoteConnection newPeer = acceptCommandPeer(abiList);
                        // 加入到 peers 和 fds, 即開始下一次監(jiān)聽
                        peers.add(newPeer);
                        socketFDs.add(newPeer.getFileDescriptor());
                    } else if (pollIndex < usapPoolEventFDIndex) {
                        // Session socket accepted from the Zygote server socket
                        // socket連接成功之后從Zygote服務(wù)器的socket接受到的 Session socket
                        try {
                            ZygoteConnection connection = peers.get(pollIndex);
                            final Runnable command = connection.processOneCommand(this);

                            // TODO (chriswailes): Is this extra check necessary?
                            if (mIsForkChild) {
                                ......
                                return command;
                            } else {
                                // 如果不是fork子進程則關(guān)閉連接混槐,刪除當前 fd 消息
                                if (connection.isClosedByPeer()) {
                                    connection.closeSocket();
                                    peers.remove(pollIndex);
                                    socketFDs.remove(pollIndex);
                                }
                            }
                        } catch (Exception e) {
                            ......
                        } finally {
                            ......
                            mIsForkChild = false;
                        }
                    }
                }
                            ......
            }
                        ......
        }
    }

runSelectLoop()方法中獲取zygoteSendArgsAndGetResult()方法中傳輸過來的應用進程的啟動參數(shù)等编兄,即和Zygote進程建立起連接,其方法流程如下:

  1. 開啟Loop死循環(huán)監(jiān)聽socket事件声登,沒有連接時就阻塞在那里狠鸳,當有連接到來時喚醒繼續(xù)往下執(zhí)行。

  2. pollIndex==0時悯嗓,說明收到請求連接的事件件舵,請求和Zygote建立socket連接,調(diào)用acceptCommandPeer()方法創(chuàng)建ZygoteConnection對象绅作,并調(diào)用mZygoteSocket#accept()方法建立socket 連接芦圾,然后添加到監(jiān)聽列表peers中,等待與該socket有關(guān)的命令的到來俄认。

  3. pollIndex < usapPoolEventFDIndex時个少,表示是已經(jīng)連接的socket上的命令到來,此時調(diào)用ZygoteConnection#processOneCommand()方法來接收客戶端傳輸過來的應用進程的啟動參數(shù)眯杏,并執(zhí)行進程創(chuàng)建工作夜焦,處理完后,就會斷開與客戶端的連接岂贩,并把用于連接的socket從監(jiān)聽列表peers中移除茫经。

4.4 ZygoteConnection

// frameworks/base/core/java/com/android/internal/os/ZygoteConnection.java
    Runnable processOneCommand(ZygoteServer zygoteServer) {
        String[] args;
        try {
            // 逐行讀取 client 端通過socketwrite過來的啟動參數(shù)(字符串數(shù)組)
            args = Zygote.readArgumentList(mSocketReader);
        } catch (IOException ex) {
            throw new IllegalStateException("IOException on command socket", ex);
        }
        ......
        int pid;
        FileDescriptor childPipeFd = null;
        FileDescriptor serverPipeFd = null;
        // 將數(shù)據(jù)解析成ZygoteArguments格式
        ZygoteArguments parsedArgs = new ZygoteArguments(args);
        ......
        int[][] rlimits = null;
        if (parsedArgs.mRLimits != null) {
            rlimits = parsedArgs.mRLimits.toArray(Zygote.INT_ARRAY_2D);
        }

        int[] fdsToIgnore = null;
        if (parsedArgs.mInvokeWith != null) {
            try {
                FileDescriptor[] pipeFds = Os.pipe2(O_CLOEXEC);
                childPipeFd = pipeFds[1];
                serverPipeFd = pipeFds[0];
                Os.fcntlInt(childPipeFd, F_SETFD, 0);
                fdsToIgnore = new int[]{childPipeFd.getInt$(), serverPipeFd.getInt$()};
            } catch (ErrnoException errnoEx) {
                throw new IllegalStateException("Unable to set up pipe for invoke-with", errnoEx);
            }
        }
        // 將client端fd和server端fd存入fdsToClose數(shù)組中,然后fd置為null
        int [] fdsToClose = { -1, -1 };
        FileDescriptor fd = mSocket.getFileDescriptor();
        if (fd != null) {
            fdsToClose[0] = fd.getInt$();
        }
        fd = zygoteServer.getZygoteSocketFileDescriptor();
        if (fd != null) {
            fdsToClose[1] = fd.getInt$();
        }
        // 調(diào)用forkAndSpecialize()方法來fork子進程
        pid = Zygote.forkAndSpecialize(parsedArgs.mUid, parsedArgs.mGid, parsedArgs.mGids,
                parsedArgs.mRuntimeFlags, rlimits, parsedArgs.mMountExternal, parsedArgs.mSeInfo,
                parsedArgs.mNiceName, fdsToClose, fdsToIgnore, parsedArgs.mStartChildZygote,
                parsedArgs.mInstructionSet, parsedArgs.mAppDataDir, parsedArgs.mIsTopApp,
                parsedArgs.mPkgDataInfoList, parsedArgs.mWhitelistedDataInfoList,
                parsedArgs.mBindMountAppDataDirs, parsedArgs.mBindMountAppStorageDirs);

        try {
            if (pid == 0) {
                // in child
                //  pid = 0表示創(chuàng)建成功,則進入子進程中卸伞,即應用程序進程
                zygoteServer.setForkChild();
                // 關(guān)閉socket連接
                zygoteServer.closeServerSocket();
                IoUtils.closeQuietly(serverPipeFd);
                serverPipeFd = null;
                // 進入子進程執(zhí)行相關(guān)操作
                return handleChildProc(parsedArgs, childPipeFd, parsedArgs.mStartChildZygote);
            } else {
                // In the parent. A pid < 0 indicates a failure and will be handled in handleParentProc.
                // pid < 0表示創(chuàng)建失敗抹镊,則進入父進程返回消息給client socket表示啟動失敗
                IoUtils.closeQuietly(childPipeFd);
                childPipeFd = null;
                // 進入父進程執(zhí)行相關(guān)操作
                handleParentProc(pid, serverPipeFd);
                return null;
            }
        } finally {
            IoUtils.closeQuietly(childPipeFd);
            IoUtils.closeQuietly(serverPipeFd);
        }
    }

該方法主要作用如下:

  1. 讀取system_server端socket寫入的數(shù)據(jù),并將存入字符串數(shù)組的數(shù)據(jù)封裝成ZygoteArguments格式荤傲。

  2. 調(diào)用Zygote#forkAndSpecialize()方法來fork子進程垮耳,并返回pid,這里的pid并非是進程id遂黍,而是返回結(jié)果值终佛,0表示創(chuàng)建成功,-1則失敗雾家。

  3. 子進程創(chuàng)建成功后進入子進程執(zhí)行铃彰。

4.5 Zygote創(chuàng)建子進程(native 層創(chuàng)建)

// frameworks/base/core/java/com/android/internal/os/Zygote.java
    static int forkAndSpecialize(int uid, int gid, int[] gids, int runtimeFlags,
            int[][] rlimits, int mountExternal, String seInfo, String niceName, int[] fdsToClose,
            int[] fdsToIgnore, boolean startChildZygote, String instructionSet, String appDataDir,
            boolean isTopApp, String[] pkgDataInfoList, String[] whitelistedDataInfoList,
            boolean bindMountAppDataDirs, boolean bindMountAppStorageDirs) {
        ZygoteHooks.preFork();
        // 通過 JNI 調(diào)用 native 層方法
        int pid = nativeForkAndSpecialize(
                uid, gid, gids, runtimeFlags, rlimits, mountExternal, seInfo, niceName, fdsToClose,
                fdsToIgnore, startChildZygote, instructionSet, appDataDir, isTopApp,
                pkgDataInfoList, whitelistedDataInfoList, bindMountAppDataDirs,
                bindMountAppStorageDirs);
        if (pid == 0) {
            // Note that this event ends at the end of handleChildProc,
            Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "PostFork");
            // If no GIDs were specified, don't make any permissions changes based on groups.
            if (gids != null && gids.length > 0) {
                NetworkUtils.setAllowNetworkingForProcess(containsInetGid(gids));
            }
        }
        // Set the Java Language thread priority to the default value for new apps.
        Thread.currentThread().setPriority(Thread.NORM_PRIORITY);
        ZygoteHooks.postForkCommon();
        return pid;
    }

方法中調(diào)用native層的nativeForkAndSpecialize()方法創(chuàng)建進程,然后返回進程的pid(父進程中芯咧,返回新建的子進程的pid牙捉,子進程中,則返回0敬飒,出現(xiàn)錯誤時返回負數(shù))鹃共,具體native層的源碼就不跟了,大致看了一下過程:

  1. 調(diào)用Linux的fork()方法創(chuàng)建進程驶拱,設(shè)置進程的主線程的name霜浴,如果是null或者system_server,則為system_server蓝纲。

  2. 調(diào)用CallStaticVoidMethod()方法返回Zygote#callPostForkChildHooks()方法處理fork子線程之后的gc/線程池管理等操作阴孟。

4.6 ZygoteConnection#handleChildProc()

// frameworks/base/core/java/com/android/internal/os/ZygoteConnection.java
     private Runnable handleChildProc(ZygoteArguments parsedArgs,
            FileDescriptor pipeFd, boolean isZygote) {
        // 關(guān)閉socket連接
        closeSocket();
        // 設(shè)置應用進程的name名
        Zygote.setAppProcessName(parsedArgs, TAG);

        // End of the postFork event.
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        if (parsedArgs.mInvokeWith != null) {
            WrapperInit.execApplication(parsedArgs.mInvokeWith,
                    parsedArgs.mNiceName, parsedArgs.mTargetSdkVersion,
                    VMRuntime.getCurrentInstructionSet(),
                    pipeFd, parsedArgs.mRemainingArgs);

            // Should not get here.
            throw new IllegalStateException("WrapperInit.execApplication unexpectedly returned");
        } else {
            if (!isZygote) {
                // 初始化Zygote
                return ZygoteInit.zygoteInit(parsedArgs.mTargetSdkVersion,
                        parsedArgs.mDisabledCompatChanges,
                        parsedArgs.mRemainingArgs, null /* classLoader */);
            } else {
                return ZygoteInit.childZygoteInit(parsedArgs.mTargetSdkVersion,
                        parsedArgs.mRemainingArgs, null /* classLoader */);
            }
        }
    }

該方法就是進入子進程執(zhí)行不同的初始化操作,因為已經(jīng)fork()成功税迷,關(guān)閉socket連接等釋放資源永丝,設(shè)置應用進程的name,最后調(diào)用 ZygoteInit#zygoteInit()方法初始化Zygote箭养。

4.7 ZygoteInit#zygoteInit()

// frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
    public static final Runnable zygoteInit(int targetSdkVersion, long[] disabledCompatChanges,
            String[] argv, ClassLoader classLoader) {
            ......
        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ZygoteInit");
        RuntimeInit.redirectLogStreams();
        // 進程初始化配置慕嚷,如設(shè)置異常捕獲Handler、時區(qū)毕泌、重置 LogManager 等等
        RuntimeInit.commonInit();
        // native 層初始化 -- 打開/dev/binder驅(qū)動喝检,映射內(nèi)核的地址空間,創(chuàng)建binder線程用于IPC通信
        ZygoteInit.nativeZygoteInit();
        // 創(chuàng)建的Runnable對象
        return RuntimeInit.applicationInit(targetSdkVersion, disabledCompatChanges, argv,
                classLoader);
    }

方法流程如下:

  1. 日志流重定向撼泛,將系統(tǒng)輸出和系統(tǒng)錯誤重定向到Android日志挠说。

  2. 進程初始化配置,如設(shè)置異常捕獲Handler愿题、時區(qū)损俭、重置 LogManager 等等蛙奖。

  3. native層初始化,打開/dev/binder驅(qū)動杆兵,映射內(nèi)核的地址空間雁仲,創(chuàng)建binder線程用于IPC通信。

  4. 調(diào)用RuntimeInit#applicationInit()方法琐脏,返回創(chuàng)建的Runnable對象伯顶。

4.8 RuntimeInit#applicationInit()

// frameworks/base/core/java/com/android/internal/os/RuntimeInit.java
    protected static Runnable applicationInit(int targetSdkVersion, long[] disabledCompatChanges,
            String[] argv, ClassLoader classLoader) {
        // 如果應用程序調(diào)用了System.exit()方法立即終止進程,可能會導致剩余的運行線程在進程實際退出之前崩潰
        nativeSetExitWithoutCleanup(true);

        VMRuntime.getRuntime().setTargetSdkVersion(targetSdkVersion);
        VMRuntime.getRuntime().setDisabledCompatChanges(disabledCompatChanges);

        final Arguments args = new Arguments(argv);

        // The end of of the RuntimeInit event (see #zygoteInit).
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);

        // Remaining arguments are passed to the start class's static main
        return findStaticMain(args.startClass, args.startArgs, classLoader);
    }
----------------------------------------------------------
    protected static Runnable findStaticMain(String className, String[] argv,
            ClassLoader classLoader) {
        // 待加載的類骆膝、這里是指ActivityThread,也就是我們第三節(jié)中在ProcessList中指明的entryPoint
        Class<?> cl; 
        try {
            // 加載android.app.ActivityThread類
            cl = Class.forName(className, true, classLoader);
        } catch (ClassNotFoundException ex) {
            throw new RuntimeException(
                    "Missing class when invoking static main " + className, ex);
        }
        // 獲取 main 方法
        Method m;
        try {
            // 獲取 ActivityThread#main()函數(shù)
            m = cl.getMethod("main", new Class[] { String[].class });
        } 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);
        }
        // 判斷main方法是不是public并且static類型
        int modifiers = m.getModifiers();
        if (! (Modifier.isStatic(modifiers) && Modifier.isPublic(modifiers))) {
            throw new RuntimeException("Main method is not public and static on " + className);
        }
        // 返回Caller灶体,即本小節(jié)第一部分的ZygoteInit.main()中的caller
        return new MethodAndArgsCaller(m, argv);
    }

applicationInit()方法中首先做了一個System.exit()的保護阅签,防止退出進程時發(fā)生crash,設(shè)置targetSDKVersion等參數(shù)蝎抽,最后調(diào)用findStaticMain()方法去加載ActivityThread類以及方法政钟。

??findStaticMain()方法中通過ClassLoader加載獲取目標類ActivityThread,后獲取其靜態(tài)main方法樟结,然后封裝成MethodAndArgsCaller對象返回养交,MethodAndArgsCaller實現(xiàn)了Runnable 接口。即返回到《4.2 Zygote進程啟動瓢宦、解析Socket傳入的參數(shù)》 這節(jié)中ZygoteInit#main()方法的caller碎连,如果caller=null會調(diào)用這個Runnable的run()方法。

4.9 RuntimeInit#MethodAndArgsCaller

// frameworks/base/core/java/com/android/internal/os/RuntimeInit.java
    static class MethodAndArgsCaller implements Runnable {
        /** method to call */
        private final Method mMethod;
        /** argument array */
        private final String[] mArgs;

        public MethodAndArgsCaller(Method method, String[] args) {
            mMethod = method;
            mArgs = args;
        }

        public void run() {
            try {
                // 這里會調(diào)用new MethodAndArgsCaller()的第一個參數(shù)的方法
                // 也就是ActivityThread#main()函數(shù)
                mMethod.invoke(null, new Object[] { mArgs });
            } catch (IllegalAccessException ex) {
                throw new RuntimeException(ex);
            } catch (InvocationTargetException ex) {
                ......
            }
        }
    }

MethodAndArgsCaller#run() 方法中使用了invoke反射調(diào)用驮履,至此ActivityThread的main()方法得以執(zhí)行鱼辙,應用進程也就成功啟動ActivityThread。

4.10 小結(jié)

??Zygote 進程啟動流程:

  1. init進程為用戶空間(相對于內(nèi)核空間)的第一個進程玫镐,根據(jù)init.rc啟動Zygote進程倒戏。

  2. Zygote進程啟動步驟:創(chuàng)建虛擬機、注冊JNI恐似、執(zhí)行ZygoteInit#main()方法杜跷。

  3. Zygote進程啟動system_server進程(Zygote 啟動的第一個進程),這個本小節(jié)沒有具體分析矫夷。

  4. Zygote創(chuàng)建socket連接通道葛闷,阻塞并等待新建進程的指令到來,通過fork新建用戶進程双藕。

  5. Zygote新加了一個優(yōu)化進程創(chuàng)建的機制孵运,UsapPool-池化機制,是預先緩存了幾個進程蔓彩。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末戳气,一起剝皮案震驚了整個濱河市巾遭,隨后出現(xiàn)的幾起案子杆麸,更是在濱河造成了極大的恐慌,老刑警劉巖顺又,帶你破解...
    沈念sama閱讀 211,123評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異等孵,居然都是意外死亡稚照,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,031評論 2 384
  • 文/潘曉璐 我一進店門俯萌,熙熙樓的掌柜王于貴愁眉苦臉地迎上來果录,“玉大人,你說我怎么就攤上這事咐熙∪鹾悖” “怎么了?”我有些...
    開封第一講書人閱讀 156,723評論 0 345
  • 文/不壞的土叔 我叫張陵棋恼,是天一觀的道長返弹。 經(jīng)常有香客問我,道長爪飘,這世上最難降的妖魔是什么义起? 我笑而不...
    開封第一講書人閱讀 56,357評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮师崎,結(jié)果婚禮上默终,老公的妹妹穿的比我還像新娘。我一直安慰自己犁罩,他們只是感情好穷蛹,可當我...
    茶點故事閱讀 65,412評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著昼汗,像睡著了一般肴熏。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上顷窒,一...
    開封第一講書人閱讀 49,760評論 1 289
  • 那天蛙吏,我揣著相機與錄音,去河邊找鬼鞋吉。 笑死鸦做,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的谓着。 我是一名探鬼主播泼诱,決...
    沈念sama閱讀 38,904評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼赊锚!你這毒婦竟也來了治筒?” 一聲冷哼從身側(cè)響起屉栓,我...
    開封第一講書人閱讀 37,672評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎耸袜,沒想到半個月后友多,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,118評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡堤框,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,456評論 2 325
  • 正文 我和宋清朗相戀三年域滥,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蜈抓。...
    茶點故事閱讀 38,599評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡启绰,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出沟使,到底是詐尸還是另有隱情委可,我是刑警寧澤,帶...
    沈念sama閱讀 34,264評論 4 328
  • 正文 年R本政府宣布格带,位于F島的核電站,受9級特大地震影響刹枉,放射性物質(zhì)發(fā)生泄漏叽唱。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,857評論 3 312
  • 文/蒙蒙 一微宝、第九天 我趴在偏房一處隱蔽的房頂上張望棺亭。 院中可真熱鬧,春花似錦蟋软、人聲如沸镶摘。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,731評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽凄敢。三九已至,卻和暖如春湿痢,著一層夾襖步出監(jiān)牢的瞬間涝缝,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,956評論 1 264
  • 我被黑心中介騙來泰國打工譬重, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留拒逮,地道東北人。 一個月前我還...
    沈念sama閱讀 46,286評論 2 360
  • 正文 我出身青樓臀规,卻偏偏與公主長得像滩援,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子塔嬉,可洞房花燭夜當晚...
    茶點故事閱讀 43,465評論 2 348

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