App启动-源码流程简介

简介:

在android系统中,Home界面也就是Launcher的界面,Launcher它本身也是一个应用程序,在其它程序安装完成以后,会在Launcher中出现一个图标,点击该图标,对应该图标的应用程序也就会启动起来。

在说明整个启动流程之前,我们首先需要了解几个用到的主要类:

1.   Launcher类

Launcher俗称HomeScreen,也就是我们启动Android手机,第一眼看到的就是这个应用程序,而这个应用程序是比较特殊而且任务艰巨的。它负责了我们除了应用本身操作以外的所有操作,它负责了我们有几个桌面,点击应用图标启动应用程序,长时间按桌面出现上下文菜单,长时间按桌面的图标出现垃圾箱,拖动应用图标重新定位等等,一系列的操作。

2.  Intrumentation类

官方的解释为:

/** Base class for implementing application instrumentation code.  When running

* with instrumentation turned on, this class will be instantiated for you

* before any of the application code, allowing you to monitor all of the

* interaction the system has with the application.  An Instrumentation

* implementation is described to the system through an AndroidManifest.xml's

* <instrumentation> tag.*/

大意为:Instrumentation类会在应用的任何代码执行前被实列化,用来监控系统与应用的交互。

在本文介绍的启动流程当中,Instrumentation则搭建起了从Launcher到ActivityManagerNative桥梁,从ActivityManagerNative作为起始点,开启了整个App启动的流程。

3.  ActivityManagerNative类

ActivityManager的功能是与系统中所有运行着的Activity交互提供了接口,主要的接口围绕着运行中的进程信息,任务信息,服务信息等。而在后续的介绍中,会谈到ActivityManager到ActivityManagerService之间的交互,它们二者交互中会经过一个环节,那就是进程通信,而IActivityManager以及实现接口的代理类ActivityManagerProxy,存根类ActivityManagerNative起着负责进程通信的作用,虽然这里没有使用aidl文件定义进程通信接口IActivityManager,其实是一样的,我们可以把它看做是自己手动编译的aidl进程通信java类实现,ActivityManagerProxy是代理类,ActivityManagerNative是Stub类,IActivityManager是aidl接口。

4.   ActivityStack类

正如其名,ActivityStack是一个用于管理Activity栈结构的类,下面有对栈结构的简单介绍:

Activity承担了大量的显示和交互工作,从某种角度上将,我们看见的应用程序就是许多个Activity的组合。为了让这许多 Activity协同工作而不至于产生混乱,Android平台设计了一种堆栈机制用于管理Activity,其遵循先进后出的原则,系统总是显示位于栈 顶的Activity,从逻辑上将,位于栈顶的Activity也就是最后打开的Activity,这也是符合逻辑的。

在操作应用程序时,每次启动新的Activity,都会将此压入Activity Stack,当用户执行返回操作时,移除Activity Stack顶上的Activity,这样就实现了返回上一个Activty的功能。直到用户一直返回到Home Screen,这时候可以理解为移除了Activity Stack所有的Activity,这个Activity Stack不再存在,应用程序也结束了运行。

5.   ActivityThread类

它管理应用进程的主线程的执行(相当于普通Java程序的main入口函数),并根据AMS的要求(通过IApplicationThread接口,AMS为Client、ActivityThread.ApplicationThread为Server)负责调度和执行activities、broadcasts和其它操作。

6.   ActivityApplication类

该类在其它博客和论坛上有很详细的说明,这里我们引入一些比较简单明了的解释:

Application和Activity,Service一样是android框架的一个系统组件,当android程序启动时系统会创建一个 application对象,用来存储系统的一些信息。通常我们是不需要指定一个Application的,这时系统会自动帮我们创建,如果需要创建自己 的Application,也很简单创建一个类继承 Application并在manifest的application标签中进行注册(只需要给Application标签增加个name属性把自己的 Application的名字定入即可)。

android系统会为每个程序运行时创建一个Application类的对象且仅创建一个,所以Application可以说是单例 (singleton)模式的一个类.且application对象的生命周期是整个程序中最长的,它的生命周期就等于这个程序的生命周期。因为它是全局 的单例的,所以在不同的Activity,Service中获得的对象都是同一个对象。所以通过Application来进行一些,数据传递,数据共享 等,数据缓存等操作。

7.   ActivityManagerService类

ActivityManagerService是非常值得深入学习的,这里只笼统的说明一下他的功能:

ActivityManagerService是Android中最核心的服务,主要负责系统中四大组件的启动、切换、调度及应用进程的管理和调度等工作,其职责与操作系统中的进程管理和调度模块相类似,因此它在Android中非常重要。

