Android Activity启动流程(基于Android8.0系统)

主要对象介绍

  • ActivityManagerService:负责系统中所有Activity的生命周期;
  • ActivityThread:App的真正入口,当App启动后,会调用其main方法开始执行,开启消息循环队列。是传说中的UI线程,即主线程。与ActivityManagerService配合,一起完成Activity的管理工作;
  • ApplicationThread:用来实现ActivityManagerService与ActivityThread之间的交互。在ActivityManagerService需要管理相关Application中的Activity的生命周期,通过ApplicationThread的代理对象与ActivityThread通讯;
  • ApplictationThreadProxy:是ApplicationThread在服务端的代理对象,负责和客户端的ApplicationThread通讯。AMS就是通过这个代理对象与ActivityThread进行通信的,Android 8.0上以删除该类,采用AIDL接口的方式来进行IPC,实现RPC操作
  • Instrumentation:每一个应用程序都只有一个Instrumentation对象,每个Activity内都有一个对该对象的引用。Instrumentation可以理解为应用进程的管家,ActivityThread要创建或者打开某个Activity时,都需要通过Instrumentation来进行具体的操作;
  • ActvitityStack:Activity在AMS的栈管理,用来记录已经启动的Activity的先后关系、状态信息等。通过ActivityStack决定是否需要启动新的进程;
  • ActivityRecord:ActivityStatck的管理对象,每个Activity在AMS对应的一个ActivityRecord,来记录Activity的状态以及其他信息。可以理解为Activity在服务端的Activity对象的映射;
  • ActivityClientRecord:与ActivityRecord是在服务端(AMS)的记录相对应,是Activity在客户端(ActivityThread)的记录;
  • TaskRecord:AMS抽象出来的任务栈的概念。一个TaskRecord包含若干个ActivityRecord。ASM用它来确保Activity启动和退出顺序。它与Activity的启动模式直接相关。
  • ActivityStarter:启动Activity的控制器,主要用于用来将Intent和flags转换成activity和相关任务栈;
  • ActivityStackSupervisor:主要管理着mHomeStack和mFocusedStack两个ActivityStack等相关信息;

Binder通信

  • 在Android 8.0以前,Binder通信的流程如下:
    客户端: ActivityManagerProxy -> Binder驱动 -> 服务端:ActivityManagerService
    服务端: ApplicationThreadProxy -> Binder驱动 -> 客户端:ApplicationThread
  • Android8.0开始,删除了ActivityManagerNative,AMS的继承类也发生了变化,继承了IActivityManager.Stub接口类。了解Android的Binder机制应该知道,这里IActivityManager.aidl生成的接口类。Android8.0开始,把一些Binder代码转化为了AIDL模板方式:
public class ActivityManagerService extends IActivityManager.Stub
        implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback

ActivityManager中的代码:

    public static IActivityManager getService() {
        return IActivityManagerSingleton.get();
    }

    private static final Singleton<IActivityManager> IActivityManagerSingleton =
            new Singleton<IActivityManager>() {
                @Override
                protected IActivityManager create() {
                    final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);
                    final IActivityManager am = IActivityManager.Stub.asInterface(b);
                    return am;
                }
            };
  • 而在Android7.0及其以前的版本上,则是客户端通过ActivityManaagerProxy与服务端的ActivityManager进行通信的
    ActivityManagerNative:
**
*获得IActivityManager类
*/
static public IActivityManager getDefault() {
        return gDefault.get();
    }

static public IActivityManager asInterface(IBinder obj) {
        if (obj == null) {
            return null;
        }
        IActivityManager in =
            (IActivityManager)obj.queryLocalInterface(descriptor);
        if (in != null) {
            return in;
        }
 
        return new ActivityManagerProxy(obj);

}

ActivityManager:

private static final Singleton<IActivityManager> gDefault = new Singleton<IActivityManager>() {
        protected IActivityManager create() {
            IBinder b = ServiceManager.getService("activity");
            if (false) {
                Log.v("ActivityManager", "default service binder = " + b);
            }
            IActivityManager am = asInterface(b);//注意这一行
            if (false) {
                Log.v("ActivityManager", "default service = " + am);
            }
            return am;
        }
};

可以看出来,两个写法不一样,本质上都是一样的,Android 8.0可能使用了AIDL方式进行ipc。

  • 同理,ApplicationThread同上述一样做了更改:
 private class ApplicationThread extends IApplicationThread.Stub {
    ...
 }

