Android-Handler

概述

  • Handler是Android比较基础,也是非常重要的一个组成部分;整个App运行就是基于消息驱动运行起来的,也可以理解为消费者-生产者模式;
  • 原理及实现是相对比较简单的,主要涉及四个类:Message,Handler,MessageQueue,Looper;Message是消息的载体;Handler是消息的发送者及处理者;MessageQueue是消息队列,核心操作是入列和出列;Looper从MessageQueue获取Message并进行分发;
  • 消息的执行是异步的;Message分为同步Message和异步Message,可以向MessageQueue中发送同步栅栏,阻止同步Message执行,但是异步Message不受影响;
  • Handler可以用于异步/延迟/定时执行,也可用于线程间通信;
  • 源码基于Android-SDK-29;

源码

Message
  • Message是消息的载体,生命周期从发送(获取Message对象)到处理(Handler处理结束);
  • Message是单向链式结构;
  • Message
    public final class Message implements Parcelable {
      public int what;
      Handler target;
      public long when;
      Runnable callback;
      public int arg1;
      public int arg2;
      public Object obj;
      Bundle data;
    }
    
    • what:用来标识什么消息,作用域为对应的Handler,只要Handler能区分所有的what即可;
    • target:Message的发送者和处理者;
    • when:Message执行的时间,自Android系统启动以来的时间(不包括系统睡眠时间)为起点;Message不一定正好在when时间执行(可能前面积压了很多Message),但是肯定不会早于when时间;
    • callback:callback也是Message的处理者;
    • arg1/arg2/obj:可携带的数据;是data的另一种替换方案,不涉及内存分配;
    • data:复杂数据或者跨进程携带数据;
  • 对象池
    @UnsupportedAppUsage
      /*package*/ Message next; //链式结构
      /** @hide */
      public static final Object sPoolSync = new Object();
      private static Message sPool;
      private static int sPoolSize = 0;
    
      private static final int MAX_POOL_SIZE = 50;
    
    • Message是生命周期很短(从发送到处理完毕)的对象,并且很多地方都会用到;大量创建生命周期短的对象这种场景下,创建对象本身需要比较耗时,也可能触发GC,导致系统卡顿不稳定;所以需要对象池;
    • Message是单向链式结构,对象池只要持有一个Message对象即可;
    • 获取Message对象尽量通过Message.obtain/Handler.obtain方法;源码中会对sPool加锁,防止并发调用;
  • 跨进程
    /**
       * Optional Messenger where replies to this message can be sent.  The
       * semantics of exactly how this is used are up to the sender and
       * receiver.
       */
      public Messenger replyTo;
      /**
       * Indicates that the uid is not set;
       *
       * @hide Only for use within the system server.
       */
      public static final int UID_NONE = -1;
      /**
       * Optional field indicating the uid that sent the message.  This is
       * only valid for messages posted by a {@link Messenger}; otherwise,
       * it will be -1.
       */
      public int sendingUid = UID_NONE;
      /**
       * Optional field indicating the uid that caused this message to be enqueued.
       *
       * @hide Only for use within the system server.
       */
      public int workSourceUid = UID_NONE;
    
    • 跨进程这块后面再补;
