SurfaceFlinger源碼分析三

寫在開頭:本文基本算是跟著Android畫面顯示流程分析(4)這篇過的一遍筆記。

本篇帶著下面這2個(gè)問題去分析。

Android應(yīng)用程序是如何請求SurfaceFlinger服務(wù) 創(chuàng)建 一個(gè)Surface的?
Android應(yīng)用程序是如何請求SurfaceFlinger服務(wù) 渲染 一個(gè)Surface的凛辣?

先來看第一個(gè)問題:
Android應(yīng)用程序是如何請求SurfaceFlinger服務(wù) 創(chuàng)建 一個(gè)Surface的撤卢?
Activity在顯示過程中较幌,會(huì)調(diào)用requestLayout弄兜。

//frameworks/base/services/java/com/android/server/WindowManagerService.java
public int relayoutWindow(Session session, IWindow client, int seq, LayoutParams attrs,
            int requestedWidth, int requestedHeight, int viewVisibility, int flags,
            long frameNumber, Rect outFrame, Rect outContentInsets,
            Rect outVisibleInsets, Rect outStableInsets, Rect outBackdropFrame,
            DisplayCutout.ParcelableWrapper outCutout, MergedConfiguration mergedConfiguration,
            SurfaceControl outSurfaceControl, InsetsState outInsetsState,
            InsetsSourceControl[] outActiveControls, Point outSurfaceSize,
            SurfaceControl outBLASTSurfaceControl) {
      ......
      try {
          result = createSurfaceControl(outSurfaceControl, outBLASTSurfaceControl,
          result, win, winAnimator);
      } catch (Exception e) {
      ......
}


private int createSurfaceControl(SurfaceControl outSurfaceControl,
            SurfaceControl outBLASTSurfaceControl, int result,
            WindowState win, WindowStateAnimator winAnimator) {
    ......
    WindowSurfaceController surfaceController;
    try {
        Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "createSurfaceControl");
        surfaceController = winAnimator.createSurfaceLocked(win.mAttrs.type, win.mOwnerUid);
    } finally {
        Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
    }
    if (surfaceController != null) {
        surfaceController.getSurfaceControl(outSurfaceControl);
        ......
    }
    ......
}

SurfaceControl的創(chuàng)建過程妻味,注意這里創(chuàng)建工作是調(diào)用winAnimator來完成的正压,注意下面那句surfaceController.getSurfaceControl會(huì)把創(chuàng)建出來的SurfaceControl通過形參outSurfaceControl傳出去。

創(chuàng)建了一個(gè)WindowSurfaceController责球,進(jìn)而再創(chuàng)建SurfaceControll:

//frameworks/base/services/core/java/com/android/server/wm/WindowStateAnimator.java
 WindowSurfaceController createSurfaceLocked(int windowType, int ownerUid) {
    ......
    /*WindowSurfaceController mSurfaceController;*/
    mSurfaceController = new WindowSurfaceController(attrs.getTitle().toString(), width,
                    height, format, flags, this, windowType, ownerUid);
    ......
}

走到WindowSurfaceController中焦履,這里使用了SurfaceControl.Builder去構(gòu)造SurfaceControl

//http://aosp.opersys.com/xref/android-12.0.0_r2/xref/frameworks/base/services/core/java/com/android/server/wm/WindowSurfaceController.java

 WindowSurfaceController(String name, int w, int h, int format,
            int flags, WindowStateAnimator animator, int windowType, int ownerUid) {
     ......
     final SurfaceControl.Builder b = win.makeSurface()
                .setParent(win.getSurfaceControl())
                .setName(name)
                .setBufferSize(w, h)
                .setFormat(format)
                .setFlags(flags)
                .setMetadata(METADATA_WINDOW_TYPE, windowType)
                .setMetadata(METADATA_OWNER_UID, ownerUid)
                .setCallsite("WindowSurfaceController");
     ......
}

看一下Builder 雏逾,然后由于需要Parcelable序列化嘉裤,所以這里實(shí)現(xiàn)了Creator<T>接口。