所以在Android 8.0上不存在ActivityManagerProxy和ApplicationThreadProxy,而是采用了AIDL接口的方式来进行通信的。

源码调用栈(基于Android 8.0)

按照自上而下的调用栈的顺序进行调用,以类文件为单位来计步,方便大家去了解调用流程,主要涉及的几个源文件:Instumentation,ActivityMangerService,ActivityStack,ActivityStarter,ActivityStackSupervisor,ActivityThread,Activity等。

  1. Activity
public void startActivity(Intent intent)
    
public void startActivity(Intent intent, @Nullable Bundle options)

public void startActivityForResult(@RequiresPermission Intent intent, int requestCode,
            @Nullable Bundle options)
  1. Intrumentation
public ActivityResult execStartActivity(
    Context who, IBinder contextThread, IBinder token, Activity target,
    Intent intent, int requestCode, Bundle options){
    ...
    int result = ActivityManager.getService()
        .startActivity(whoThread, who.getBasePackageName(), intent,
            intent.resolveTypeIfNeeded(who.getContentResolver()),
            token, target != null ? target.mEmbeddedID : null,
            requestCode, 0, null, options);
    ...
}
  1. ActivityManagerService
public final int startActivity(IApplicationThread caller, String callingPackage,
    Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
    int startFlags, ProfilerInfo profilerInfo, Bundle bOptions) {
    ...
    return startActivityAsUser(caller, callingPackage, intent, resolvedType, resultTo,
                resultWho, requestCode, startFlags, profilerInfo, bOptions,
                UserHandle.getCallingUserId());
}
     
            
public final int startActivityAsUser(IApplicationThread caller, String callingPackage,
    Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
            int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, int userId) {
    ...
    return mActivityStarter.startActivityMayWait(caller, -1, callingPackage, intent,
                resolvedType, null, null, resultTo, resultWho, requestCode, startFlags,
                profilerInfo, null, null, bOptions, false, userId, null, "startActivityAsUser");
}
  1. ActivityStarter
final int startActivityMayWait(IApplicationThread caller, int callingUid,
    String callingPackage, Intent intent, String resolvedType,
    IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
    IBinder resultTo, String resultWho, int requestCode, int startFlags,
    ProfilerInfo profilerInfo, WaitResult outResult,
    Configuration globalConfig, Bundle bOptions, boolean ignoreTargetSecurity, 
    int userId,askRecord inTask, String reason) { 
    ...
    int res = startActivityLocked(caller, intent, ephemeralIntent, resolvedType,
        aInfo, rInfo, voiceSession, voiceInteractor,
        resultTo, resultWho, requestCode, callingPid,
        callingUid, callingPackage, realCallingPid, realCallingUid, startFlags,
        options, ignoreTargetSecurity, componentSpecified, outRecord, inTask,
        reason);
    ...
}
        

int startActivityLocked(IApplicationThread caller, Intent intent, Intent ephemeralIntent,
    String resolvedType, ActivityInfo aInfo, ResolveInfo rInfo,
    IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
    IBinder resultTo, String resultWho, int requestCode, int callingPid, int callingUid,
    String callingPackage, int realCallingPid, int realCallingUid, int startFlags,
    ActivityOptions options, boolean ignoreTargetSecurity, boolean componentSpecified,
    ActivityRecord[] outActivity, TaskRecord inTask, String reason) {
    ...
    mLastStartActivityResult = startActivity(caller, intent, ephemeralIntent, resolvedType,
    aInfo, rInfo, voiceSession, voiceInteractor, resultTo, resultWho, requestCode,
    callingPid, callingUid, callingPackage, realCallingPid, realCallingUid, startFlags,
    options, ignoreTargetSecurity, componentSpecified, mLastStartActivityRecord,
            inTask);
    ...
}
            
private int startActivity(IApplicationThread caller, Intent intent, Intent ephemeralIntent,
    String resolvedType, ActivityInfo aInfo, ResolveInfo rInfo,
    IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
    IBinder resultTo, String resultWho, int requestCode, int callingPid, int callingUid,
    String callingPackage, int realCallingPid, int realCallingUid, int startFlags,
    ActivityOptions options, boolean ignoreTargetSecurity, boolean componentSpecified,
    ActivityRecord[] outActivity, TaskRecord inTask) {
    ...
    doPendingActivityLaunchesLocked(false);
    return startActivity(r, sourceRecord, voiceSession, voiceInteractor, startFlags, true,
    options, inTask, outActivity);
}
            

