程序员自我修养之Android篇

Activity和Fragment的区别


一、Activity的启动流程

启动流程:L:Lock(L代表是锁)   I:Install(代表是安装)

    1.Activity.java  :  startActivity()->startActivityForResult()

     2.Instrumentation.java  :  startActivity()

     3.ActivityManagerProxy.java :  startActivity()

     4.通过IBinder传递到APM.java :  startActivity()->startActivityAsUser()

     5.ActivityStarter.java : startActivityMayWait()->startActivityLocked()

     6.APM.java :  startProcessLock  :  判断是否已经开启该进程,如果开启就不再创建

     7.Process.java :  Start("android.app.ActivityThread") : 这里就是开启一个ActvityThread入口

      8.Process.java :  startZygote() :  真正的孵化出一个进程

      9.ActivityThread.java : main() -> attach()----->通过调用IActivityManager来和AMS进行通行,完整Activity的生命周期

      10.主线程中调用Loop,那么为什么没有造成阻塞呢

          ActivityThread的Main方法里面有个Loop的消息队列,ActivityThread 有个 getHandler 方法,得到这个 handler 就可以发送消息,然后 loop 里就分发消息,然后就发给 handler,然后就执行到 H(Handler )里的对应代码。所以说ActivityThread的main方法主要就是做Activity相关动作的消息循环一旦退出消息循环,那么你的程序也就可以退出了,所以这些代码就不会卡死。

    1.ActivityThread :  该类是应用程序对应的进程的主线程类,即UI线程

 2.ActiityRecord :  activity的记录对象

 3.ActivityStack 、ActivityStackSupervisor : 管理activity的启动,生命周期以及activity的堆栈

  4.TaskRecord  :  应用activity记录任务栈

     5.ProcessRecord  :  该类用于记录每个进程的全部信息。主要信息包括该进程包含的activity、provider、service等信息、进程文件信息、进程状态信息

二、本地广播和全局广播的区别

    1、接受情况:全局广播任何程序都可以接收到,本地广播只能本应用可以接收到

    2、安全性:全局广播不安全,本地广播安全

    3、注册情况:全局广播可以静态和动态注册,本地广播只能动态注册

三、BindSerce和Service的区别

    1、生命周期不同

    2、使用startService()方法启用服务,调用者与服务之间没有关连,属于静态绑定,服务会一直存在。使用bindService()方法启用服务,调用者与服务绑定在了一起,属于动态绑定调用者一旦退出,服务也就终止。

四、view显示的过程

     Window及其实现类是PhoneWindow, 页面都是依附在窗口之上的,DecorView即是窗口最顶层的视图

1.MainActivity.java :  setContentView

2.AppCompatDelegateImpl.java:  setContentView,这里面会有LayoutInfalter去解析xml文件成view,然后会调用mWindow.setContentView方法

 3.PhoneWindow.java :  setContentView里面去创建DecorView,然后解析view添加到DecorView当中。WindowManager继承ViewManager并实现里面的addView、deleteView、updateView三个方法

4.ActivityThread.java : handleResumeActivity 方法里面,有个WindowManager.addView(decorView) , 这个时候就开始view真正的绘制流程,WindowManager是继承ViewManager的类,ViewManager里面有对view的增删更新三个方法

5.WindowManagerImpl.java :  WindowManagerImpl是继承WindowManger的类,去调用addView的方法

6.WindowManagerGlobal.java :  去调用addView的方法,这里面会创建ViewRootImpl的对象,然后去root.setView你要显示的view,在这里之前会有个子线程是否可以更新UI问题,调用完setView这个方法就开始执行view的绘制、测量等操作,在这里之前会有一个是否为自线程更新UI的判断

7.屏幕刷新机制:

    在viewRootImpl哪里通过单例模式创建Choreographer(拷牙骨缝儿)的对象,Choreographer作用是可以接受系统的 VSYNC 信号,以及统一管理应用的输入、动画和绘制等任务的执行时机,16.6ms是在Choreographer的构造方法里面创建的。

