Android源码 windowManager

前言

嗯嗯 windowManager是用来干嘛的?
从名字上可以看出windowManger是窗口管理的意思, 主要

window

/**
 * Abstract base class for a top-level window look and behavior policy.  An
 * instance of this class should be used as the top-level view added to the
 * window manager. It provides standard UI policies such as a background,   title
 * area, default key processing, etc.
 *
 * <p>The only existing implementation of this abstract class is
 * android.view.PhoneWindow, which you should instantiate when needing a
 * Window.
 */
public abstract class Window {
/** Flag for the "options panel" feature.  This is enabled by default. */
public static final int FEATURE_OPTIONS_PANEL = 0;
/** Flag for the "no title" feature, turning off the title at the top
 *  of the screen. */
public static final int FEATURE_NO_TITLE = 1;

   //....

 }

看下类的解释 :一个顶级窗口和行为策略的抽象基类, 实例作为顶级view添加到windowManager,提供了标准UI,唯一实现类是PhoneWindow .

哪些地方用到了windowManager
windowManager什么时候创建的

  • IWindowSession mWindowSession;
image.png

Activity中的windowManager的创建
在dialog中调用

 mWindowManager = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);


在Activity中

@Override
public Object getSystemService(@ServiceName @NonNull String name) {
    if (getBaseContext() == null) {
        throw new IllegalStateException(
                "System services not available to Activities before onCreate()");
    }

    if (WINDOW_SERVICE.equals(name)) {
        return mWindowManager;
    } else if (SEARCH_SERVICE.equals(name)) {
        ensureSearchManager();
        return mSearchManager;
    }
    return super.getSystemService(name);
}