Handler
  • Handler是Message的发送者和处理者;
  • 每个Handler对象都绑定到Looper对象,Handler发送Message最终发送到绑定的Looper对象的MessageQueue中;如果创建Handler时未指定了Looper对象,那么就绑定到创建Handler对象时所处的线程的Looper对象;
  • 创建Handler对象
    public Handler(@Nullable Callback callback, boolean async) {
          //提醒可能内存泄漏
          if (FIND_POTENTIAL_LEAKS) {
              final Class<? extends Handler> klass = getClass();
              if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
                      (klass.getModifiers() & Modifier.STATIC) == 0) {
                  Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
                      klass.getCanonicalName());
              }
          }
          //获取当前线程的Looper
          mLooper = Looper.myLooper();
          if (mLooper == null) {
              throw new RuntimeException(
                  "Can't create handler inside thread " + Thread.currentThread()
                          + " that has not called Looper.prepare()");
          }
          mQueue = mLooper.mQueue;
          mCallback = callback;
          mAsynchronous = async;
      }
    
    public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) {
          mLooper = looper;
          mQueue = looper.mQueue;
          mCallback = callback;
          mAsynchronous = async;
      }
    
    • 如果构造函数没有指定Looper,那么获取当前线程的Looper对象;构造函数就是为了生成Looper,MessageQueue,Callback(Handler的Callback,不是Message的Callback,类似于Handler.handleMessage方法),Asynchronous(为true时,该Handler发送的都是异步Message);
  • 发送消息
    public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {
          MessageQueue queue = mQueue;
          if (queue == null) {
              RuntimeException e = new RuntimeException(
                      this + " sendMessageAtTime() called with no mQueue");
              Log.w("Looper", e.getMessage(), e);
              return false;
          }
          return enqueueMessage(queue, msg, uptimeMillis);
      }
    
    private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
              long uptimeMillis) {
          msg.target = this;
          msg.workSourceUid = ThreadLocalWorkSource.getUid();
    
          if (mAsynchronous) {
              msg.setAsynchronous(true);
          }
          return queue.enqueueMessage(msg, uptimeMillis);
      }
    
    • 发送Message和Runnable,最终都会调用sendMessageAtTime;Runnable会被封装为Message对象;sendMessage,sendMessageDelayed通过时间转化最终都会转化为sendMessageAtTime;
    • Message入列是直接调用MessageQueue.enqueueMessage方法,如果Handler.mAsynchronous为true,则该Handler所有的Message都是异步Message;
    public final boolean runWithScissors(@NonNull Runnable r, long timeout) {
          if (Looper.myLooper() == mLooper) {
              调用线程和执行线程是同一个线程时,直接run
              r.run();
              return true;
          }
    
          BlockingRunnable br = new BlockingRunnable(r);
          return br.postAndWait(this, timeout);
      }
    
    • 阻塞发送消息:如果Handler绑定的Looper和调用者所在的线程Looper是同一个,直接调用Runnable.run;否则调用者所在的线程阻塞,直到执行结束;
    • 发送立即处理消息:postAtFrontOfQueue/sendMessageAtFrontOfQueue方法,设置Message.when为0,Message会排在MessageQueue的最前面,Looper取出的下一个Message就是该Message,所以是立即处理消息;
  • 删除消息
    • Handler的删除消息都会调用MessageQueue对应的方法;
  • 处理消息
    public void dispatchMessage(@NonNull Message msg) {
          if (msg.callback != null) {
              handleCallback(msg); // Runnable转化的Message
          } else {
              if (mCallback != null) {
                  //Handler.Callback优先处理Message,如果返回true,表示处理结束
                  if (mCallback.handleMessage(msg)) {
                      return;
                  }
              }
              handleMessage(msg);
          }
      }
    
    private static void handleCallback(Message message) {
          message.callback.run();
      }
    
    public void handleMessage(@NonNull Message msg) {
      }
    
    • 如果Message的callback不为null(Runnable转化的Message),直接调用callback处理;否则,优先Handler.Callback处理,如果返回true,表示处理结束;否则,handleMessage处理;