requestLayout() ——>  scheduleTraversals() ———>  Choreographer会执行posSyncBarrier(),通过同步屏障消息机制,把消息插入到handler最前面,通过执行postCalllBack等待vsync的信号到来

8.vsync的监听以及处理:

    在FrameDisplayEventReceiver里面的onReceiver,通过JNI的方式去注册Vsync的监听者,通过有onVsync去接受并对Vsync信号的处理, doFrame执行每一帧情况,超过16.6ms就用上一帧,没有超过就及时刷新,然后去执行callBack的函数,然后执行performTraversals——>再去执行viewRootImpl的绘制流程

两篇屏幕刷新机制:

https://cloud.tencent.com/developer/article/1685247

https://www.jianshu.com/p/86d00bbdaf60

如何优先去执行view的测绘呢? 有同步屏障消息和异步消息去执行

ViewRootImpl的作用

    1.它是关联Window和View相关联的桥梁

    2.它执行view 的onMeasure、onLayout、onDraw三大流程

    3.它可以接受屏幕输入事件和分发手势

    在子线程view.requestLayout和view.postInvalidate更新Ui就不会生效了,因为ViewRootImpl的setView方法时候会有线程的检查

onMeasure里面的参数使用:

1、MeasureSpec.UNSPECIFIED ->未指定尺寸

2、MeasureSpec.EXACTLA ->精确尺寸,控件的宽高指定大小或者为FILL_PARENT

3、MeasureSpec.AT_MOST ->最大尺寸,控件的宽高为WRAP_CONTENT,控件大小一般随着控件的子空间或内容进行变化,此时控件尺寸只要不超过父控件允许的最大尺寸

五、自定义view的流程    

    1.LayoutParams是什么,与MeasureSpec有什么关系 

        LayoutParams代表布局中的宽和高

        MeasureSpec里面的三个参数

        EXACTLY :   确切的控件大小

        AT_MOST :  不会超过某个数值,比如父布局大小

        UNSPECIFIED :  控件大小不确定

    2.如何把LayoutParams转换为MeasureSpec的具体参数值

      getChildMeasureSpec() : 这个需要自己看看里面算法

    面试考点:getMeasuredWidth和getWidth的区别

六、view的事件分发流程

       rootViewImpl->DecorView->Activity->Window->DecorView->ViewGroup

      dispatchTouchEvent、onInterceptTouchEvent、onTouchEvent

View事件的总结: 

dispatchTouchEvent的总结: 

ViewGrop:  

在down改为true :  ViewGrop   down、move、up三个事件都可以收到,从Activity传递给ViewGrop的,view什么事件都不能接收到  

 在move改为true:   ViewGrop可以接收到down事件,view也可以接收到down事件,其余什么事件都接受不到,由Activity dispatchTouchEvent和onTouchEvent自己处理   

在up改为true :ViewGroup可以接收到down事件,view也可以接收到down事件,其余什么都接受不到,由Activity dispatchTouchEvent和onTouchEvent自己处理 

 onInterceptTouchEvent的总结: 

ViewGroup: 只能接受down的事件,其余什么都收不到

在down改为true :  ViewGrop可以接收到down事件,view什么事件都接受不到,由Activity dispatchTouchEvent和onTouchEvent自己处理     

在up改为true :  ViewGrop可以接收到down事件,view也可以接收到down事件,由Activity dispatchTouchEvent和onTouchEvent自己处理     

在move改为true :  ViewGrop可以接收到down事件,view也可以接收到down事件,由Activity dispatchTouchEvent和onTouchEvent自己处理 

 onTouchEvent的总结: 

ViewGrop:  

在down改为true :  ViewGrop   down、move、up三个事件都可以收到,从Activity传递给ViewGrop的,view可以收到down事件

在up改为true :  ViewGrop可以接收到down事件,view也可以接收到down事件,由Activity dispatchTouchEvent和onTouchEvent自己处理   

在move改为true :  ViewGrop可以接收到down事件,view也可以接收到down事件,由Activity dispatchTouchEvent和onTouchEvent自己处理 

 dispatchTouchEvent的总结: 

View:          