//http://aosp.opersys.com/xref/android-12.0.0_r2/xref/frameworks/base/core/java/android/view/SurfaceControl.java

  /**
     * Builder class for {@link SurfaceControl} objects.
     *
     * By default the surface will be hidden, and have "unset" bounds, meaning it can
     * be as large as the bounds of its parent if a buffer or child so requires.
     *
     * It is necessary to set at least a name via {@link Builder#setName}
     */
    public static class Builder {
        private SurfaceSession mSession;
        private int mFlags = HIDDEN;
        private int mWidth;
        private int mHeight;
        private int mFormat = PixelFormat.OPAQUE;
        private String mName;
        private WeakReference<View> mLocalOwnerView;
        private SurfaceControl mParent;
        private SparseIntArray mMetadata;
        private String mCallsite = "SurfaceControl.Builder";

        /**
         * Begin building a SurfaceControl with a given {@link SurfaceSession}.
         *
         * @param session The {@link SurfaceSession} with which to eventually construct the surface.
         * @hide
         */
        public Builder(SurfaceSession session) {
            mSession = session;
        }

        /**
         * Begin building a SurfaceControl.
         */
        public Builder() {
        }

        /**
         * Construct a new {@link SurfaceControl} with the set parameters. The builder
         * remains valid.
         */
        @NonNull
        public SurfaceControl build() {
            if (mWidth < 0 || mHeight < 0) {
                throw new IllegalStateException(
                        "width and height must be positive or unset");
            }
            if ((mWidth > 0 || mHeight > 0) && (isEffectLayer() || isContainerLayer())) {
                throw new IllegalStateException(
                        "Only buffer layers can set a valid buffer size.");
            }

            if ((mFlags & FX_SURFACE_MASK) == FX_SURFACE_NORMAL) {
                setBLASTLayer();
            }

            return new SurfaceControl(
                    mSession, mName, mWidth, mHeight, mFormat, mFlags, mParent, mMetadata,
                    mLocalOwnerView, mCallsite);
        }

        /**
         * Set a debugging-name for the SurfaceControl.
         *
         * @param name A name to identify the Surface in debugging.
         */
        @NonNull
        public Builder setName(@NonNull String name) {
            mName = name;
            return this;
        }

        /**
         * Set the local owner view for the surface. This view is only
         * valid in the same process and is not transferred in an IPC.
         *
         * Note: This is used for cases where we want to know the view
         * that manages the surface control while intercepting reparenting.
         * A specific example is InlineContentView which exposes is surface
         * control for reparenting as a way to implement clipping of several
         * InlineContentView instances within a certain area.
         *
         * @param view The owner view.
         * @return This builder.
         *
         * @hide
         */
        @NonNull
        public Builder setLocalOwnerView(@NonNull View view) {
            mLocalOwnerView = new WeakReference<>(view);
            return this;
        }

        /**
         * Set the initial size of the controlled surface's buffers in pixels.
         *
         * @param width The buffer width in pixels.
         * @param height The buffer height in pixels.
         */
        @NonNull
        public Builder setBufferSize(@IntRange(from = 0) int width,
                @IntRange(from = 0) int height) {
            if (width < 0 || height < 0) {
                throw new IllegalArgumentException(
                        "width and height must be positive");
            }
            mWidth = width;
            mHeight = height;
            // set this as a buffer layer since we are specifying a buffer size.
            return setFlags(FX_SURFACE_NORMAL, FX_SURFACE_MASK);
        }

        private void unsetBufferSize() {
            mWidth = 0;
            mHeight = 0;
        }

        /**
         * Set the pixel format of the controlled surface's buffers, using constants from
         * {@link android.graphics.PixelFormat}.
         */
        @NonNull
        public Builder setFormat(@PixelFormat.Format int format) {
            mFormat = format;
            return this;
        }

        /**
         * Specify if the app requires a hardware-protected path to
         * an external display sync. If protected content is enabled, but
         * such a path is not available, then the controlled Surface will
         * not be displayed.
         *
         * @param protectedContent Whether to require a protected sink.
         * @hide
         */
        @NonNull
        public Builder setProtected(boolean protectedContent) {
            if (protectedContent) {
                mFlags |= PROTECTED_APP;
            } else {
                mFlags &= ~PROTECTED_APP;
            }
            return this;
        }

        /**
         * Specify whether the Surface contains secure content. If true, the system
         * will prevent the surfaces content from being copied by another process. In
         * particular screenshots and VNC servers will be disabled. This is however
         * not a complete prevention of readback as {@link #setProtected}.
         * @hide
         */
        @NonNull
        public Builder setSecure(boolean secure) {
            if (secure) {
                mFlags |= SECURE;
            } else {
                mFlags &= ~SECURE;
            }
            return this;
        }

        /**
         * Indicates whether the surface must be considered opaque,
         * even if its pixel format is set to translucent. This can be useful if an
         * application needs full RGBA 8888 support for instance but will
         * still draw every pixel opaque.
         * <p>
         * This flag only determines whether opacity will be sampled from the alpha channel.
         * Plane-alpha from calls to setAlpha() can still result in blended composition
         * regardless of the opaque setting.
         *
         * Combined effects are (assuming a buffer format with an alpha channel):
         * <ul>
         * <li>OPAQUE + alpha(1.0) == opaque composition
         * <li>OPAQUE + alpha(0.x) == blended composition
         * <li>OPAQUE + alpha(0.0) == no composition
         * <li>!OPAQUE + alpha(1.0) == blended composition
         * <li>!OPAQUE + alpha(0.x) == blended composition
         * <li>!OPAQUE + alpha(0.0) == no composition
         * </ul>
         * If the underlying buffer lacks an alpha channel, it is as if setOpaque(true)
         * were set automatically.
         * @param opaque Whether the Surface is OPAQUE.
         */
        @NonNull
        public Builder setOpaque(boolean opaque) {
            if (opaque) {
                mFlags |= OPAQUE;
            } else {
                mFlags &= ~OPAQUE;
            }
            return this;
        }

        /**
         * Set the initial visibility for the SurfaceControl.
         *
         * @param hidden Whether the Surface is initially HIDDEN.
         * @hide
         */
        @NonNull
        public Builder setHidden(boolean hidden) {
            if (hidden) {
                mFlags |= HIDDEN;
            } else {
                mFlags &= ~HIDDEN;
            }
            return this;
        }

        /**
         * Set a parent surface for our new SurfaceControl.
         *
         * Child surfaces are constrained to the onscreen region of their parent.
         * Furthermore they stack relatively in Z order, and inherit the transformation
         * of the parent.
         *
         * @param parent The parent control.
         */
        @NonNull
        public Builder setParent(@Nullable SurfaceControl parent) {
            mParent = parent;
            return this;
        }

        /**
         * Sets a metadata int.
         *
         * @param key metadata key
         * @param data associated data
         * @hide
         */
        public Builder setMetadata(int key, int data) {
            if (mMetadata == null) {
                mMetadata = new SparseIntArray();
            }
            mMetadata.put(key, data);
            return this;
        }

        /**
         * Indicate whether an 'EffectLayer' is to be constructed.
         *
         * An effect layer behaves like a container layer by default but it can support
         * color fill, shadows and/or blur. These layers will not have an associated buffer.
         * When created, this layer has no effects set and will be transparent but the caller
         * can render an effect by calling:
         *  - {@link Transaction#setColor(SurfaceControl, float[])}
         *  - {@link Transaction#setBackgroundBlurRadius(SurfaceControl, int)}
         *  - {@link Transaction#setShadowRadius(SurfaceControl, float)}
         *
         * @hide
         */
        public Builder setEffectLayer() {
            mFlags |= NO_COLOR_FILL;
            unsetBufferSize();
            return setFlags(FX_SURFACE_EFFECT, FX_SURFACE_MASK);
        }

        /**
         * A convenience function to create an effect layer with a default color fill
         * applied to it. Currently that color is black.
         *
         * @hide
         */
        public Builder setColorLayer() {
            unsetBufferSize();
            return setFlags(FX_SURFACE_EFFECT, FX_SURFACE_MASK);
        }

        private boolean isEffectLayer() {
            return  (mFlags & FX_SURFACE_EFFECT) == FX_SURFACE_EFFECT;
        }

        /**
         * @hide
         */
        public Builder setBLASTLayer() {
            return setFlags(FX_SURFACE_BLAST, FX_SURFACE_MASK);
        }

        /**
         * Indicates whether a 'ContainerLayer' is to be constructed.
         *
         * Container layers will not be rendered in any fashion and instead are used
         * as a parent of renderable layers.
         *
         * @hide
         */
        public Builder setContainerLayer() {
            unsetBufferSize();
            return setFlags(FX_SURFACE_CONTAINER, FX_SURFACE_MASK);
        }

        private boolean isContainerLayer() {
            return  (mFlags & FX_SURFACE_CONTAINER) == FX_SURFACE_CONTAINER;
        }

        /**
         * Set 'Surface creation flags' such as {@link #HIDDEN}, {@link #SECURE}.
         *
         * TODO: Finish conversion to individual builder methods?
         * @param flags The combined flags
         * @hide
         */
        public Builder setFlags(int flags) {
            mFlags = flags;
            return this;
        }

        /**
         * Sets the callsite this SurfaceControl is constructed from.
         *
         * @param callsite String uniquely identifying callsite that created this object. Used for
         *                 leakage tracking.
         * @hide
         */
        public Builder setCallsite(String callsite) {
            mCallsite = callsite;
            return this;
        }

        private Builder setFlags(int flags, int mask) {
            mFlags = (mFlags & ~mask) | flags;
            return this;
        }
    }


    public static final @android.annotation.NonNull Creator<SurfaceControl> CREATOR
            = new Creator<SurfaceControl>() {
        public SurfaceControl createFromParcel(Parcel in) {
            return new SurfaceControl(in);
        }

        public SurfaceControl[] newArray(int size) {
            return new SurfaceControl[size];
        }
    };