Looper
  • Looper从MessageQueue获取Message并进行分发;主要功能就是消息循环以及监控;
  • Looper是保存在ThreadLocal中,MessageQueue是Looper的实例变量,所以MessageQueue对象也是线程内唯一;
  • 线程本地存储
      static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
    
      final MessageQueue mQueue;
      final Thread mThread;
    
    • Looper保存在ThreadLocal中,确保线程内单例;MessageQueue是Looper的成员变量,外部不可创建,所以MessageQueue也是线程内单例;
  • 初始化
    private static void prepare(boolean quitAllowed) {
          if (sThreadLocal.get() != null) {
              throw new RuntimeException("Only one Looper may be created per thread");
          }
          sThreadLocal.set(new Looper(quitAllowed));
      }
    
    private Looper(boolean quitAllowed) {
          mQueue = new MessageQueue(quitAllowed);
          mThread = Thread.currentThread();
      }
    
    • Looper构造函数中,初始化了MessageQueue,并将Looper保存在ThreadLocal中;
    • 初始化必须在对应的线程中调用;
    • 默认是可退出的(Looper/MessageQueue);
  • 循环
    public static void loop() {
          final Looper me = myLooper();
          final MessageQueue queue = me.mQueue;
    
          // Allow overriding a threshold with a system prop. e.g.
          // adb shell 'setprop log.looper.1000.main.slow 1 && stop && start'
          final int thresholdOverride =
                  SystemProperties.getInt("log.looper."
                          + Process.myUid() + "."
                          + Thread.currentThread().getName()
                          + ".slow", 0);
    
          boolean slowDeliveryDetected = false;
          //死循环
          for (;;) {
              Message msg = queue.next(); // 可阻塞
              if (msg == null) { //MessageQueue已退出
                  return;
              }
    
              // This must be in a local variable, in case a UI event sets the logger
              final Printer logging = me.mLogging;
              if (logging != null) {
                  logging.println(">>>>> Dispatching to " + msg.target + " " +
                          msg.callback + ": " + msg.what);
              }
              // Make sure the observer won't change while processing a transaction.
              final Observer observer = sObserver;
    
              final long traceTag = me.mTraceTag;
              long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;
              long slowDeliveryThresholdMs = me.mSlowDeliveryThresholdMs;
              if (thresholdOverride > 0) {
                  slowDispatchThresholdMs = thresholdOverride;
                  slowDeliveryThresholdMs = thresholdOverride;
              }
              final boolean logSlowDelivery = (slowDeliveryThresholdMs > 0) && (msg.when > 0);
              final boolean logSlowDispatch = (slowDispatchThresholdMs > 0);
    
              final boolean needStartTime = logSlowDelivery || logSlowDispatch;
              final boolean needEndTime = logSlowDispatch;
    
              if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
                  Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
              }
    
              final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0;
              final long dispatchEnd;
              Object token = null;
              if (observer != null) {
                  token = observer.messageDispatchStarting();
              }
              long origWorkSource = ThreadLocalWorkSource.setUid(msg.workSourceUid);
              try {
                  msg.target.dispatchMessage(msg);
                  if (observer != null) {
                      observer.messageDispatched(token, msg);
                  }
                  dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
              } catch (Exception exception) {
                  if (observer != null) {
                      observer.dispatchingThrewException(token, msg, exception);
                  }
                  throw exception;
              } finally {
                  ThreadLocalWorkSource.restore(origWorkSource);
                  if (traceTag != 0) {
                      Trace.traceEnd(traceTag);
                  }
              }
              if (logSlowDelivery) {
                  if (slowDeliveryDetected) {
                      if ((dispatchStart - msg.when) <= 10) {
                          Slog.w(TAG, "Drained");
                          slowDeliveryDetected = false;
                      }
                  } else {
                      if (showSlowLog(slowDeliveryThresholdMs, msg.when, dispatchStart, "delivery",
                              msg)) {
                          // Once we write a slow delivery log, suppress until the queue drains.
                          slowDeliveryDetected = true;
                      }
                  }
              }
              if (logSlowDispatch) {
                  showSlowLog(slowDispatchThresholdMs, dispatchStart, dispatchEnd, "dispatch", msg);
              }
    
              if (logging != null) {
                  logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
              }
              msg.recycleUnchecked(); //Message回收
          }
      }
    
    • 主要代码就是从MessageQueue中取出要执行的下一个Message(可能阻塞),然后调用Handler.dispatchMessage进行分发;最后回收Message对象;
  • 监控
    • 在loop方法中,分发Message前后,根根据设置的阈值/Printer/全局Observer,执行对应的Log以及回调;
    • 主要用于系统监控,业务层只能设置Printer,但是返回的只有一个拼接好的String,用法有限;