在down改为true :  ViewGrop   down、move、up三个事件都可以收到,view这个三个事件也都可以收到   

在move改为true: ViewGrop可以接收到down事件,view也可以接收到down事件,其余什么事件都接受不到,由Activity dispatchTouchEvent和onTouchEvent自己处理   

在up改为true: ViewGroup可以接收到down事件,view也可以接收到down事件,其余什么都接受不到,由Activity dispatchTouchEvent和onTouchEvent自己处理 

 onTouchEvent的总结: 

View:          

在down改为true :  ViewGrop   down、move、up三个事件都可以收到,view这个三个事件也都可以收到   

在up改为true :  ViewGrop可以接收到down事件,view也可以接收到down事件,由Activity dispatchTouchEvent和onTouchEvent自己处理   

在move改为true :  ViewGrop可以接收到down事件,view也可以接收到down事件,由Activity dispatchTouchEvent和onTouchEvent自己处理 

滑动冲突解决的办法

    1.外部拦截法:在父布局这里加onInterceptTouchEvent方法处理

    2.内部拦截法:在子布局的dispatchTouchEvent里面的调用getParent().requestDisallowInterceptTouchEvent(true)        

七、多进程通信的方式

            1.intent/bundle

            2.ContentProvider

            3. 广播

            4.AIDL

            5.FileObserver :  使用startWatching和stopWatching两个方法进行加锁,通过监听它的onEvent事件来做处理,原理是底层inotify原理机制,采用是对文件变化做监听

            6.IPC通信

            7.socket : 两次拷贝、C/S架构(复用性差)、不安全

            8.binder :  一次拷贝、C/S架构(复用性强)、安全

            9.管道  :  两次拷贝、C/S架构(复用性差)、不安全

            10. 共享内存 :  两次拷贝、复用性差、不安全

            11. grpc 通信

八、Binder的通信使用和原理

1. aidl如何生成java的文件,客户端和服务端建立联系

     主要是三个文件:Stub(服务端接受数据)、Proxy(封装客户端处理数据是调用stub.asInterface())、继承IInterface接口

   sub的实现在继承service的onBind里面去实现的


2. 客户端于服务数据连通  

 service端的使用: 继承service然后再onBind里面把stub给new出去

        client端的使用:通过bindService去serviceConnection里面去调用Stub.asInterface方法来获得远端Service的对象

    总结: sub     -----> 是给服务端创建的

                proxy  -----> 是给客户端创建的

                IIterface ------>  公共的抽象接口,给sub和procy来使用                

   3.通信实现过程:

        1.首先会用Pacel这个数据结构定义data、reply两个对象,data是用来存客户端准备发给服务端的数据,reply是服务端返回给客户端的数据,用writeToParcel方法,把你们数据转换为Parcel数据

        2.IBinder.transact(变量定义int a= 1,。。。。。)去发送,这个时候客户端等待服务端给返回数据

        3.Stub 的onTrancation()方法,通过定义的a来确定去执行

        4.Binder对象的获取 : Stub.asInterface去拿到Proxy的对象

https://www.bilibili.com/video/BV13A411J7i4?p=7 讲解的地址

   4.Binder的原理

    它是基于C/S架构实现,binder的驱动、Client端、Service 端、ServiceManager构成的

    ServiceManager里面常见的四种方法:

    addService、getService、listService、checkService

    addService案例举例:

    1. 先获得ServiceManager的对象

        sp < IServiceManager > sm = defaultServiceManager();

    2.调用ServiceManager的addService方法

    3.IPCThreadState.cpp(又可以理解为ProcessState)  :  

        transact() ---> 通过Parcel传递数据,同时传递ADD_SERVICE_TRANSACTION这个参数

        writeTransactionData() ---> 把Parcel数据转为binder_transaction_data

        waitForResponse() ---->传递一个BC_transaction变量,这个时候就等待驱动层返回给数据,再返回给上层

    4.binder.c 

       binder_open():在驱动底层开辟空间,主要是为用户态传递数据,这里也是数据拷贝一次

       binder_ioctl() ---> binder_transaction() 

       把binder_transation_data数据转化为binder_transaction,这里收到传入BC_transaction参数传递到下面,驱动层等待创建好被唤起

       binder_thread_write()

       binder_transaction的数据转换为binder_write_read数据

       把binder_write_read存在binder_transaction_data结构中,它里面有个binder_node节点数据结构-->binder_proc(它是存进程的)-->binder_ref(它是存线程的),里面有个红黑树,专门存放线程id和名字作为一一对应的,这样就能从节点--->进程---->再找到对应的线程,数据存储好之后会调IPCThreadState.cpp的waitForResponse方法,并且返回给BR_TRANSACTION_COMPLETE参数,告诉它存储完毕

    IPCThreadState.cpp  ------>BpBinder端,Binder.cpp  -------->BnBinder端,他们两个都是基于IBinder去写的