attach时

 final void attach(...) {
     //....
    "1.创建window"
    mWindow = new PhoneWindow(this, window, activityConfigCallback);
    "2.设置window回调"
    mWindow.setWindowControllerCallback(this);
    mWindow.setCallback(this);
    mWindow.setOnWindowDismissedCallback(this);
    mWindow.getLayoutInflater().setPrivateFactory(this);
    //....
    "4.创建windowManger并关联window和windowManger"
    mWindow.setWindowManager(
            (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
            mToken, mComponent.flattenToString(),
            (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
    if (mParent != null) {
        mWindow.setContainer(mParent.getWindow());
    }
    mWindowManager = mWindow.getWindowManager();
    mCurrentConfig = config;

  //...
}

在服务中获取windowManager
下图是输入法InputMethodService创建输入法dialog的过程
softInputWindow实际就是输入法的视图区


image.png

断点继续走到dialog的创建过程


用户

此时用户获取windowManger的上下文是service

image.png
image.png
image.png

image.png

image.png

=========================================================

可以看到最终是通过了 SystemServiceRegistry 这个类获取windowManager

 "管理所有可以通过Context的getSystemService()获取的系统服务"
 /**
 * Manages all of the system services that can be returned by {@link         Context#getSystemService}.
 * Used by {@link ContextImpl}.
 * 
 */
final class SystemServiceRegistry {
private static final String TAG = "SystemServiceRegistry";

// Service registry information.
// This information is never changed once static initialization has completed.
"保存了所有的系统服务的名字"
private static final HashMap<Class<?>, String> SYSTEM_SERVICE_NAMES =
        new HashMap<Class<?>, String>();
"保存了所有的系统服务的ServiceFetcher  "
"getSystemService时 是通过服务的名字先获取ServiceFetcher ,然后通过fetcher.getService(ctx) 获取注册了的服务"
private static final HashMap<String, ServiceFetcher<?>> SYSTEM_SERVICE_FETCHERS =
        new HashMap<String, ServiceFetcher<?>>();
private static int sServiceCacheSize;

// Not instantiable.
private SystemServiceRegistry() { }

//类加载时注册所有的服务
static {
    registerService(Context.ACCESSIBILITY_SERVICE, AccessibilityManager.class,
            new CachedServiceFetcher<AccessibilityManager>() {
        @Override
        public AccessibilityManager createService(ContextImpl ctx) {
            return AccessibilityManager.getInstance(ctx);
        }});
    //太多服务了  这里先忽略..........
    //WINDOW_SERVICE 在这里注册的
    registerService(Context.WINDOW_SERVICE, WindowManager.class,
            new CachedServiceFetcher<WindowManager>() {
        @Override
        public WindowManager createService(ContextImpl ctx) {
            return new WindowManagerImpl(ctx);
        }});

   //太多服务了  这里先忽略..........

}

/**
 * Creates an array which is used to cache per-Context service instances.
 */
public static Object[] createServiceCache() {
    return new Object[sServiceCacheSize];
}
 "here  通过contextImpl 和Context中的服务名(如Context.WINDOW_SERVICE) 获取服务"
"getSystemService时 是通过服务的名字先获取ServiceFetcher ,然后通过fetcher.getService(ctx) 获取注册了的服务"
/**
 * Gets a system service from a given context.
 */
public static Object getSystemService(ContextImpl ctx, String name) {
    ServiceFetcher<?> fetcher = SYSTEM_SERVICE_FETCHERS.get(name);
    return fetcher != null ? fetcher.getService(ctx) : null;
}

/**
 * Gets the name of the system-level service that is represented by the specified class.
 */
public static String getSystemServiceName(Class<?> serviceClass) {
    return SYSTEM_SERVICE_NAMES.get(serviceClass);
}
  "类加载时调用 注册系统服务"
/**
 * Statically registers a system service with the context.
 * This method must be called during static initialization only
 */
private static <T> void registerService(String serviceName, Class<T> serviceClass,
        ServiceFetcher<T> serviceFetcher) {
    SYSTEM_SERVICE_NAMES.put(serviceClass, serviceName);
    SYSTEM_SERVICE_FETCHERS.put(serviceName, serviceFetcher);
}
"系统服务获取的接口"
/**
 * Base interface for classes that fetch services.
 * These objects must only be created during static initialization.
 */
static abstract interface ServiceFetcher<T> {
    T getService(ContextImpl ctx);
}
"当system service需要 context时使用这个"
/**
 * Override this class when the system service constructor needs a
 * ContextImpl and should be cached and retained by that context.
 */
static abstract class CachedServiceFetcher<T> implements ServiceFetcher<T> {
    private final int mCacheIndex;

    public CachedServiceFetcher() {
        mCacheIndex = sServiceCacheSize++;
    }

    @Override
    @SuppressWarnings("unchecked")
    public final T getService(ContextImpl ctx) {
        final Object[] cache = ctx.mServiceCache;
        synchronized (cache) {
            // Fetch or create the service.
            Object service = cache[mCacheIndex];
            "如果服务为空通过 ContextImpl create 并保存到缓存中"
            if (service == null) {
                try {
                    service = createService(ctx);
                    "把ServiceFetcher create的服务加到缓存中,下次可以直接从缓存中获取"
                    cache[mCacheIndex] = service;
                } catch (ServiceNotFoundException e) {
                    onServiceNotFound(e);
                }
            }
            return (T)service;
        }
    }

    public abstract T createService(ContextImpl ctx) throws ServiceNotFoundException;
}
   "当system service不需要 context时使用这个"
/**
 * Override this class when the system service does not need a ContextImpl
 * and should be cached and retained process-wide.
 */
static abstract class StaticServiceFetcher<T> implements ServiceFetcher<T> {
    private T mCachedInstance;

    @Override
    public final T getService(ContextImpl ctx) {
        synchronized (StaticServiceFetcher.this) {
            if (mCachedInstance == null) {
                try {
                    mCachedInstance = createService();
                } catch (ServiceNotFoundException e) {
                    onServiceNotFound(e);
                }
            }
            return mCachedInstance;
        }
    }

    public abstract T createService() throws ServiceNotFoundException;
}
"每个进程只有一个实例的system service使用这个StaticApplicationContextServiceFetcher注册   ==>只有ConnectivityManager用到  "
/**
 * Like StaticServiceFetcher, creates only one instance of the service per application, but when
 * creating the service for the first time, passes it the application context of the creating
 * application.
 *
 * TODO: Delete this once its only user (ConnectivityManager) is known to work well in the
 * case where multiple application components each have their own ConnectivityManager object.
 */
static abstract class StaticApplicationContextServiceFetcher<T> implements ServiceFetcher<T> {
    private T mCachedInstance;

    @Override
    public final T getService(ContextImpl ctx) {
        synchronized (StaticApplicationContextServiceFetcher.this) {
            if (mCachedInstance == null) {
                Context appContext = ctx.getApplicationContext();
                // If the application context is null, we're either in the system process or
                // it's the application context very early in app initialization. In both these
                // cases, the passed-in ContextImpl will not be freed, so it's safe to pass it
                // to the service. http://b/27532714 .
                try {
                    mCachedInstance = createService(appContext != null ? appContext : ctx);
                } catch (ServiceNotFoundException e) {
                    onServiceNotFound(e);
                }
            }
            return mCachedInstance;
        }
    }

    public abstract T createService(Context applicationContext) throws ServiceNotFoundException;
}

public static void onServiceNotFound(ServiceNotFoundException e) {
    // We're mostly interested in tracking down long-lived core system
    // components that might stumble if they obtain bad references; just
    // emit a tidy log message for normal apps
    if (android.os.Process.myUid() < android.os.Process.FIRST_APPLICATION_UID) {
        Log.wtf(TAG, e.getMessage(), e);
    } else {
        Log.w(TAG, e.getMessage());
    }
}
}

========================================================

image.png
  /**
   * Context for decor views which can be seeded with pure application 
  context and not depend on the
 * activity, but still provide some of the facilities that Activity has,
 * e.g. themes, activity-based resources, etc.
 *
 * @hide
 */
class DecorContext extends ContextThemeWrapper {
private PhoneWindow mPhoneWindow;
private WindowManager mWindowManager;
private Resources mActivityResources;

public DecorContext(Context context, Resources activityResources) {
    super(context, null);
    mActivityResources = activityResources;
}

void setPhoneWindow(PhoneWindow phoneWindow) {
    mPhoneWindow = phoneWindow;
    mWindowManager = null;
}

@Override
public Object getSystemService(String name) {
    if (Context.WINDOW_SERVICE.equals(name)) {
        if (mWindowManager == null) {
            WindowManagerImpl wm =
                    (WindowManagerImpl) super.getSystemService(Context.WINDOW_SERVICE);
            mWindowManager = wm.createLocalWindowManager(mPhoneWindow);
        }
        return mWindowManager;
    }
    return super.getSystemService(name);
}

@Override
public Resources getResources() {
    return mActivityResources;
}

@Override
public AssetManager getAssets() {
    return mActivityResources.getAssets();
}

}

Dialog

public class Dialog implements DialogInterface, Window.Callback,
    KeyEvent.Callback, OnCreateContextMenuListener, Window.OnWindowDismissedCallback {
 //...
"qtip:  look here!!!   dialog 中的windowManger "
private final WindowManager mWindowManager;

final Context mContext;
"qtip:  look here!!!   dialog 中的Window "
final Window mWindow;

View mDecor;

 //....

/**
 * Creates a dialog window that uses the default dialog theme.
 * <p>
 * The supplied {@code context} is used to obtain the window manager and
 * base theme used to present the dialog.
 *
 * @param context the context in which the dialog should run
 * @see android.R.styleable#Theme_dialogTheme
 */
public Dialog(@NonNull Context context) {
    this(context, 0, true);
}
//....... 以下略


}


  //dialog的创建

  Dialog(@NonNull Context context, @StyleRes int themeResId, boolean createContextThemeWrapper) {
    if (createContextThemeWrapper) {
        if (themeResId == ResourceId.ID_NULL) {
            final TypedValue outValue = new TypedValue();
            context.getTheme().resolveAttribute(R.attr.dialogTheme, outValue, true);
            themeResId = outValue.resourceId;
        }
        mContext = new ContextThemeWrapper(context, themeResId);
    } else {
        mContext = context;
    }
    "1.获取windowManager  "
    mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
    "2 创建window"
    final Window w = new PhoneWindow(mContext);
    mWindow = w;
    "3.设置window回调"
    w.setCallback(this);
    w.setOnWindowDismissedCallback(this);
    w.setOnWindowSwipeDismissedCallback(() -> {
        if (mCancelable) {
            cancel();
        }
    });
   " 4 给window设置windowManger "
    w.setWindowManager(mWindowManager, null, null);
    w.setGravity(Gravity.CENTER);

    mListenersHandler = new ListenersHandler(this);
}

PopupWindow

public class PopupWindow {


 //...

private Context mContext;
" PopupWindow 中的windowManager ..."
private WindowManager mWindowManager;

//...

/** View that handles event dispatch and content transitions. */
private PopupDecorView mDecorView;

// ...... 以下 略


}

popupWindow的显示

/**
 * Display the content view in a popup window at the specified location.
 *
 * @param token Window token to use for creating the new window
 * @param gravity the gravity which controls the placement of the popup window
 * @param x the popup's x location offset
 * @param y the popup's y location offset
 *
 * @hide Internal use only. Applications should use
 *       {@link #showAtLocation(View, int, int, int)} instead.
 */
    public void showAtLocation(IBinder token, int gravity, int x, int y) {
    if (isShowing() || mContentView == null) {
        return;
    }

    TransitionManager.endTransitions(mDecorView);

    detachFromAnchor();

    mIsShowing = true;
    mIsDropdown = false;
    mGravity = gravity;

    final WindowManager.LayoutParams p = createPopupLayoutParams(token);
    preparePopup(p);

    p.x = x;
    p.y = y;

    invokePopup(p);
}
 "继续看invokePopup()"

    private void invokePopup(WindowManager.LayoutParams p) {
    if (mContext != null) {
        p.packageName = mContext.getPackageName();
    }

    final PopupDecorView decorView = mDecorView;
    decorView.setFitsSystemWindows(mLayoutInsetDecor);

    setLayoutDirectionFromAnchor();
    "向windowManger中添加View 显示popupWindow "
    mWindowManager.addView(decorView, p);

    if (mEnterTransition != null) {
        decorView.requestEnterTransition(mEnterTransition);
    }
}

"popupWindow的隐藏"
/**
 * Disposes of the popup window. This method can be invoked only after
 * {@link #showAsDropDown(android.view.View)} has been executed. Failing
 * that, calling this method will have no effect.
 *
 * @see #showAsDropDown(android.view.View)
 */
public void dismiss() {
    //如果没有显示或者正在消失 return
    if (!isShowing() || isTransitioningToDismiss()) {
        return;
    }

    final PopupDecorView decorView = mDecorView;
    final View contentView = mContentView;

    final ViewGroup contentHolder;
    final ViewParent contentParent = contentView.getParent();
    if (contentParent instanceof ViewGroup) {
        contentHolder = ((ViewGroup) contentParent);
    } else {
        contentHolder = null;
    }

    // Ensure any ongoing or pending transitions are canceled.
    decorView.cancelTransitions();

    mIsShowing = false;
    mIsTransitioningToDismiss = true;

    // This method may be called as part of window detachment, in which
    // case the anchor view (and its root) will still return true from
    // isAttachedToWindow() during execution of this method; however, we
    // can expect the OnAttachStateChangeListener to have been called prior
    // to executing this method, so we can rely on that instead.
    final Transition exitTransition = mExitTransition;
    if (exitTransition != null && decorView.isLaidOut()
            && (mIsAnchorRootAttached || mAnchorRoot == null)) {
        // The decor view is non-interactive and non-IME-focusable during exit transitions.
        final LayoutParams p = (LayoutParams) decorView.getLayoutParams();
        p.flags |= LayoutParams.FLAG_NOT_TOUCHABLE;
        p.flags |= LayoutParams.FLAG_NOT_FOCUSABLE;
        p.flags &= ~LayoutParams.FLAG_ALT_FOCUSABLE_IM;
        mWindowManager.updateViewLayout(decorView, p);

        final View anchorRoot = mAnchorRoot != null ? mAnchorRoot.get() : null;
        final Rect epicenter = getTransitionEpicenter();

        // Once we start dismissing the decor view, all state (including
        // the anchor root) needs to be moved to the decor view since we
        // may open another popup while it's busy exiting.
        decorView.startExitTransition(exitTransition, anchorRoot, epicenter,
                new TransitionListenerAdapter() {
                    @Override
                    public void onTransitionEnd(Transition transition) {
                        dismissImmediate(decorView, contentHolder, contentView);
                    }
                });
    } else {
    "立即消失   ==============="
        dismissImmediate(decorView, contentHolder, contentView);
    }

    // Clears the anchor view.
    detachFromAnchor();

    if (mOnDismissListener != null) {
        mOnDismissListener.onDismiss();
    }
}


 "再来看看是怎么立即消失的"
/**
 * Removes the popup from the window manager and tears down the supporting
 * view hierarchy, if necessary.
 */
private void dismissImmediate(View decorView, ViewGroup contentHolder, View contentView) {
    // If this method gets called and the decor view doesn't have a parent,
    // then it was either never added or was already removed. That should
    // never happen, but it's worth checking to avoid potential crashes.
    if (decorView.getParent() != null) {

        //qtip: 移除decorView 
        mWindowManager.removeViewImmediate(decorView);

    }

    if (contentHolder != null) {
        contentHolder.removeView(contentView);
    }

    // This needs to stay until after all transitions have ended since we
    // need the reference to cancel transitions in preparePopup().
    mDecorView = null;
    mBackgroundView = null;
    mIsTransitioningToDismiss = false;
}

看看decorView是啥
从windowManager中移除的decorView就是PopupWindow中的一个成员变量 :mDecorView
popupWindow 调用showAtLocation()时会先调用preparePopup(),在preparePopup()中完成decorView 和 mBackgroundView的创建

private void preparePopup(WindowManager.LayoutParams p) {
    if (mContentView == null || mContext == null || mWindowManager == null) {
        throw new IllegalStateException("You must specify a valid content view by "
                + "calling setContentView() before attempting to show the popup.");
    }

    if (p.accessibilityTitle == null) {
        p.accessibilityTitle = mContext.getString(R.string.popup_window_default_title);
    }

    // The old decor view may be transitioning out. Make sure it finishes
    // and cleans up before we try to create another one.
    if (mDecorView != null) {
        mDecorView.cancelTransitions();
    }

    // When a background is available, we embed the content view within
    // another view that owns the background drawable.
    if (mBackground != null) {
     "qtip: 通过popupWindow设置的contentView创建一个背景PopupBackgroundView,PopupBackgroundView继承于FramLayout"
        mBackgroundView = createBackgroundView(mContentView);
        mBackgroundView.setBackground(mBackground);
    } else {
        mBackgroundView = mContentView;
    }
    "qtip: 再通过背景创建一个装饰视图 mDecorView"
    mDecorView = createDecorView(mBackgroundView);

    // The background owner should be elevated so that it casts a shadow.
    mBackgroundView.setElevation(mElevation);

    // We may wrap that in another view, so we'll need to manually specify
    // the surface insets.
    p.setSurfaceInsets(mBackgroundView, true /*manual*/, true /*preservePrevious*/);

    mPopupViewInitialLayoutDirectionInherited =
            (mContentView.getRawLayoutDirection() == View.LAYOUT_DIRECTION_INHERIT);
}


/**
 * Wraps a content view in a FrameLayout.
 *
 * @param contentView the content view to wrap
 * @return a FrameLayout that wraps the content view
 */
private PopupDecorView createDecorView(View contentView) {
    final ViewGroup.LayoutParams layoutParams = mContentView.getLayoutParams();
    final int height;
    if (layoutParams != null && layoutParams.height == WRAP_CONTENT) {
        height = WRAP_CONTENT;
    } else {
        height = MATCH_PARENT;
    }

    final PopupDecorView decorView = new PopupDecorView(mContext);
    decorView.addView(contentView, MATCH_PARENT, height);
    decorView.setClipChildren(false);
    decorView.setClipToPadding(false);

    return decorView;
}

PopupDecorView是popupWindow的一个内部类 继承于FramLayout ,重新处理了点击和按键等事件以控制popupWindow返回键和touch消失

是不是对比PopupWindow 和dialog 好像有些不一样

image.png
image.png

Toast

public class Toast {
static final String TAG = "Toast";
static final boolean localLOGV = false;

/** @hide */
@IntDef({LENGTH_SHORT, LENGTH_LONG})
@Retention(RetentionPolicy.SOURCE)
public @interface Duration {}

/**
 * Show the view or text notification for a short period of time.  This time
 * could be user-definable.  This is the default.
 * @see #setDuration
 */
public static final int LENGTH_SHORT = 0;

/**
 * Show the view or text notification for a long period of time.  This time
 * could be user-definable.
 * @see #setDuration
 */
public static final int LENGTH_LONG = 1;

final Context mContext;
" look here !!   TN是什么 "
final TN mTN;
int mDuration;
View mNextView;

//...... 以下略
}



private static class TN extends ITransientNotification.Stub {
    private final WindowManager.LayoutParams mParams = new WindowManager.LayoutParams();

    private static final int SHOW = 0;
    private static final int HIDE = 1;
    private static final int CANCEL = 2;
    final Handler mHandler;

    int mGravity;
    int mX, mY;
    float mHorizontalMargin;
    float mVerticalMargin;


    View mView;
    View mNextView;
    int mDuration;
    "look here !!!   windowManager  "
    WindowManager mWM;

    String mPackageName;

    static final long SHORT_DURATION_TIMEOUT = 4000;
    static final long LONG_DURATION_TIMEOUT = 7000;

    TN(String packageName, @Nullable Looper looper) {
        // XXX This should be changed to use a Dialog, with a Theme.Toast
        // defined that sets up the layout params appropriately.
        final WindowManager.LayoutParams params = mParams;
        params.height = WindowManager.LayoutParams.WRAP_CONTENT;
        params.width = WindowManager.LayoutParams.WRAP_CONTENT;
        params.format = PixelFormat.TRANSLUCENT;
        params.windowAnimations = com.android.internal.R.style.Animation_Toast;
        params.type = WindowManager.LayoutParams.TYPE_TOAST;
        params.setTitle("Toast");
        params.flags = WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
                | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;

        mPackageName = packageName;

        if (looper == null) {
            // Use Looper.myLooper() if looper is not specified.
            looper = Looper.myLooper();
            if (looper == null) {
                throw new RuntimeException(
                        "Can't toast on a thread that has not called Looper.prepare()");
            }
        }
        mHandler = new Handler(looper, null) {
            @Override
            public void handleMessage(Message msg) {
                switch (msg.what) {
                    case SHOW: {
                        IBinder token = (IBinder) msg.obj;
                        handleShow(token);
                        break;
                    }
                    case HIDE: {
                        handleHide();
                        // Don't do this in handleHide() because it is also invoked by
                        // handleShow()
                        mNextView = null;
                        break;
                    }
                    case CANCEL: {
                        handleHide();
                        // Don't do this in handleHide() because it is also invoked by
                        // handleShow()
                        mNextView = null;
                        try {
                            getService().cancelToast(mPackageName, TN.this);
                        } catch (RemoteException e) {
                        }
                        break;
                    }
                }
            }
        };
    }
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,544评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,430评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,764评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,193评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,216评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,182评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,063评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,917评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,329评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,543评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,722评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,425评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,019评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,671评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,825评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,729评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,614评论 2 353

推荐阅读更多精彩内容