build()中調(diào)用了

 return new SurfaceControl(
                    mSession, mName, mWidth, mHeight, mFormat, mFlags, mParent, mMetadata,
                    mLocalOwnerView, mCallsite);

然后走到SurfaceControl中的SurfaceControl(SurfaceSession session, String name, int w, int h, int format, int flags,SurfaceControl parent, SparseIntArray metadata, WeakReference<View> localOwnerView,String callsite)

// http://aosp.opersys.com/xref/android-12.0.0_r2/xref/frameworks/base/core/java/android/view/SurfaceControl.java#1408

  private static native long nativeCreate(SurfaceSession session, String name,
            int w, int h, int format, int flags, long parentObject, Parcel metadata)
            throws OutOfResourcesException;

  private SurfaceControl(SurfaceSession session, String name, int w, int h, int format, int flags,
            SurfaceControl parent, SparseIntArray metadata, WeakReference<View> localOwnerView,
            String callsite)
                    throws OutOfResourcesException, IllegalArgumentException {
    ......

    mNativeObject = nativeCreate(session, name, w, h, format, flags,
                    parent != null ? parent.mNativeObject : 0, metaParcel);
    ......

}

這里調(diào)用后走到JNI中校套。這里會(huì)調(diào)用client->createSurfaceChecked(String8(name.c_str()), w, h, format, &surface,flags, parentHandle, std::move(metadata));价脾。

//http://aosp.opersys.com/xref/android-12.0.0_r2/xref/frameworks/base/core/jni/android_view_SurfaceControl.cpp
static jlong nativeCreate(JNIEnv* env, jclass clazz, jobject sessionObj,
        jstring nameStr, jint w, jint h, jint format, jint flags, jlong parentObject,
        jobject metadataParcel) {
    ScopedUtfChars name(env, nameStr);
    sp<SurfaceComposerClient> client;
    if (sessionObj != NULL) {
        client = android_view_SurfaceSession_getClient(env, sessionObj);
    } else {
        client = SurfaceComposerClient::getDefault();
    }
    SurfaceControl *parent = reinterpret_cast<SurfaceControl*>(parentObject);
    sp<SurfaceControl> surface;
   ...

    status_t err = client->createSurfaceChecked(String8(name.c_str()), w, h, format, &surface,
                                                flags, parentHandle, std::move(metadata));
   ...

    surface->incStrong((void *)nativeCreate);
    return reinterpret_cast<jlong>(surface.get());
}

走到SurfaceComposerClient::createSurfaceChecked之后,最終調(diào)用了mClient->createSurface(name, w, h, format, flags, parentHandle, std::move(metadata),&handle, &gbp, &id, &transformHint);笛匙。

// frameworks/native/libs/gui/SurfaceComposerClient.cpp
status_t SurfaceComposerClient::createSurfaceChecked(const String8& name, uint32_t w, uint32_t h,
                                                     PixelFormat format,
                                                     sp<SurfaceControl>* outSurface, uint32_t flags,
                                                     const sp<IBinder>& parentHandle,
                                                     LayerMetadata metadata,
                                                     uint32_t* outTransformHint) {
    sp<SurfaceControl> sur;
    status_t err = mStatus;

    if (mStatus == NO_ERROR) {
        sp<IBinder> handle;
        sp<IGraphicBufferProducer> gbp;

        uint32_t transformHint = 0;
        int32_t id = -1;
        err = mClient->createSurface(name, w, h, format, flags, parentHandle, std::move(metadata),
                                     &handle, &gbp, &id, &transformHint);

        if (outTransformHint) {
            *outTransformHint = transformHint;
        }
        ALOGE_IF(err, "SurfaceComposerClient::createSurface error %s", strerror(-err));
        if (err == NO_ERROR) {
            *outSurface =
                    new SurfaceControl(this, handle, gbp, id, w, h, format, transformHint, flags);
        }
    }
    return err;
}

而mClient 在前篇分析過侨把,是onFirstRef的時(shí)候初始化,而且是以SurfaceFlinger為參數(shù)妹孙,創(chuàng)建的一個(gè)Client對象秋柄,也就是持有SurfaceFlinger

//frameworks/native/libs/gui/SurfaceComposerClient.cpp

void SurfaceComposerClient::onFirstRef() {
    sp<ISurfaceComposer> sf(ComposerService::getComposerService());
    if (sf != nullptr && mStatus == NO_INIT) {
        sp<ISurfaceComposerClient> conn;
        conn = sf->createConnection();
        if (conn != nullptr) {
            mClient = conn;
            mStatus = NO_ERROR;
        }
    }
}
// frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp
sp<ISurfaceComposerClient> SurfaceFlinger::createConnection() {
    const sp<Client> client = new Client(this);
    return client->initCheck() == NO_ERROR ? client : nullptr;
}

Client::createSurface,會(huì)調(diào)用mFlinger->createLayer蠢正。


//frameworks/native/services/surfaceflinger/Client.cpp
class Client : public BnSurfaceComposerClient
{
public:
    explicit Client(const sp<SurfaceFlinger>& flinger);
    ~Client() = default;

    status_t initCheck() const;
   .....
// constant
  sp<SurfaceFlinger> mFlinger;
   .....
}

//http://aosp.opersys.com/xref/android-12.0.0_r2/xref/frameworks/native/services/surfaceflinger/Client.cpp
status_t Client::createSurface(const String8& name, uint32_t w, uint32_t h, PixelFormat format,
                               uint32_t flags, const sp<IBinder>& parentHandle,
                               LayerMetadata metadata, sp<IBinder>* handle,
                               sp<IGraphicBufferProducer>* gbp, int32_t* outLayerId,
                               uint32_t* outTransformHint) {
    // We rely on createLayer to check permissions.
    return mFlinger->createLayer(name, this, w, h, format, flags, std::move(metadata), handle, gbp,
                                 parentHandle, outLayerId, nullptr, outTransformHint);
}