九、进程保活的实现

        1.Android的进程优先级:前台进程、可见进程、服务进程、后台进程、空进程(主要为了做缓存、缩短下次启动时间)

        2.进程保活方案

     a.利用系统广播拉活

  缺点:当只有在特定场景下才可以拉起保活,下次被杀死后,无法得到控制

     b.利用系统Service机制拉活 :  onStartCommand方法,返回START_STICKY

     缺点:在短时间内被多次杀死就不会被拉活、被root或者其他工具被stop也是无法拉活的

  c.通过native进程拉活(通过AMS的进程杀死)

  d.进程相互唤醒(打开一个app同时唤醒另一个app)

       e.提升Service进程优先级,比如改为前台进程 startForeground(1,notification)

  f.JobScheduler用来检测你自己的服务是否被杀掉,如果被杀掉了,你重启自己的服务               缺点:需要有自启动权限

        g.采用SyncAdapters机制,等待系统去更新同步信息,就是在账号和同步添加账户

十、Handler,Looper,MessageQueue的工作原理

        主线程的Looper对象的生命周期 = 该应用程序的生命周期,它是导致于内存泄漏的原因

       Message消息的种类:

        同步(Barrier)屏障消息、异步消息(在handler初始化可以设置)、普通消息

        同步屏障消息作用:一般该消息都为null,只是起到flag的作用。目的是忽略同步消息让异步消息先执行,一般同步屏障消息是和异步消息一起使用,只是给系统级别使用,当发送异步消息处理完之后就把同步消息给移除

       存消息是按照发送时间去存储的,取出消息的顺序是:是否为同步屏障消息作为判断,遍历单向链表,优先取出异步消息,如果没有,再按照时间顺序去取出普通消息

Handler常见几个面试题:

 1.Looper.loop里死循环为什么没有卡死

   主线程会进入循环状态,它循环的是线程的生命周期,它结束了这个功能也就结束了,等待其他消息再进来,没有消息时候它会进入休眠,会释放cpu并不会占用很大的消耗

 1.1. handler是如何阻塞的?

    MessageQueue.next()里面的nativePollOnce这个方法里面,里面有个nextPollTimeoutMillis整形变量,如果它!= 0就会进入阻塞状态,如果为-1,它就会进入休眠状态,并且释放cpu。

2.post和sendMessage的区别

   post发送是Runnable对象,然后封装成Message对象,主要区别是dispatchMessage这个函数时候,handleCallBack会比handleMessage的对象的优先级高,也就是post比sendMessage的使用优先级高


3.message.obtain和new Message的区别             

   Message.obtain()是一种享元设计模式,采用单向链表数据结构存储消息,Message内部保存了一个缓存的消息池,我们可以用obtain从缓存池获得一个消息,Message使用完后系统会调用recycle回收,如果自己new很多Message,每次使用完后系统放入缓存池,会占用很多内存,造成内存浪费

    4.handler发送延迟原因:

        通过updateTime方法,获取当前时间戳,时间戳顺序来存储到MessageQueue里面


  5.SyncBarrier是什么以及作用:

        它是同步屏障消息,作用是跳过所有的同步消息,然后尽快处理异步消息,提高优先级


6.如何让子线程弹出Toast的方法:

   需要调用looper.prepare以及Looper.loop函数,需要最后手动调用Looper.quitSafely方法才能退出,否则线程不会结束