private int startActivity(final ActivityRecord r, ActivityRecord sourceRecord,
    IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
    int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask,
    ActivityRecord[] outActivity) {
    ...
    result = startActivityUnchecked(r, sourceRecord, voiceSession, voiceInteractor,
        startFlags, doResume, options, inTask, outActivity);
    ...
}  
            
private int startActivityUnchecked(final ActivityRecord r, ActivityRecord sourceRecord,
    IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
    int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask,
    ActivityRecord[] outActivity) {
    ...
    if (mDoResume) {
        mSupervisor.resumeFocusedStackTopActivityLocked();
    }
    ...
}
  1. ActivityStackSupervisor
boolean resumeFocusedStackTopActivityLocked() {
    return resumeFocusedStackTopActivityLocked(null, null, null);
}
    
boolean resumeFocusedStackTopActivityLocked(
    ActivityStack targetStack, ActivityRecord target, ActivityOptions targetOptions) {
    ...
    if (r == null || r.state != RESUMED) {
        mFocusedStack.resumeTopActivityUncheckedLocked(null, null);
    } 
    ...
}
  1. ActivityStack
boolean resumeTopActivityUncheckedLocked(ActivityRecord prev, ActivityOptions options) {
    ...
    result = resumeTopActivityInnerLocked(prev, options);
    ...
}

private boolean resumeTopActivityInnerLocked(ActivityRecord prev, ActivityOptions options) { 
   ....
    if (mResumedActivity != null) {
        //同步等待pause当前Activity的结果
        pausing |= startPausingLocked(userLeaving, false, next, false);
    }
    ....
      //开始启动下一个Activity
      mStackSupervisor.startSpecificActivityLocked(next, true, false);
    ....
}


final boolean startPausingLocked(boolean userLeaving, boolean uiSleeping, ActivityRecord resuming, boolean pauseImmediately) {
    ....
    //去当前Activity所在应用进程暂停当前activity
     prev.app.thread.schedulePauseActivity(prev.appToken, prev.finishing,
            userLeaving, prev.configChangeFlags, pauseImmediately);
    ....
}
       
  1. ActivityThread$ApplicationThread
public final void schedulePauseActivity(IBinder token, boolean finished, boolean userLeaving, int configChanges, boolean dontReport) {
    ...
    sendMessage(finished ? H.PAUSE_ACTIVITY_FINISHING: H.PAUSE_ACTIVITY, token,
    (userLeaving ? USER_LEAVING: 0) | (dontReport ? DONT_REPORT: 0), configChanges, seq);
    ...
}
  1. ActivityThread
private void sendMessage(int what, Object obj, int arg1, int arg2, int seq) {
    Message msg = Message.obtain();
    ....
    mH.sendMessage(msg);
}
  1. ActivityThread$H
public void handleMessage(Message msg) {
   ...
    switch (msg.what) {
    case PAUSE_ACTIVITY:
        {
            SomeArgs args = (SomeArgs) msg.obj;
            handlePauseActivity((IBinder) args.arg1, false, (args.argi1 & USER_LEAVING) != 0,
            args.argi2, (args.argi1 & DONT_REPORT) != 0, args.argi3);
        }
        break;
    }
    ...
}
  1. ActivityThread
private void handlePauseActivity(IBinder token, boolean finished, 
    boolean userLeaving, int configChanges, boolean dontReport, int seq) {
    ...
    performPauseActivity(token, finished, r.isPreHoneycomb(), "handlePauseActivity");
    ...
    //执行完后通知AMS当前Activity已经pause
    ActivityManager.getService().activityPaused(token);
    ...
}

final Bundle performPauseActivity(ActivityClientRecord r, boolean finished,
     boolean saveState, String reason) {
         ...
         performPauseActivityIfNeeded(r, reason);
         ...
}


private void performPauseActivityIfNeeded(ActivityClientRecord r, String reason) {
    ...
     mInstrumentation.callActivityOnPause(r.activity);
    ...
}
  1. Instrumentation
public void callActivityOnPause(Activity activity) {
    activity.performPause();
}
  1. Activity