MessageQueue
  • MessageQueue是消息队列,负责Message入列,出列,空闲时间IdleHandler的处理,以及线程的挂起;
  • 初始化
    MessageQueue(boolean quitAllowed) {
          mQuitAllowed = quitAllowed;
          mPtr = nativeInit();
      }
    
    private native static long nativeInit();
    
    • 初始化Looper对象时,也会初始化MessageQueue,进而在Native层也初始化一个NativeMessageQueue,并在Java层保存起来(mPtr);
    • Native层也有消息队列,和Java层类似,MessageQueue是Native和Java层消息队列的纽带;
  • 入列
    boolean enqueueMessage(Message msg, long when) {
    
          synchronized (this) { //加锁
              if (mQuitting) {
                  IllegalStateException e = new IllegalStateException(
                          msg.target + " sending message to a Handler on a dead thread");
                  Log.w(TAG, e.getMessage(), e);
                  msg.recycle();
                  return false;
              }
    
              msg.markInUse();
              msg.when = when;
              Message p = mMessages;
              boolean needWake;
              if (p == null || when == 0 || when < p.when) {
                  // 插入到头部
                  msg.next = p;
                  mMessages = msg;
                  needWake = mBlocked;
              } else {
                  // Inserted within the middle of the queue.  Usually we don't have to wake
                  // up the event queue unless there is a barrier at the head of the queue
                  // and the message is the earliest asynchronous message in the queue.
                  needWake = mBlocked && p.target == null && msg.isAsynchronous();
                  Message prev;
                  //根据when插入到合适位置
                  for (;;) {
                      prev = p;
                      p = p.next;
                      if (p == null || when < p.when) {
                          break;
                      }
                      if (needWake && p.isAsynchronous()) {
                          needWake = false;
                      }
                  }
                  msg.next = p; // invariant: p == prev.next
                  prev.next = msg;
              }
    
              // We can assume mPtr != 0 because mQuitting is false.
              if (needWake) { //唤醒
                  nativeWake(mPtr);
              }
          }
          return true;
      }
    
    • 可能并发,所以在MessageQueue对象上加锁;
    • 根据Message.when插入到合适位置;
    • 如果需要唤醒,则从Native唤醒;
  • 出列
    Message next() {
          final long ptr = mPtr;
          if (ptr == 0) { //消息队列已经退出
              return null;
          }
    
          int pendingIdleHandlerCount = -1; // -1 only during first iteration
          int nextPollTimeoutMillis = 0;
          for (;;) {
    
              nativePollOnce(ptr, nextPollTimeoutMillis);
    
              synchronized (this) { //加锁
                  // Try to retrieve the next message.  Return if found.
                  final long now = SystemClock.uptimeMillis();
                  Message prevMsg = null;
                  Message msg = mMessages;
                  if (msg != null && msg.target == null) {
                      //同步栅栏,获取下一个异步Message
                      do {
                          prevMsg = msg;
                          msg = msg.next;
                      } while (msg != null && !msg.isAsynchronous());
                  }
                  if (msg != null) {
                      if (now < msg.when) {
                          //头部Message还未到执行时间
                          nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                      } else {
                          //成功获取到要分发的Message
                          mBlocked = false;
                          if (prevMsg != null) {
                              prevMsg.next = msg.next;
                          } else {
                              mMessages = msg.next;
                          }
                          msg.next = null;
                          if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                          msg.markInUse();
                          return msg;
                      }
                  } else {
                      // No more messages.
                      nextPollTimeoutMillis = -1;
                  }
    
                  // Process the quit message now that all pending messages have been handled.
                  if (mQuitting) {
                      dispose();
                      return null;
                  }
    
                 //
                  if (pendingIdleHandlerCount < 0
                          && (mMessages == null || now < mMessages.when)) {
                      pendingIdleHandlerCount = mIdleHandlers.size();
                  }
                  if (pendingIdleHandlerCount <= 0) {
                      // No idle handlers to run.  Loop and wait some more.
                      mBlocked = true;
                      continue;
                  }
    
                  if (mPendingIdleHandlers == null) {
                      mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
                  }
                  mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
              }
    
              // Run the idle handlers.
              // We only ever reach this code block during the first iteration.
              for (int i = 0; i < pendingIdleHandlerCount; i++) {
                  final IdleHandler idler = mPendingIdleHandlers[i];
                  mPendingIdleHandlers[i] = null; // release the reference to the handler
    
                  boolean keep = false;
                  try {
                      keep = idler.queueIdle();
                  } catch (Throwable t) {
                      Log.wtf(TAG, "IdleHandler threw exception", t);
                  }
    
                  if (!keep) {
                      synchronized (this) {
                          mIdleHandlers.remove(idler);
                      }
                  }
              }
    
              // Reset the idle handler count to 0 so we do not run them again.
              pendingIdleHandlerCount = 0;
    
              // While calling an idle handler, a new message could have been delivered
              // so go back and look again for a pending message without waiting.
              nextPollTimeoutMillis = 0;
          }
      }
    
    • 整体是个死循环,循环获取Message(Java层);
    • 调用nativePollOnce,每个迭代优先处理Native层,Native消息处理完,判断nextPollTimeoutMillis,如果等于0直接返回,如果等于-1进入休眠,如果大于0进入超时休眠;
    • 处理Java层,如果头部Message执行时间已到(包括同步栅栏逻辑)则直接返回Message,否则处理IdleHandler,进入下个迭代;
  • 删除
    • 从头部开始迭代,删除符合参数的Message;
  • IdleHandler
    public void addIdleHandler(@NonNull IdleHandler handler) {
          if (handler == null) {
              throw new NullPointerException("Can't add a null IdleHandler");
          }
          synchronized (this) {
              mIdleHandlers.add(handler);
          }
      }
    
    public void removeIdleHandler(@NonNull IdleHandler handler) {
          synchronized (this) {
              mIdleHandlers.remove(handler);
          }
      }
    
    public boolean isIdle() {
          synchronized (this) {
              final long now = SystemClock.uptimeMillis();
              return mMessages == null || now < mMessages.when;
          }
      }
    
    • IdleHandler用于在消息队列空闲的时候,执行任务;执行逻辑在next方法中
    • IdleHandler.queueIdle:返回true表示保留任务,返回false表示删除任务只执行一次;
  • 同步栅栏
    private int postSyncBarrier(long when) {
          // Enqueue a new sync barrier token.
          // We don't need to wake the queue because the purpose of a barrier is to stall it.
          synchronized (this) {
              final int token = mNextBarrierToken++;
              //同步栅栏Message(target = null)
              final Message msg = Message.obtain();
              msg.markInUse();
              msg.when = when;
              msg.arg1 = token;
              //插入同步栅栏Message
              Message prev = null;
              Message p = mMessages;
              if (when != 0) {
                  while (p != null && p.when <= when) {
                      prev = p;
                      p = p.next;
                  }
              }
              if (prev != null) { // invariant: p == prev.next
                  msg.next = p;
                  prev.next = msg;
              } else {
                  msg.next = p;
                  mMessages = msg;
              }
              //用于删除同步栅栏
              return token;
          }
      }
    
    public void removeSyncBarrier(int token) {
          // Remove a sync barrier token from the queue.
          // If the queue is no longer stalled by a barrier then wake it.
          synchronized (this) {
              Message prev = null;
              Message p = mMessages;
              //定义同步栅栏Message
              while (p != null && (p.target != null || p.arg1 != token)) {
                  prev = p;
                  p = p.next;
              }
              if (p == null) {
                  throw new IllegalStateException("The specified message queue synchronization "
                          + " barrier token has not been posted or has already been removed.");
              }
              final boolean needWake;
              //删除同步栅栏Message
              if (prev != null) {
                  prev.next = p.next;
                  needWake = false;
              } else {
                  mMessages = p.next;
                  needWake = mMessages == null || mMessages.target != null;
              }
              p.recycleUnchecked();
    
              // If the loop is quitting then it is already awake.
              // We can assume mPtr != 0 when mQuitting is false.
              //唤醒
              if (needWake && !mQuitting) {
                  nativeWake(mPtr);
              }
          }
      }
    
    • 同步栅栏Message,用于拖延同步Message;代码逻辑在next方法中;
    • 添加同步栅栏,将同步栅栏Message插入到合适的位置,该位置之后的Message都被拖延,只有异步消息可以执行,直到该同步栅栏消息被移除;
    • 删除同步栅栏,根据token删除对应的同步栅栏,如果需要,唤醒线程;