7. 发送一个异步消息

    Message msg = handler.obtainMessage();

    msg.setAsynchronous(true) //设置异步消息

    msg.sendMessage() ......

十一、HandlerThread、ThreadLocal的工作原理    

ThreadLocal的工作原理

    ThreadLocal的作用是:简化了参数的传递和数据的获取,线程是不安全的,它是对ThreadLocalMap数据模型进行封装

     数据模型

四种引用:强引用、弱引用、软应用、虚引用

 弱引用回收的机制:当强引用删除时候,GC才对弱引用进行回收,不在GC内存不足才对它回收

key是弱引用,value是强引用

十二、SurfaceView和TextureView的区别

    https://www.zhihu.com/question/30922650  :有篇文章进行讲解     

      1.SurfaceView的理解:   

        普通的View共享一个Window,它根据DecorView找到对应的Windows,Window找到对应的WindowState,WindowState又对应一个Surface,所以所有的view的DecorView会共享一个Surface。SurfaceView就自己会单独有一个属于自己的Surface,然后又可以拿到Canvas对象,这样就可以绘制画面,这也就是为什么它可以在子线程自己单独绘制原因   

   2.SurfaceView双缓冲机制

      双缓冲:在运用时可以理解为:SurfaceView在更新视图时用到了两张Canvas,一张frontCanvas和一张backCanvas,每次实际显示的是frontCanvas,backCanvas存储的是即将显示的视图。前端缓冲区是正在渲染的图形缓冲区,而后端缓冲区是接下来要渲染的图形缓冲区。把要画的东西先画到一个内存区域里,然后整体的一次性画出来,好处是反复局部刷屏带来的闪烁

    3.TextureView的理解:

       它可以将内容流直接投影到View,和SurfaceView不同,它不会在WMS中单独创建窗口,而是作为View hierachy中的一个普通View,因此可以和其它普通View一样进行移动,旋转,缩放,动画等变化。值得注意的是TextureView必须在硬件加速的窗口中,它显示的内容流数据可以来自App进程或是远端进程

    4.SurfaceView、TextureView的区别

  SurfaceView的优缺点:

     优点:可以在一个独立的线程中进行绘制,不会影响主线程,使用双缓冲机制,播放视频时画面更流畅

  缺点:Surface不在View hierachy中,它的显示也不受View的属性控制,所以不能进行平移,缩放等变换,也不能放在其它ViewGroup中。SurfaceView 不能嵌套使用

  TextureView的优缺点

       优点:支持移动、旋转、缩放等动画,支持截图

       缺点:必须在硬件加速的窗口中使用,占用内存比SurfaceView高,在5.0以前在主线程渲染,5.0以后有单独的渲染线程。

十三、onNewIntent 的使用以及注意事项

    解释 : 如果一个Activity已经启动过,并且存在当前应用的Activity任务栈中,为了防止它再次启动创建新的实例,就会走onNewInetnt这个方法

1.当ActivityA的LaunchMode为SingleTop时:

    如果ActivityA在栈顶,且现在要再启动ActivityA,这时会调用onNewIntent()方法 ,生命周期顺序为:onCreate--->onStart--->onResume---onPause>onNewIntent--->onResume

2.当ActivityA的LaunchMode为SingleInstance,SingleTask:如果ActivityA已经在堆栈中,那么此时会调用onNewIntent()方法,生命周期调用顺序为:

    onCreate--->onStart--->onResume---按下Home键>onPause--->onstop--->onNewIntent--->onRestart--->onstart--->onResume

3.这里面有个注意事项

@Override

protected void onNewIntent(Intent intent) {

super.onNewIntent(intent);

setIntent(intent);//设置新的intent

int data = getIntent().getIntExtra("tanksu", 0);//此时的到的数据就是正确的了

}

    在这里,如果你没有调用setIntent()方法的话,则getIntent()获取的intent都只会是最初那个intent,这里很重要

十四、Android动画几种方式