createLayer具體做了什么呢骇笔?

//http://aosp.opersys.com/xref/android-12.0.0_r2/xref/frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp
status_t SurfaceFlinger::createLayer(const String8& name, const sp<Client>& client, uint32_t w,
                                     uint32_t h, PixelFormat format, uint32_t flags,
                                     LayerMetadata metadata, sp<IBinder>* handle,
                                     sp<IGraphicBufferProducer>* gbp,
                                     const sp<IBinder>& parentHandle, int32_t* outLayerId,
                                     const sp<Layer>& parentLayer, uint32_t* outTransformHint) {
    if (int32_t(w|h) < 0) {
        ALOGE("createLayer() failed, w or h is negative (w=%d, h=%d)",
                int(w), int(h));
        return BAD_VALUE;
    }

    ALOG_ASSERT(parentLayer == nullptr || parentHandle == nullptr,
            "Expected only one of parentLayer or parentHandle to be non-null. "
            "Programmer error?");

    status_t result = NO_ERROR;

    sp<Layer> layer;

    std::string uniqueName = getUniqueLayerName(name.string());

    switch (flags & ISurfaceComposerClient::eFXSurfaceMask) {
        case ISurfaceComposerClient::eFXSurfaceBufferQueue:
        case ISurfaceComposerClient::eFXSurfaceBufferState: {
            result = createBufferStateLayer(client, std::move(uniqueName), w, h, flags,
                                            std::move(metadata), handle, &layer);
            std::atomic<int32_t>* pendingBufferCounter = layer->getPendingBufferCounter();
            if (pendingBufferCounter) {
                std::string counterName = layer->getPendingBufferCounterName();
                mBufferCountTracker.add((*handle)->localBinder(), counterName,
                                        pendingBufferCounter);
            }
        } break;
        case ISurfaceComposerClient::eFXSurfaceEffect:
            // check if buffer size is set for color layer.
            if (w > 0 || h > 0) {
                ALOGE("createLayer() failed, w or h cannot be set for color layer (w=%d, h=%d)",
                      int(w), int(h));
                return BAD_VALUE;
            }

            result = createEffectLayer(client, std::move(uniqueName), w, h, flags,
                                       std::move(metadata), handle, &layer);
            break;
        case ISurfaceComposerClient::eFXSurfaceContainer:
            // check if buffer size is set for container layer.
            if (w > 0 || h > 0) {
                ALOGE("createLayer() failed, w or h cannot be set for container layer (w=%d, h=%d)",
                      int(w), int(h));
                return BAD_VALUE;
            }
            result = createContainerLayer(client, std::move(uniqueName), w, h, flags,
                                          std::move(metadata), handle, &layer);
            break;
        default:
            result = BAD_VALUE;
            break;
    }

    if (result != NO_ERROR) {
        return result;
    }

    bool addToRoot = callingThreadHasUnscopedSurfaceFlingerAccess();
    result = addClientLayer(client, *handle, *gbp, layer, parentHandle, parentLayer, addToRoot,
                            outTransformHint);
    if (result != NO_ERROR) {
        return result;
    }
    mInterceptor->saveSurfaceCreation(layer);

    setTransactionFlags(eTransactionNeeded);
    *outLayerId = layer->sequence;
    return result;
}
  1. 檢查寬高數(shù)值是否合法,寬/高不應(yīng)為負(fù)值嚣崭;

  2. 檢查parentLayer/parentHandle笨触,對于我們的應(yīng)用來說,沒有parent雹舀,兩者都為 null芦劣;

  3. 獲取一個(gè)獨(dú)一無二的Layer name,即不能有重名的Layer;

  4. 根據(jù)flags去創(chuàng)建對應(yīng)類型的Layer ? createBufferStateLayer or createEffectLayer or createContainerLayer

最后所有在Client中獲取到的Layer信息都被封裝到一個(gè)SurfaceControl對象中说榆,并返回給應(yīng)用來使用虚吟。

//frameworks/native/libs/gui/SurfaceComposerClient.cpp
if (err == NO_ERROR) {
   *outSurface =
       new SurfaceControl(this, handle, gbp, id, w, h, format, transformHint, flags);
}

那么應(yīng)用側(cè)又是如何拿到這個(gè)Layer的操作接口呢?
通過Surface的copyFrom方法從SurfaceControl來創(chuàng)建這個(gè)Surface