额外补充:

除上面提到的信息以外,还需要对Android系统进程间通讯Binder机制有一定的了解,由于本文中只介绍app的启动流程,对于Binder机制,这里就从网上找了一些基本嘻嘻,进行简单说明一下:

Android系统存了Zygote进程和SystemServer进程以及各种应用进程等,为了能够实现各种进程之间的通讯,Android系统采用了自己的进程间通讯方式Binder机制。其中主要涉及到了四种角色:Binder Client,Binder Server,Binder Manager, Binder driver。各种角色之间的关系可以参考下面这张图的介绍:

Binder机制图解

流程及源码:

下面我们详细介绍一下点击后,整个源码的运行流程:

1.Launcher:

1.a  位于packages/apps/Launcher2目录。

1.b  源码部分:

Launcher源码

在点击了Launcher界面中的App的图标以后,会执行其中的onClick方法,其中intent为我们在AndroidManifest.xml文件中配置的信息:

AndroidManifest.xml配置信息

同时,从代码中可以发现其中最终调用了startActivitySafely方法:

startActivitySafely方法源码

可以发现内部则又调用了startActivity方法,但是我们在Launcher类中没有发现有该方法,由 于Launcher类为Activity类的子类,我们可以在其父类中进行查找,发现其中有我们正在查找的方法:

startActivity方法源码

它本身由调用了startActivityForResult进行处理,其中传入的参数-1代表结束后,不需要这个Activity的结果,让我们再来看这个startActivityForResult:

startActivityForResult方法源码

可以发现,在startActivityForResult方法内部,又调用了Intrumentation类的对象中的execStartActivity方法,执行时,传入了相应的参数,其中:

A. mMainThread:为Activity成员变量,类型Activity Thread,它代表的是应用程序的主线程。

B. mMainThread.getApplicationThread():则是为了获取其内部的Application Thread内部类,在随后的流程中,它作为一个Binder对象,扮演了一个链接通讯的角色,ActivityManagerService会使用它来和ActivityThread来进行进程间通信。

C. mToken:为一个Binder对象的远程接口。

2.  Instrumentation.execStartActivity

2.a  作用:

它用来监控应用程序和系统的交互。

2.b  位置:

package android.app(1492行)

2.c  源码:

由于在上面中调用到了Instrumentation中的execStartActivity方法,我们直接来看该方法内部的源码:

execStartActivity方法源码

在方法中可以看到,与startActivity相关的方法是:ActivityManagerNative.getDefault.startActivity方法,其中:

ActivityManagerNative.getDefault返回是ActivityManagerService的远程接口,即为ActivityManagerProxy的接口,通过该接口调用startActivity方法,在参数中可以发现,传入了intent.resolveTypeIfNeeded这个参数,实则为AndroidMainfest.xml中的设置的MIME类型,从上面配置的信息中,我们可以知道intent并没有配置MIME类型,因此在此返回null,不用做考虑。

3.  ActivityManagerProxy.startActivity

3.a  作用:

提供IActivityManager,根据参数,执行接口中提供的对应方法。

3.b  位置:

ActivityManagerNative中的内部类(3044行)

3.c  源码:

startActivity方法源码

在ActivityManagerProxy的startActivity方法中,可以看见对传入的参数及信息封装到一个Parcel类型的Data包内,在封装完成后,执行了IBinder类型mRemote变量中的transact方法,其中传入的对应startActivity行为的参数START_ACTIVITY_TRANSACTION。

4.  ActivityManagerService.startActivity

4.a  作用:

在经过上面的mRemote变量的transact方法执行以后,最终会通过Binder机制,进入到ActivityManagerService当中,执行startActivity方法。

4.b  位置:

android.server.am目录下

4.c  源码:

位置(4282行)

startActivity方法源码

执行的内容:

最终是将操作交给了mActivityStarter中的startActivityMayWait去执行。

5.  ActivityStarter.startActivityMayWait

5.a  作用:

对intent中传来的信息进行封装,同时调用startActivityLocked进行下一步操作。

5.b  位置:

android.server.am目录下

5.c  源码:

位置:(675行)

在startActivityMayWait方法中,与启动相关的内容主要有:

startActivityMayWait方法源码(1)

这部分内容,除了在新的intent中封装好即将启动的应用程序信息内容以外,同时,调用resolveIntent方法,负责将intent中的内容给解析出来,得到MainActivity的相关信息,保存在ainfo变量当中。

startActivityMayWait方法源码(2)

在最后的时候,调用startActivityLocked(819行)方法去进一步处理。

6.  ActivityStarter.startActivityLocked