帧动画、补间动画、属性动画

     帧动画可以通过顺序播放资源来实现动画的,补间动画可以实现控件的渐入渐出、移动、旋转和缩放效果,以上两种只是针对于view上,且不能实现复杂动画,而属性动画既可以在作用在view上也可以作用在对象上,还可以制作很复杂的动画显示效果

十五、application和activity的context区别

        Application的context的生命周期是整个应用结束后

        activity的context的生命周期是当前页面的生命周期       

十六、Intent 为什么无法传递大数据

        因为它底层是是由Binder实现,Binder底层开辟内存大部分是1024*1024

        Intent和Bundle区别

        Bundle可对对象进行操作,而Intent是不可以,Bundle相对于Intent拥有更多的接口,用起来比较灵活,但是使用Bundle也还是需要借助Intent才可以完成数据传递总之,Bundle旨在存储数据,而Intent旨在传值。

十七、Android的类加载机制

        1.什么是双亲委派机制

         首先判断parent的loadClass是否加载过,如果没有,一直找到最顶级parent去处理;如果到最顶级的parent都没有找到,则去交换给子的class去调用findClass去处理

         它的作用 : 防止.class文件被重复加载、保证一个类在JVM虚拟机中具有唯一性、保证系统的.class文件不能被修改

   2.Android主要几个加载器

        PathClassLoader、DexClassLoader、BaseClassLoader、BootClassLoader            

  3.PathClassLoader和DexClassLoader的区别

       PathClassLoader : 负责app的应用程序的class的加载和资源文件的加载

       DexClassLoader : 

      他们在8.0以后没有区别,8.0以前的区别是:DexClassLoader不为空,PathClassLoader为空,optimizedDirectory作用是存放优化后的dex,DexClassLoader存放优化后的dex位置可以自己定义,而PathClassLoader为空,只能默认存储系统的位置

        冷启动优化的点:BaseClassLoader里面有个Element的数组,里面就是所有的.dex的文件,它需要通过dexElement.findClass遍历整个数组,找到对应的.class才能启动,把我们要先启动的.class放在最前面,这样就可以加快启动速度。

  4.Class文件不同的加载方式

        为什么静态变量不能加载非静态变量呢?

        类加载到内存步骤:加载、链接、初始化

        加载:查找并加载类的二进制数据

        链接:验证(是否为java文件)、准备(包含初始化静态变量和函数-->static)、解析

        初始化:给静态变量赋值

        5.Class.forName和ClassLoader.loaderClass的区别

          class.forName会初始化静态变量和初始化过程,ClassLoader.loaderClass不会有,只是把.class文件加载到jvm内存中

十八、ActivityLifecycleCallBack去监听生命周期函数的回调,有时间去学习

DecorView->Activity->Window->DecorView->ViewGroup

主线程的Looper对象的生命周期 = 该应用程序的生命周期

十九、System.currentTimeMillis和SystemClock.elapsedRealtime区别

        System.currentTimeMillis : 获取当前系统时间戳,可以修改

        SystemClock.elapsedRealtime : 从设备boot后经历的时间值,不可以修改

二十、Binder、HwBinder、VndBinder之间的区别

binder :  架构与应用之间的通信 (system-system)

驱动设备"/dev/binder"

一个守护进行"/system/bin/servicemanager"

一个binder库"/system/lib64/libbinder.so"

vndbinder :    供应商与供应商通信(vendor-vendor)

驱动设备"/dev/vndbinder"

一个守护进行"/system/bin/vndservicemanager"

一个binder库"/system/lib64/libbinder.so"

hwbinder :  供应商与架构之间通信(vendor-system)

驱动设备"/dev/hwbinder"

一个守护进行"/system/bin/hwservicemanager"

一个binder库"/system/lib64/libhwbinder.so"

二十一、binder的内存泄露

通过AIBinder_DeathRecipient_new创建这个引用,然后通过AIBinder_linkToDeath方式去释放binder,防止出现野指针问题。

二十二、解决无法读取进程的信息

mount | grep proc     proc on /proc type proc (rw,relatime,gid=3009,hidepid=2)

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

推荐阅读更多精彩内容