// frameworks/base/core/java/android/view/ViewRootImpl.java
private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility,
            boolean insetsPending) throws RemoteException {
     ......
     int relayoutResult = mWindowSession.relayout(mWindow, mSeq, params,
                (int) (mView.getMeasuredWidth() * appScale + 0.5f),
                (int) (mView.getMeasuredHeight() * appScale + 0.5f), viewVisibility,
                insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0, frameNumber,
                mTmpFrame, mTmpRect, mTmpRect, mTmpRect, mPendingBackDropFrame,
                mPendingDisplayCutout, mPendingMergedConfiguration, 
//這里的mSurfaceControl是從WMS傳來的
                mSurfaceControl, mTempInsets, 
                mTempControls, mSurfaceSize, mBlastSurfaceControl);
     ......
     if (mSurfaceControl.isValid()) {
            if (!useBLAST()) {
//通過Surface的copyFrom方法從SurfaceControl來創(chuàng)建這個(gè)Surface
                mSurface.copyFrom(mSurfaceControl);
            }
     ......
}

至此签财,Android應(yīng)用程序請求SurfaceFlinger服務(wù) 創(chuàng)建 一個(gè)Surface的流程分析完成串慰。

下面來看看問題二:Android應(yīng)用程序是如何請求SurfaceFlinger服務(wù) 渲染 一個(gè)Surface的?這個(gè)問題換個(gè)更通俗易懂的說法就是:Surface如何通過SurfaceFlinger服務(wù)顯示到屏幕上唱蒸。
一般把Surface稱為畫布邦鲫,Layer稱為圖層。
Surface是應(yīng)用側(cè)創(chuàng)建的油宜,LayerSurfaceFlinger服務(wù)端創(chuàng)建的掂碱。
前面分析Surface的創(chuàng)建過程中可以看到怜姿, WindowManagerService中創(chuàng)建SurfaceControl ,然后實(shí)際走到nativeCreate,最終走到了SurfaceFlinger::createLayer疼燥,創(chuàng)建Layer沧卢,最終調(diào)用的是SurfaceFlinger::createLayer,最后所有在Client中獲取到的Layer信息都被封裝到一個(gè)SurfaceControl對象中醉者,并返回給應(yīng)用來使用但狭,然后通過應(yīng)用側(cè)通過mSurface.copyFrom(mSurfaceControl);創(chuàng)建Surface
那么拿到Surface對象后怎么就能拿到幀緩沖區(qū)的操作接口呢撬即?
關(guān)鍵看下面這兩個(gè)native 函數(shù)立磁。

// frameworks/base/core/java/android/view/Surface.java
public class Surface implements Parcelable {
    ......
  private static native long nativeLockCanvas(long nativeObject, Canvas canvas, Rect dirty)
            throws OutOfResourcesException;
    private static native void nativeUnlockCanvasAndPost(long nativeObject, Canvas canvas);
    ......    
}


//frameworks/base/core/jni/android_view_Surface.cpp
static jlong nativeLockCanvas(JNIEnv* env, jclass clazz,
        jlong nativeObject, jobject canvasObj, jobject dirtyRectObj) {
  ...
    status_t err = surface->lock(&buffer, dirtyRectPtr);
  ...
    }

   
static void nativeUnlockCanvasAndPost(JNIEnv* env, jclass clazz,
        jlong nativeObject, jobject canvasObj) {
   ...
    // unlock surface
    status_t err = surface->unlockAndPost();
    ...
}

這兩個(gè)native會(huì)分別調(diào)用dequeueBufferqueueBuffer

// http://aosp.opersys.com/xref/android-12.0.0_r2/xref/frameworks/native/libs/gui/Surface.cpp
status_t Surface::lock(
        ANativeWindow_Buffer* outBuffer, ARect* inOutDirtyBounds){
     ......
     status_t err = dequeueBuffer(&out, &fenceFd);
     ......
}
int Surface::dequeueBuffer(android_native_buffer_t** buffer, int* fenceFd) {
    ATRACE_CALL();
    ......
    status_t result = mGraphicBufferProducer->dequeueBuffer(&buf, &fence, reqWidth, reqHeight,
                                                            reqFormat, reqUsage, &mBufferAge,
                                                            enableFrameTimestamps ? &frameTimestamps
                                                                                  : nullptr);
    ......
}


status_t Surface::unlockAndPost()
{
   ...
    err = queueBuffer(mLockedBuffer.get(), fd);
   ...
}

int Surface::queueBuffer(android_native_buffer_t* buffer, int fenceFd) {
...
    status_t err = mGraphicBufferProducer->queueBuffer(i, input, &output);
...
 
}

調(diào)用dequeueBuffer后剥槐,應(yīng)用拿到了它繪制界面所需的“畫布”唱歧。
知道了應(yīng)用是如何拿到“畫布”的,接下來我們來看下應(yīng)用是如何在繪畫完一幀后來提交數(shù)據(jù)的:
performTraversals函數(shù)中獲取到了操作幀緩沖區(qū)的Surface對象粒竖,這個(gè)Surface對象會(huì)通過RenderProxy傳遞給RenderThread, 一些關(guān)健代碼如下:
performTraversals里初始化RenderThread時(shí)會(huì)把Surface對象傳過去:

private void performTraversals() {
    ......
    if (mAttachInfo.mThreadedRenderer != null) {
    try {
        hwInitialized = mAttachInfo.mThreadedRenderer.initialize(mSurface);
        ........
    } 
    ......
}

在ThreadedRenderer的初始化中調(diào)用了setSurface颅崩,這個(gè)setSurface函數(shù)會(huì)通過JNI調(diào)到native層:

//http://aosp.opersys.com/xref/android-12.0.0_r2/xref/frameworks/base/core/java/android/view/ThreadedRenderer.java

public final class ThreadedRenderer extends HardwareRenderer {
    boolean initialize(Surface surface) throws OutOfResourcesException {
        ......
        setSurface(surface);
        ......
    }

    @Override
    public void setSurface(Surface surface) {
        // TODO: Do we ever pass a non-null but isValid() = false surface?
        // This is here to be super conservative for ViewRootImpl
        if (surface != null && surface.isValid()) {
            super.setSurface(surface);
        } else {
            super.setSurface(null);
        }
    }
}

走到HardwareRenderer中的nSetSurface

//http://aosp.opersys.com/xref/android-12.0.0_r2/xref/frameworks/base/graphics/java/android/graphics/HardwareRenderer.java
    private static native void nSetSurface(long nativeProxy, Surface window, boolean discardBuffer);

   public void setSurface(@Nullable Surface surface) {
        setSurface(surface, false);
    }

    public void setSurface(@Nullable Surface surface, boolean discardBuffer) {
        if (surface != null && !surface.isValid()) {
            throw new IllegalArgumentException("Surface is invalid. surface.isValid() == false.");
        }
        nSetSurface(mNativeProxy, surface, discardBuffer);
    }
//http://aosp.opersys.com/xref/android-12.0.0_r2/xref/frameworks/base/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
static void android_view_ThreadedRenderer_setSurface(JNIEnv* env, jobject clazz,
        jlong proxyPtr, jobject jsurface, jboolean discardBuffer) {
    RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
   ...
    proxy->setSurface(window, enableTimeout);
    ...
}

最終可以看到setSurface是通過RenderProxy這個(gè)對象向RenderThread的消息隊(duì)列中post了一個(gè)消息蕊苗,在這個(gè)消息的處理中會(huì)調(diào)用Context的setSurface, 這里的mContext是CanvasContext.

//RenderProxy.cpp 
void RenderProxy::setSurface(ANativeWindow* window, bool enableTimeout) {
    ANativeWindow_acquire(window);
    mRenderThread.queue().post([this, win = window, enableTimeout]() mutable {
        mContext->setSurface(win, enableTimeout);
        ANativeWindow_release(win);
    });
}

首先App每次開始繪畫都是收到一個(gè)vsync信號(hào)才會(huì)開始繪圖沿后,應(yīng)用是通過Choreographer來感知vsync信號(hào), 在ViewRootImpl里向Choreographer注冊一個(gè)callback, 每當(dāng)有vsync信號(hào)來時(shí)會(huì)執(zhí)行mTraversalRunnable:

//ViewRootImpl.java
 void scheduleTraversals() {
     if (!mTraversalScheduled) {
         ......
         mChoreographer.postCallback(
         Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);//注冊vsync的回調(diào)
         ......
     }
 }
 final class TraversalRunnable implements Runnable {
     @Override
     public void run() {
        doTraversal();//每次vsync到時(shí)調(diào)用該函數(shù)
     }
 }

而doTraversal()主要是調(diào)用performTraversals()這個(gè)函數(shù),performTraversals里會(huì)調(diào)用到draw()函數(shù):

//ViewRootImpl.java
 private boolean draw(boolean fullRedrawNeeded) {
     ......
      if (!dirty.isEmpty() || mIsAnimating || accessibilityFocusDirty) {
          ......
          mAttachInfo.mThreadedRenderer.draw(mView, mAttachInfo, this);//這里傳下去的mView就是DecorView
          ......
      }
     ......
 }

上面的draw()函數(shù)進(jìn)一步調(diào)用了ThreadedRenderer的draw:

//ThreadedRenderer.java
void draw(View view, AttachInfo attachInfo, DrawCallbacks callbacks) {
   ......
   updateRootDisplayList(view, callbacks);
   ......
}
private void updateRootDisplayList(View view, DrawCallbacks callbacks) {
   Trace.traceBegin(Trace.TRACE_TAG_VIEW, "Record View#draw()");//這里有個(gè)trace朽砰,我們可以在systrace中觀察到它
   ......
   updateViewTreeDisplayList(view);
   ......
}
private void updateViewTreeDisplayList(View view) {
   ......
   view.updateDisplayListIfDirty();//這里開始調(diào)用DecorView的updateDisplayListIfDirty
   ......
}

接下來代碼調(diào)用到DecorView的基類View.java:

public RenderNode updateDisplayListIfDirty() {
   ......
    final RecordingCanvas canvas = renderNode.beginRecording(width, height);//這里開始displaylist的record
    ......
    draw(canvas);
    ......
    
}

上面的RecordingCanvas就是扮演一個(gè)繪圖指令的記錄員角色尖滚,它會(huì)將這個(gè)View通過draw函數(shù)繪制的指令以displaylist形式記錄下來,Android里的View和RenderNode是類似的概念瞧柔,View代表的是實(shí)體在空間結(jié)構(gòu)上的存在漆弄,而RenderNode代表它在界面呈現(xiàn)上的存在。這樣的設(shè)計(jì)可以讓存在和呈現(xiàn)進(jìn)行分離造锅,便于實(shí)現(xiàn)同一存在不同狀態(tài)下呈現(xiàn)也不同置逻。

在Android的設(shè)計(jì)里View會(huì)對應(yīng)一個(gè)RenderNode, RenderNode里的一個(gè)重要數(shù)據(jù)結(jié)構(gòu)是DisplayList, 每個(gè)DisplayList都會(huì)包含一系列DisplayListData. 這些DisplayList也會(huì)同樣以樹形結(jié)構(gòu)組織在一起。

當(dāng)UI線程完成它的繪制工作后备绽,它工作的產(chǎn)物是一堆DisplayListData。UI線程并沒有將應(yīng)用設(shè)計(jì)的View轉(zhuǎn)換成像素點(diǎn)數(shù)據(jù)鬓催,而是將每個(gè)View的繪圖指令存入了內(nèi)存中肺素,我們通常稱這些繪圖指令為DisplayList。

//RecordingCanvas.h
class DisplayListData final {
    ......
    void drawPath(const SkPath&, const SkPaint&);
    void drawRect(const SkRect&, const SkPaint&);
    void drawRegion(const SkRegion&, const SkPaint&);
    void drawOval(const SkRect&, const SkPaint&);
    void drawArc(const SkRect&, SkScalar, SkScalar, bool, const SkPaint&);
    ......
    template <typename T, typename... Args>
    void* push(size_t, Args&&...);
    ......
    SkAutoTMalloc<uint8_t> fBytes;
    ......
}

當(dāng)所有的View的displaylist建立完成后宇驾,代碼走到RenderProxy中倍靡,然后走到DrawFrameTask中。

//RenderProxy.cpp 
int RenderProxy::syncAndDrawFrame() {
    return mDrawFrameTask.drawFrame();
}
//DrawFrameTask.cpp

int DrawFrameTask::drawFrame() {
    LOG_ALWAYS_FATAL_IF(!mContext, "Cannot drawFrame with no CanvasContext!");

    mSyncResult = SyncResult::OK;
    mSyncQueued = systemTime(SYSTEM_TIME_MONOTONIC);
    postAndWait();

    return mSyncResult;
}

void DrawFrameTask::postAndWait() {
    AutoMutex _lock(mLock);
    mRenderThread->queue().post([this]() { run(); });//丟任務(wù)到RenderThread線程
    mSignal.wait(mLock);
}

UI線程的工作到此結(jié)束课舍,它丟了一個(gè)叫DrawFrameTask的任務(wù)到RenderThread線程中去塌西,之后畫面繪制的工作轉(zhuǎn)移到RenderThread中來他挎。

// http://aosp.opersys.com/xref/android-12.0.0_r2/xref/frameworks/base/libs/hwui/renderthread/DrawFrameTask.cpp
void DrawFrameTask::run() {
    CanvasContext* context = mContext;
    .....
    dequeueBufferDuration = context->draw();
    .....
}
//CanvasContext.cpp
void CanvasContext::draw() {
    ......
    Frame frame = mRenderPipeline->getFrame();//這句會(huì)調(diào)用到Surface的dequeueBuffer
    ......
     bool drew = mRenderPipeline->draw(frame, windowDirty, dirty, mLightGeometry, &mLayerUpdateQueue,
                                      mContentDrawBounds, mOpaque, mLightInfo, mRenderNodes,
                                      &(profiler()));
    ......
    waitOnFences();
    ......
    bool didSwap =
            mRenderPipeline->swapBuffers(frame, drew, windowDirty, mCurrentFrameInfo, &requireSwap);//這句會(huì)調(diào)用到Surface的queueBuffer
    ......
}

RenderPipeline,渲染管線捡需。在計(jì)算機(jī)圖形學(xué)中办桨,管線就是一系列過程。例如下圖就是一個(gè)OpenGL 管線站辉。


https://blog.csdn.net/wb175208/article/details/125455294

在這個(gè)函數(shù)中完成了三個(gè)重要的動(dòng)作呢撞,一個(gè)是通過getFrame調(diào)到了Surface的dequeueBuffer向SurfaceFlinger申請了畫布, 第二是通過mRenderPipeline->draw將畫面畫到申請到的畫布上饰剥, 第三是通過調(diào)mRenderPipeline->swapBuffers把畫布提交到SurfaceFlinger去顯示殊霞。

那么在mRenderPipeline->draw里是如何將displaylist翻譯成畫布上的像素點(diǎn)顏色的呢?

//http://aosp.opersys.com/xref/android-12.0.0_r2/xref/frameworks/base/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
bool SkiaOpenGLPipeline::draw(const Frame& frame, const SkRect& screenDirty, const SkRect& dirty,
                              const LightGeometry& lightGeometry,
                              LayerUpdateQueue* layerUpdateQueue, const Rect& contentDrawBounds,
                              bool opaque, const LightInfo& lightInfo,
                              const std::vector<sp<RenderNode>>& renderNodes,
                              FrameInfoVisualizer* profiler) {
      ......
      renderFrame(*layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface, SkMatrix::I());
      ......
 }

renderFrameImpl中會(huì)把在UI線程中記錄的displaylist重新“繪制”到skSurface中汰蓉,然后通過SkCanvas將其轉(zhuǎn)化為gl指令绷蹲, surface->getCanvas()->flush();
所以整體流程為:

首先是UI線程進(jìn)行measure, layout然后開始draw, 在draw的過程中會(huì)建立displaylist樹,將每個(gè)view應(yīng)該怎么畫記錄下來顾孽,然后通過RenderProxy把后續(xù)任務(wù)下達(dá)給RenderThread, RenderThread主要完成三個(gè)動(dòng)作祝钢,先通過Surface接口向Surfaceflinger申請buffer, 然后通過SkiaOpenGLPipline的draw方法把displaylist翻譯成GPU指令, 指揮GPU把指令變成像素點(diǎn)數(shù)據(jù)岩齿, 最后通過swapBuffer把數(shù)據(jù)提交給SurfaceFlinger, 完成一幀數(shù)據(jù)的繪制和提交太颤。

應(yīng)用提交buffer以后SurfaceFlinger會(huì)如何處理呢?又是如何提交到HWC Service去合成的呢盹沈?
首先響應(yīng)應(yīng)用queueBuffer的是一條binder線程龄章, 處理邏輯會(huì)走進(jìn)BufferQueueProducer.cpp。

//BufferQueueProducer.cpp
status_t BufferQueueProducer::queueBuffer(int slot,
        const QueueBufferInput &input, QueueBufferOutput *output) {
    ATRACE_CALL();
    ATRACE_BUFFER_INDEX(slot);
    ......
     if (frameAvailableListener != nullptr) {
        frameAvailableListener->onFrameAvailable(item);
     }
    ......
}

上面的frameAvailableListener是BufferQueueLayer

//BufferQueueLayer.cpp
void BufferQueueLayer::onFrameAvailable(const BufferItem& item) {
    ......
     mFlinger->signalLayerUpdate();//這里申請一下個(gè)vsync-sf信號(hào)
    ......
}

只要有l(wèi)ayer上幀乞封,那么就會(huì)申請下一次的vsync-sf信號(hào)做裙, 當(dāng)vsync-sf信號(hào)來時(shí)會(huì)調(diào)用onMessageReceived函數(shù)來處理幀數(shù)據(jù)。

void MessageQueue::Handler::dispatchInvalidate() {
    if ((android_atomic_or(eventMaskInvalidate, &mEventMask) & eventMaskInvalidate) == 0) {
        mQueue.mLooper->sendMessage(this, Message(MessageQueue::INVALIDATE));
    }
}

void MessageQueue::Handler::handleMessage(const Message& message) {
    switch (message.what) {
        case INVALIDATE:
            android_atomic_and(~eventMaskInvalidate, &mEventMask);
            mQueue.mFlinger->onMessageReceived(message.what);
            break;
        case REFRESH:
            android_atomic_and(~eventMaskRefresh, &mEventMask);
            mQueue.mFlinger->onMessageReceived(message.what);
            break;
    }
}

發(fā)送了一個(gè)INVALIDATE消息交給SF的onMessageReceive處理肃晚。
SF onMessageReceive 處理 INVALIDATE消息

// http://aosp.opersys.com/xref/android-12.0.0_r2/xref/frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp#1875
void SurfaceFlinger::onMessageReceived(int32_t what, int64_t vsyncId, nsecs_t expectedVSyncTime) {
    switch (what) {
        case MessageQueue::INVALIDATE: {
            onMessageInvalidate(vsyncId, expectedVSyncTime);
            break;
        }
        case MessageQueue::REFRESH: {
            onMessageRefresh();
            break;
        }
    }
}

走到SurfaceFlinger::onMessageInvalidate中锚贱。

//SurfaceFlinger.cpp
void SurfaceFlinger::onMessageInvalidate(nsecs_t expectedVSyncTime) {
    ATRACE_CALL();
    ......
        refreshNeeded |= handleMessageInvalidate();
    ......
    signalRefresh();//再次向消息隊(duì)列發(fā)送一個(gè)消息,消息到達(dá)時(shí)會(huì)調(diào)用onMessageRefresh
    ......
}
bool SurfaceFlinger::handleMessageInvalidate() {
    ATRACE_CALL();
    bool refreshNeeded = handlePageFlip();
    ......
}

在handleMessageInvalidate里一個(gè)比較重要的函數(shù)是handlePageFlip():

bool SurfaceFlinger::handlePageFlip()
{
    ATRACE_CALL();
    ......
    mDrawingState.traverse([&](Layer* layer) {
        if (layer->hasReadyFrame()) {
            frameQueued = true;
            if (layer->shouldPresentNow(expectedPresentTime)) {
                mLayersWithQueuedFrames.push_back(layer);
            } 
            .......
        } 
        ......
    });
    ......
        for (auto& layer : mLayersWithQueuedFrames) {
            if (layer->latchBuffer(visibleRegions, latchTime, expectedPresentTime)) {
                mLayersPendingRefresh.push_back(layer);
            }
            .......
        }
        ......
}

這里可以看出來关串,handlePageFlip里一個(gè)重要的工作是檢查所有的Layer是否有新buffer提交拧廊,如果有則調(diào)用其latchBuffer來處理

//BufferLayer.cpp
bool BufferLayer::latchBuffer(bool& recomputeVisibleRegions, nsecs_t latchTime,
                              nsecs_t expectedPresentTime) {
    ATRACE_CALL();
    ......
    status_t err = updateTexImage(recomputeVisibleRegions, latchTime, expectedPresentTime);
    ......
}

//BufferQueueLayer.cpp
status_t BufferQueueLayer::updateTexImage(bool& recomputeVisibleRegions, nsecs_t latchTime,
                                          nsecs_t expectedPresentTime) {
     ......
      status_t updateResult = mConsumer->updateTexImage(&r, expectedPresentTime, &mAutoRefresh,
                                                      &queuedBuffer, maxFrameNumberToAcquire);
     ......
}

//BufferLayerConsumer.cpp
status_t BufferLayerConsumer::updateTexImage(BufferRejecter* rejecter, nsecs_t expectedPresentTime,
                                             bool* autoRefresh, bool* queuedBuffer,
                                             uint64_t maxFrameNumber) {
    ATRACE_CALL();
    ......
    status_t err = acquireBufferLocked(&item, expectedPresentTime, maxFrameNumber);
    ......
}

status_t ConsumerBase::acquireBufferLocked(BufferItem *item,
        nsecs_t presentWhen, uint64_t maxFrameNumber) {
    ......
    status_t err = mConsumer->acquireBuffer(item, presentWhen, maxFrameNumber);
    ......
}

到這里onMessageReceived中的主要工作結(jié)束,在這個(gè)函數(shù)的處理中晋修,SurfaceFlinger主要是檢查每個(gè)Layer是否有新提交的buffer吧碾, 如果有則調(diào)用latchBuffer將每個(gè)BufferQueue中的Slot 通過acquireBuffer拿走。 之后拿走的buffer(Slot對應(yīng)的狀態(tài)是ACQUIRED狀態(tài))會(huì)被交由HWC Service處理墓卦,這部分是在onMessageRefresh里處理的,然后走到CompositionEngine倦春,再走到Output。

// SurfaceFlinger.cpp
void SurfaceFlinger::onMessageRefresh() {
    ATRACE_CALL();
    ......
    mCompositionEngine->present(refreshArgs);
    ......
}
// CompositionEngine.cpp
void CompositionEngine::present(CompositionRefreshArgs& args) {
    ATRACE_CALL();
    ......
    for (const auto& output : args.outputs) {
        output->present(args);
    }
    ......
}
// Output.cpp
void Output::present(const compositionengine::CompositionRefreshArgs& refreshArgs) {
    ATRACE_CALL();
    ......
    updateAndWriteCompositionState(refreshArgs);//告知HWC service有哪些layer要參與合成
    ......
    beginFrame();
    prepareFrame();
    ......
    finishFrame(refreshArgs);
    postFramebuffer();//這里會(huì)調(diào)用到HWC service的接口去present display合成畫面
}
void Output::postFramebuffer() {
    ......
    auto frame = presentAndGetFrameFences();
    ......
}

compositionengine::Output::FrameFences Output::presentAndGetFrameFences() {
    compositionengine::Output::FrameFences result;
    if (getState().usesClientComposition) {
        result.clientTargetAcquireFence = mRenderSurface->getClientTargetAcquireFence();
    }
    return result;
}

然后下面就涉及到HWC Service了。這個(gè)一般廠商會(huì)保密睁本,所以一般沒有公開的資料尿庐。
沿著代碼邏輯學(xué)習(xí)了應(yīng)用是如何申請到畫布、使用android的View系統(tǒng)如何繪圖呢堰、繪圖完成后如何提交buffer以及buffer提交以及Surfaceflinger如何處理抄瑟。但本章所述的邏輯均是指通過android的View系統(tǒng)繪圖的過程,也可以稱其為hwui繪圖流程暮胧,從上面代碼流程可以知道锐借,hwui的繪圖流程是被vsync信號(hào)觸發(fā)的,開始于vsync信號(hào)到達(dá)UI線程調(diào)用performTraversals函數(shù)往衷, hwui的畫面更新是被vsync信號(hào)驅(qū)動(dòng)的钞翔。

參考鏈接:
Android圖形渲染原理下
Android圖形系統(tǒng)(七)-app請求SurfaceFlinger創(chuàng)建Surface過程
Android圖形系統(tǒng)(十)-SurfaceFlinger啟動(dòng)及圖層合成送顯過程
Android應(yīng)用程序請求SurfaceFlinger服務(wù)渲染Surface的過程分析
Android畫面顯示流程分析(2)
Android畫面顯示流程分析(4)
Android 12(S) 圖像顯示系統(tǒng) - createSurface的流程(五)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市席舍,隨后出現(xiàn)的幾起案子布轿,更是在濱河造成了極大的恐慌来颤,老刑警劉巖汰扭,帶你破解...
    沈念sama閱讀 212,222評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件庵佣,死亡現(xiàn)場離奇詭異,居然都是意外死亡喉脖,警方通過查閱死者的電腦和手機(jī)性锭,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,455評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門怎棱,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人绷跑,你說我怎么就攤上這事拳恋。” “怎么了砸捏?”我有些...
    開封第一講書人閱讀 157,720評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵谬运,是天一觀的道長。 經(jīng)常有香客問我带膜,道長吩谦,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,568評(píng)論 1 284
  • 正文 為了忘掉前任膝藕,我火速辦了婚禮式廷,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘芭挽。我一直安慰自己滑废,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,696評(píng)論 6 386
  • 文/花漫 我一把揭開白布袜爪。 她就那樣靜靜地躺著蠕趁,像睡著了一般。 火紅的嫁衣襯著肌膚如雪辛馆。 梳的紋絲不亂的頭發(fā)上俺陋,一...
    開封第一講書人閱讀 49,879評(píng)論 1 290
  • 那天豁延,我揣著相機(jī)與錄音,去河邊找鬼腊状。 笑死诱咏,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的缴挖。 我是一名探鬼主播袋狞,決...
    沈念sama閱讀 39,028評(píng)論 3 409
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼映屋!你這毒婦竟也來了苟鸯?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,773評(píng)論 0 268
  • 序言:老撾萬榮一對情侶失蹤棚点,失蹤者是張志新(化名)和其女友劉穎早处,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體乙濒,經(jīng)...
    沈念sama閱讀 44,220評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡陕赃,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,550評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了颁股。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片么库。...
    茶點(diǎn)故事閱讀 38,697評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖甘有,靈堂內(nèi)的尸體忽然破棺而出诉儒,到底是詐尸還是另有隱情,我是刑警寧澤亏掀,帶...
    沈念sama閱讀 34,360評(píng)論 4 332
  • 正文 年R本政府宣布忱反,位于F島的核電站,受9級(jí)特大地震影響滤愕,放射性物質(zhì)發(fā)生泄漏温算。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,002評(píng)論 3 315
  • 文/蒙蒙 一间影、第九天 我趴在偏房一處隱蔽的房頂上張望注竿。 院中可真熱鬧,春花似錦魂贬、人聲如沸巩割。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,782評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽宣谈。三九已至,卻和暖如春键科,著一層夾襖步出監(jiān)牢的瞬間闻丑,已是汗流浹背漩怎。 一陣腳步聲響...
    開封第一講書人閱讀 32,010評(píng)論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留嗦嗡,地道東北人扬卷。 一個(gè)月前我還...
    沈念sama閱讀 46,433評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像酸钦,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子咱枉,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,587評(píng)論 2 350

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