扩展

IdleHandler
  • IdleHandler用于在线程空闲时(没有Message要执行时)执行任务;
  • IdleHandler的应用场景
    • UI更新后,获取对应的属性,通过IdleHandler在空闲时执行,空闲意味着表示UI测量/布局/绘制已经结束;
    • Activity中初始化以及加载数据可以在IdleHandler中执行;
    • 用于App启动时间优化;
    • 预加载/预处理;
HandlerThread
@Override
  public void run() {
      mTid = Process.myTid();
      Looper.prepare();
      synchronized (this) {
          mLooper = Looper.myLooper();
          notifyAll();
      }
      Process.setThreadPriority(mPriority);
      onLooperPrepared();
      Looper.loop();
      mTid = -1;
  }
  • HandlerThread继承于Thread,但是多了消息队列;
  • 在 run 方法中调用 Looper.prepare() 初始化消息队列,然后进入循环队列;
Messager
  • 跨进程通信,待补充;
ANR
  • 待补充

总结

  • Message是消息的载体(包括Runnable);Handler是消息的发送者(MessageQueue入列)和处理者;Looper负责循环分发消息(MessageQueue出列);MessageQueue负责消息的入列,出列,同步栅栏的处理,IdleHandler的处理;
  • MessageQueue是Java层和Native的纽带;Java层和Native层都有消息队列,优先处理Native层消息,再处理Java层消息,如果Java层没有可执行Message,则Native层进入休眠(超时休眠或者一直休眠);入列或者删除栅栏可唤醒休眠;

存疑

  • Messager跨进程通信;
  • Native与Java层的消息队列的交互;
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,189评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,577评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,857评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,703评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,705评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,620评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,995评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,656评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,898评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,639评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,720评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,395评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,982评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,953评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,195评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,907评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,472评论 2 342