6.a  作用:

接收并保存调用者的进程信息,并调用startActivityUncheckedLocked去进行下一步操作

6.b  位置:

android.server.am目录下

6.c  源码:

位置:(224行)

startActivityLocked方法源码

startActivityLocked方法中内部会将调用该方法的进程对象,即caller的进程信息(Launch),保存在callerApp当中,用于下面的ActivityRecord记录用。

startActivityLocked方法源码(1)

同时,作为Launcher这个Activity里面的一个Binder对象,它可以获得LauncherActivity的相关信息,同时也被该方法的sourceRecord变量记录了下来。

startActivityLocked方法源码(2)

随后,所有即将用到的进程信息会被传入创建的ActivityRecord类型对象r(493行)当中。

startActivityLocked方法源码(3)

最后调用startActivityUnchecked(535行)方法将r对象,sourceRecord等参数传入该方法中。

7  ActivityStack.startActivityUnchecked等方法

7.a  作用:

判断即将启动的Activity是否需要在新的task中进行创建和创建新的Task

7.b  位置:(959行)

startActivityUnchecked方法源码(1)

在执行时,会调用getReusableIntentActivity方法去看当前有没有Task可以用来执行这个Activity。

getReusableIntentActivity方法源码

在该方法中,由于没有在AndroidMainfest.xml中设置launchmode,因此默认情况下为0,即standard的情况。因此在检查时候,会执行对应条件中的findTaskLocked方法,由于这个Activity当前都没有创建过,因此,必然返回为null值。

startActivityUnchecked方法源码(2)

因此,会调用上面的setTaskFromReuseOrCreateNewTask(1091行)方法,进行Task的创建。该方法中会将新建Task,并将新建的Task添加进TaskRecord当中做记录。

startActivityUnchecked方法源码(3)

最后会调用ActivityStack类型的变量mTargetStack的startActivityLocked来进行进一步处理。

8  ActivityStack.startActivityLocked

8.a作用:

确保当前要开启的activity添加到新建的task的顶端。

8.b位置:(2636行)

8.c源码:

startActivityLocked方法源码

调用了addActivityToTop确保即将开启的Activity被添加到Task的顶部。随后调用resumeTopActivityInnerLocked(2113行)方法。

9  ActivityStack.resumeTopActivityInnerLocked

9.a  作用:

将位于当前正在运行的LaunchActivity由Resumed状态转换为Paused状态

9.b  位置:(2113行)

9.c  源码:

resumeTopActivityInnerLocked方法源码(1)

将栈顶的Activity取出来,这里的栈顶的Activity,就是刚刚添加到栈顶MainActivity,这时候取出来保存到next变量中。

resumeTopActivityInnerLocked方法源码(2)

与当前Activity相关的,则还包括上面的判断,mLastPausedActivity会将栈顶Activity转换为休眠状态时赋值给自己,而我们当前的LaunchActivity由于仍处于Resumed状态,因此跳过本判断,继续往下进行。

resumeTopActivityInnerLocked方法源码(3)

这时,由于当前正在处于Resumed状态下的Activity是LaunchActivity,则此时,就会走该判断逻辑中的startPausingLocked(2252行)方法,将LaunchActivity推送到Paused状态。

10  ActivityStack.startPausingLocked

10.a  作用

将当前处于Resumed状态的Activity转换为Paused状态

10.b  位置:(1076行)

10.c  源码:

startPausingLocked方法源码(1)

当前的LaunchActivity会作为参数赋值给prev变量。

startPausingLocked方法源码(2)

在随后的逻辑判断中,由于LaunchActivity是在Zygote创建的时候,其app及thread就被创建完成了,因此必然不为null,prev.app.thread为通过它来通知Launcher这个Activity它要进入Paused状态了。当然,这里的prev.app.thread是一个ApplicationThread对象的远程接口,通过调用这个远程接口的schedulePauseActivity来通知Launcher进入Paused状态。

这时就会调用ApplicationThreadProxy,该代理类中的schedulePauseActivity方法。

11  ApplicationThreadProxy.schedulePauseActivity

11.a  作用:

通过Binder进程间通信的机制,进入到ApplicationThread.schedulePauseActivity(657)方法当中。

11.b  位置:(775行)

11.c  源码:

schedulePauseActivity方法源码

该初就是将LaunchActivity的相关信息存入Parcel类型的data变量当中。最终通过Binder类型的mRemote对象运用进程间通讯的机制,调用ActivityThread.schedulePauseActivity。

12  ActivityThread.schedulePauseActivity

12.a  作用:

发送将LaunchActivity转换为Paused状态的信息。