final void performPause() {
    ...
    onPause();
    ...
}

由于在ActivityThread中handlePauseActivity的方法里,在pause成功后,需要通知AMS已经pause成功,所以接着分析ActivityManagerService.activityPaused方法。

  1. ActivityManagerService
public final void activityPaused(IBinder token) {
    final long origId = Binder.clearCallingIdentity();
    synchronized(this) {
        ActivityStack stack = ActivityRecord.getStackLocked(token);
        if (stack != null) {
            stack.activityPausedLocked(token, false);
        }
    }
    Binder.restoreCallingIdentity(origId);
}
  1. ActivityStack
final void activityPausedLocked(IBinder token, boolean timeout) {
    ...
    mHandler.removeMessages(PAUSE_TIMEOUT_MSG, r);
    ...
    completePauseLocked(true /* resumeNext */, null /* resumingActivity */);
    ...
}

private void completePauseLocked(boolean resumeNext, ActivityRecord resuming) {
    ...
    mStackSupervisor.resumeFocusedStackTopActivityLocked(topStack, prev, null);
    ...
}
  1. ActivityStackSupervisor
boolean resumeFocusedStackTopActivityLocked(ActivityStack targetStack, ActivityRecord target, ActivityOptions targetOptions) {
    //如果启动Activity和要启动的Activity在同一个ActivityStack中,调用targetStack对象的方法
    if (targetStack != null && isFocusedStack(targetStack)) {
        return targetStack.resumeTopActivityUncheckedLocked(target, targetOptions);
    }
    //如果不在同一个ActivityStack中,则调用mFocusStack对象的方法
    if (r == null || r.state != RESUMED) {
        mFocusedStack.resumeTopActivityUncheckedLocked(null, null);
    }

    return false;
}
  1. ActivityStatck
boolean resumeTopActivityUncheckedLocked(ActivityRecord prev, ActivityOptions options) {
    ...
    result = resumeTopActivityInnerLocked(prev, options);
    ...
}

private boolean resumeTopActivityInnerLocked(ActivityRecord prev, ActivityOptions options) { 
   ...
    if (mResumedActivity != null) {
        //同步等待pause当前Activity的结果,但在pause时,已经把mResumedActivity置为了null,所以不走这里
        pausing |= startPausingLocked(userLeaving, false, next, false);
        ...
    }
    ...
    return true;
    ...
      //开始启动下一个Activity
      mStackSupervisor.startSpecificActivityLocked(next, true, false);
    ...
}
  1. ActivityStackSupervisor
void startSpecificActivityLocked(ActivityRecord r,
    boolean andResume, boolean checkConfig) { 
    if (app != null && app.thread != null) { 
        ...
        realStartActivityLocked(r, app, andResume, checkConfig);
        return;
    }
    ...
    //启动跨进程的Activity需要先开启新的应用进程
    mService.startProcessLocked(r.processName, r.info.applicationInfo, true, 0,
        "activity", r.intent.getComponent(), false, false, true);
}

final boolean realStartActivityLocked(ActivityRecord r, ProcessRecord app,
    boolean andResume, boolean checkConfig) throws RemoteException { 
    ...
    app.thread.scheduleLaunchActivity(new Intent(r.intent), r.appToken,
        System.identityHashCode(r), r.info,
        // TODO: Have this take the merged configuration instead of separate global
        // and override configs.
        mergedConfiguration.getGlobalConfiguration(),
        mergedConfiguration.getOverrideConfiguration(), r.compat,
        r.launchedFromPackage, task.voiceInteractor, app.repProcState, r.icicle,
        r.persistentState, results, newIntents, !andResume,
        mService.isNextTransitionForward(), profilerInfo);
    ...
}
  1. ActivityThread$ApplicationThread
public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
    ActivityInfo info, Configuration curConfig, Configuration overrideConfig,
    CompatibilityInfo compatInfo, String referrer, IVoiceInteractor voiceInteractor,
    int procState, Bundle state, PersistableBundle persistentState,
    List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents,
    boolean notResumed, boolean isForward, ProfilerInfo profilerInfo) {
    ...
    sendMessage(H.LAUNCH_ACTIVITY, r);
}
  1. ActivityThread
private void sendMessage(int what, Object obj) {
    sendMessage(what, obj, 0, 0, false);
}
 
 
private void sendMessage(int what, Object obj, int arg1, int arg2, boolean async) {
    ...
    mH.sendMessage(msg);
} 
 
  1. ActivityThread$H
public void handleMessage(Message msg) {
    ...
    switch (msg.what) {
    case LAUNCH_ACTIVITY:
        {
            ....
            handleLaunchActivity(r, null, "LAUNCH_ACTIVITY");
            ....
        }
        break;
        ...
    }
}
  1. ActivityThread
private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {
    ...
    Activity a = performLaunchActivity(r, customIntent);
}

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent){ 
    ...
    if (r.isPersistable()) {
        mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
    } else {
        mInstrumentation.callActivityOnCreate(activity, r.state);
    }
    ...
}
  1. Instumentation
public void callActivityOnCreate(Activity activity, Bundle icicle) {
    prePerformCreate(activity);
    activity.performCreate(icicle);
    postPerformCreate(activity);
}
  1. Activity
final void performCreate(Bundle icicle) {
    performCreate(icicle, null);
}

final void performCreate(Bundle icicle, PersistableBundle persistentState) {
    ...
    if (persistentState != null) {
        onCreate(icicle, persistentState);
    } else {
        onCreate(icicle);
    }
   ...
}

Activity启动流程

为了叙述方便,我们假设Activity A要启动Activity B。

  1. Activity A向AMS发送一个启动Activity B的进程间通信请求;
  2. AMS会将要启动的Activity B的组件信息保存下来,然后通过Binder通信(ApplicationThread及其接口定义语言),让Activity A执行pause操作;
  3. Activity B完成pause操作后,通过Binder通信(ActivityManagerService及其接口定义语言)通知AMS,可以执行启动Activity B的操作了(要启动的activity信息保存在了栈顶);
  4. 在启动之前,如果发现Activity B的应用程序进程不存在,会先启动一个新的进程(上述调用栈没涉及,同学们可自行查看源码);
  5. AMS执行一系列启动Activity B的操作,并通过Binder通信(ApplicationThread及其接口定义语言)进行跨进程调用,将Activity B启动起来;

关于AMS

  • AMS的启动是在SystemServer(系统进程)中启动的,同其他Android应用一样,也是由Zygote进程fork出来的:
public final class SystemServer {

    /**
     * The main entry point from zygote.
     * 注释写的很清楚了,SystemServer进程是有Zygote进程fork出来的
     */
    public static void main(String[] args) {
        new SystemServer().run();
    }    
    
    private void run() {
        ...
         startBootstrapServices();
        ...
    }
    
    private void startBootstrapServices() {
        ...
        // Activity manager runs the show.
        traceBeginAndSlog("StartActivityManager");
        //开启ActivityManagerService进程
        mActivityManagerService = mSystemServiceManager.startService(
                ActivityManagerService.Lifecycle.class).getService();
        mActivityManagerService.setSystemServiceManager(mSystemServiceManager);
        mActivityManagerService.setInstaller(installer);
        traceEnd();        
        ...
    }    
}
  • AMS与APP进程的通信:
  1. APP进程通过IActivityManager.aidl接口向AMS进程进行通信。
    ActivityManager.getService()获得AMS的Binder接口,再通过Stub.asInterface的方式,转成IActivityManager的接口,通过IActivityManager与AMS进行通信,实现RPC远程跨进程调用;
    Instrumentation
int result = ActivityManager.getService()
                .startActivity(whoThread, who.getBasePackageName(), intent,
                        intent.resolveTypeIfNeeded(who.getContentResolver()),
                        token, target, requestCode, 0, null, options);

ActivityManager

public static IActivityManager getService() {
        return IActivityManagerSingleton.get();
}
 
private static final Singleton<IActivityManager> IActivityManagerSingleton =
    new Singleton<IActivityManager>() {
        @Override
        protected IActivityManager create() {
            final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);
            final IActivityManager am = IActivityManager.Stub.asInterface(b);
            return am;
        }
};

  1. AMS进程通过IApplication.aidl接口向APP进程进行通信。
    在AMS内部持有每个ActivityThread的IApplicatinThread接口实例,用时可以直接调用。同IActivityManager,也是通过Binder进行进程间通信。

参考链接
【凯子哥带你学Framework】Activity启动过程全解析
Activity启动流程简直丧心病狂!

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

推荐阅读更多精彩内容