12.b  位置:(657行)

12.c  源码:

schedulePauseActivity方法源码

这里finished参数代表的是传入的参数为prev对象所代表的Activity是否正在等待结束的Activity列表中,当前LaunchActivity当然不在这个队列中,还在Resumed的情况下呢。因此必然为PAUSE_ACTIVITY传参,这时就可以将信息发送出去了。

13  ActivityThread.handleMessage.handlePauseActivity

13.a  作用:

接收并处理发送来的信息请求

13.b  位置:(1469行)

13.c  源码:

handleMessage方法源码

根据msg传入的what信息,在handleMessage找到对应的PAUSE_ACTIVITY项。

handlePauseActivity方法源码

在handlePauseActivity方法中,主要做了以下三件事情:

A.  A.1  传入的userLeaving值为在步骤7的方法源码中,判断intent标记是否有Intent.FLAG_ACTIVITY_NO_USER_ACTION,如果没有设置,则为true。我们在AndroidMainfest.xml文件中并没有配置。因此此处的userLeaving为true。

     A.2  此时回调用performUserLeavingActivity函数来调用Activity.onUserLeaveHint通知Activity,用户要离开它了,进行相应操作。

B.  调用performPauseActivity函数来调用LaunchActivity中的OnPause方法。

C.  它通知ActivityManagerService,这个Activity已经进入Paused状态了ActivityManagerService现在可以完成未竟的事情,启动MainActivity。

14  ActivityManagerProxy.activityPaused

14.a  作用:

运行方法,利用Binder机制执行actiityPaused方法

14.b  位置:(3585行)

14.c  源码:

actiityPaused方法源码

看到这段代码是不是很熟悉,是的,在此利用了代理类来执行了IBinder类型mRemote变量中的transact方法,不过这次传入的ACTIVITY_PAUSED_TRANSACTION字段,执行的是pause方法。这时会会到ActivityManagerService中执行activityPaused方法。

15  ActivityManagerService.activityPausedLocked

15.a  作用:

调用ActivityStack中的activityPausedLocked方法

15.b  位置:(6857行)

15.c  源码:

activityPaused方法源码

16  ActivityStack.activityPausedLocked

16.a  作用:

执行completePauseLocked方法

16.b  位置:(1189行)

16.c  源码:

activityPausedLocked方法源码

当前一直在运行想要转入Pause状态的是LaunchActivity,传入的IBinder类型token值则对应的就是LaunchActivity,因此mPausingActivity与r均为LaunchActivity,则执行其中completePauseLocked方法。

17  ActivityStack.completePauseLocked

17.a  作用:

调用ActivityStackSupervisor中的开启Activity方法

17.b  位置:(1270行)

17.c  源码:

completePauseLocked方法源码(1)

它主要负责了两件事情,一件是将原来为LaunchActivity的mPausingActivity滞空。(1311)

completePauseLocked方法源码(2)

另外一件事情就是调用resumeFocusedStackTopActivityLocked(1317行)方法,获得当前堆栈顶端的Activity,如我们之前的步骤所记,我们新建了一个task,并将即将启动的MainActivity添加到栈顶,则当前的Activity就是我们即将要启动的MainActivity,此时通过该方法,将其取出来并存储与next当中,然后最终调用其中的ActivityStackSupervisor中的startSpecificActivityLocked()方法,将next传入,开启next所代表的MainActivity(2514行)。

18  ActivityStack.Supervisor.startSpecificActivityLocked

18.a  作用:

判断是否开启该进程,如果没有则调用ActivityManagerService进行开启。

18.b  位置:(1355行)

18.c  源码:

startSpecificActivityLocked方法源码(1)

在进入方法后会受限判断该应用是否已经开启,由于我们只是刚刚点开该程序,则取回来的app则肯定为空。

startSpecificActivityLocked方法源码(2)

随后就会调用ActivityManagerService.startProcessLocked方法进行下一步操作。

19  ActivityManagerService.startProcessLocked

19.a  作用:

调用Process.start接口来新建一个新的进程

19.b  位置:(3477行-3487行-3600行-3616行-3743行)

19.c  源码:

startProcessLocked方法部分源码

经过几个方法到跳转,最终调用到了Process.start的方法,在调用Process.start方法之前,将一个字符串entryPoint作为参数传入到start方法中,通过该方法,我们创建一个新的进程,这里涉及到了Zygote中的Socket接口监听并调用孵化APP进程的过程,这里我们详细说明一下:

19.c.A   调用Process.start方法

Process.start方法源码

19.c.B  内部调用了startViaZygote方法,我们再到该方法中看一下:

startViaZygote方法部分源码

19.c.C  可以发现在其最后,又调用了zygoteSendArgsAndGetResult方法:

zygoteSendArgsAndGetResult方法部分源码

19.c.D  在zygoteSendArgsAndGetResult方法中,可以看到它调用了zygoteState对象(Zygote孵化完成后,开启到监听Socket),并借该对象通过Socket通信机制,让Zygote进程fork除了一个新的进程,并根据startProcessLocked方法中传递来的entryPoint(”android.app.ActivityThread”)字符串,反射出该对象并执行ActivityThread的main方法。此时我们可以进入ActivityThread的main函数中看下进行了哪些操作。

20  ActivityThread.main

20.a  作用:

创建消息循环及接收处理机制,并最终调用ActivityManagerService中的attachApplication。

20.b  位置:

20.c  源码:

main方法源码

进程在创建完成以后,调用了ActivityThread中的main函数,其中调用了attach函数,随后调用Looper的消息循环机制进行消息循环。

attach方法源码

在attach方法中(5888行),通过ActivityMangerNative.getDefault方法获得到了ActivityManager的代理类ActivityManagerProxy,调用其中的attachApplication(3544行)函数。

attachApplication方法源码

该方法中又发现了我们熟悉的Binder类型对象mRemote,再次通过Binder机制,根据传入的ATTACH_APPLICATION_TRANSACTION字段,调用ActivityManagerService中的attachApplication方法(534行)。

21  ActivityManagerService.attachApplication

21.a  作用:

方法调用ActivityStackSupervisor类中的attachApplicationLocked方法。

21.b  位置:(6610行)

21.c  源码:

attachApplication方法源码

内部操作转发给了attachApplicationLocked方法(6384行)。

attachApplicationLocked方法源码

最终会调用位于6550行的ActivityStackSupervisor类中的attachApplicationLocked方法。

22  ActivityStackSupervisor.attachApplicationLocked

22.a  作用:

将此时将启动的MainActivity传递给realStartActivityLocked进行正式启动。

22.b  位置:(825行)

22.c  源码:

attachApplicationLocked方法源码

在该方法中,可以发现其通过调用topRunningActivityLocked将此时位于栈顶的MainActivity 取出来,存储于ActivityRecord类型的hr变量中,并将hr传递给realStartActivityLocked方法进行开启。

realStartActivityLocked方法源码

通过realStartActivityLocked方法,最终通过app.thread进入到ApplicationThreadProxy的scheduleLaunchActivity函数中(709行),注意,这里的第二个参数r,是一个ActivityRecord类型的Binder对象,用来作来这个Activity的token值。此时我们进入到ActivityThreadProxy类中进行查看。

scheduleLaunchActivity方法源码

此方法就是将即将启动的Activity信息进行封装,并以LAUNCH_ACTIVITY的形式发送出去。最终交给ActivityThread中的HandleMessage进行处理。

HandleMessage方法源码

然后再次移交给handleLaunchActivity进行处理。

23  ActivityThread.HandleMessage

23.a  作用:

23.b  位置:(2687行)

23.c  源码:

方法里主要执行了两个步骤:

handleLaunchActivity方法源码

第一个步骤执行了performLaunchActivity方法,该方法加载了这个Activity类,即MainActivity,然后调用它的onCreate函数,

第二步在回到handleLaunchActivity函数时,再调用handleResumeActivity函数来使这个Activity进入Resumed状态,即会调用这个Activity的onResume函数,这是遵循Activity的生命周期的。

我们这里主要研究一下performLaunchActivity方法。

24  ActivityThread.performLaunchActivity

24.a  作用:

保存组件信息,加载需要启动的Activity类,创建Application对象,并调用执行onCreate方法

24.b  位置:(2687行)

24.c  源码:

performLaunchActivity方法源码(1)

这段代码主要负责了收集启动的Activity的package和component信息。

performLaunchActivity方法源码(2)

这段代码主要负责将启动的Activity的类加载到内存当中

performLaunchActivity方法源码(3)

根据AndroidMainfest.xml中配置的Application信息,创建Application对象。

performLaunchActivity方法源码(4)

根据记录到ActivityRecord中的信息,设置Activity中的配置信息,以及上下文信息等

performLaunchActivity方法源码(5)

最后通过mInstrumentation的callActivityOnCreate函数来间接调用,前面我们说过,mInstrumentation在这里的作用是监控Activity与系统的交互操作,相当于是系统运行日志。

到这里,从点击app到启动app执行onCreate方法到一整个流程就进行完毕了。

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

推荐阅读更多精彩内容