HomeActivity啟動(dòng)流程(launcher)

0. 寫(xiě)在前面的話

上一篇講了各種Service是在何時(shí)啟動(dòng)的什往,最后提到了關(guān)于HomeActivity的啟動(dòng)冒萄。HomeActivity作為Android系統(tǒng)啟動(dòng)后第一個(gè)加載的Activity堂鲜,今天就來(lái)分析下其啟動(dòng)流程累奈。
其實(shí)還有個(gè)時(shí)序圖的趁餐,但是太大了俐芯,截圖也不全,等下次直接上傳喘蟆,方便自己查看缓升。
暫時(shí)的規(guī)劃先是Activity的啟動(dòng)流程,通過(guò)這個(gè)又會(huì)涉及到進(jìn)程間通信——Binder機(jī)制蕴轨,接著Activity啟動(dòng)后港谊,布局是如何繪制到屏幕上,View的事件是如何分發(fā)的橙弱?通過(guò)這條線來(lái)慢慢整理這兩年多我對(duì)Android的理解歧寺,加深自己的印象燥狰。


1. ActivityManagerService.systemReady(new Runnable())做了什么?

我們先來(lái)看下這個(gè)方法的代碼:

public void systemReady(final Runnable goingCallback) {
    synchronized(this) {
        //mSystemReady默認(rèn)為false斜筐,所以第一次調(diào)用這個(gè)方法時(shí)不會(huì)執(zhí)行g(shù)oingCallback.run()
        if (mSystemReady) {
            if (goingCallback != null) {
                goingCallback.run();
            }
            return;
        }
        ......
    }
    //要?dú)⒌舻倪M(jìn)程(啟動(dòng)時(shí)不允許啟動(dòng)的進(jìn)程)
    ArrayList<ProcessRecord> procsToKill = null;
    synchronized(mPidsSelfLocked) {
        for (int i=mPidsSelfLocked.size()-1; i>=0; i--) {
            ProcessRecord proc = mPidsSelfLocked.valueAt(i);
            if (!isAllowedWhileBooting(proc.info)){
                if (procsToKill == null) {
                    procsToKill = new ArrayList<ProcessRecord>();
                }
                procsToKill.add(proc);
            }
        }
    }
    //殺掉這些進(jìn)程
    synchronized(this) {
        if (procsToKill != null) {
            for (int i=procsToKill.size()-1; i>=0; i--) {
                ProcessRecord proc = procsToKill.get(i);
                Slog.i(TAG, "Removing system update proc: " + proc);
                removeProcessLocked(proc, true, false, "system update done");
            }
        }

        // Now that we have cleaned up any update processes, we
        // are ready to start launching real processes and know that
        // we won't trample on them any more.
        mProcessesReady = true;
    }
    ......
    //這里會(huì)調(diào)用Runnable的run()方法
    if (goingCallback != null) goingCallback.run();
    ......
    synchronized (this) {
        //啟動(dòng)常駐的應(yīng)用
        startPersistentApps(PackageManager.MATCH_DIRECT_BOOT_AWARE);
        mBooting = true;
        if (UserManager.isSplitSystemUser()) {
            ComponentName cName = new ComponentName(mContext, SystemUserHomeActivity.class);
            try {
                //解禁SystemUserHomeActivity
                AppGlobals.getPackageManager().setComponentEnabledSetting(cName,
                        PackageManager.COMPONENT_ENABLED_STATE_ENABLED, 0,
                        UserHandle.USER_SYSTEM);
            } catch (RemoteException e) {
                throw e.rethrowAsRuntimeException();
            }
        }
        //開(kāi)始啟動(dòng)HomeActivity
        startHomeActivityLocked(currentUserId, "systemReady");
        ......
    }
}

這個(gè)方法內(nèi)容挺多的龙致,首次進(jìn)入是設(shè)置的Runnable不會(huì)執(zhí)行,后面通過(guò)查找啟動(dòng)時(shí)系統(tǒng)不允許啟動(dòng)的進(jìn)程顷链,并將其kill掉目代。接下來(lái)啟動(dòng)了系統(tǒng)常駐應(yīng)用startPersistentApps(PackageManager.MATCH_DIRECT_BOOT_AWARE),通過(guò)使用AppGlobals.getPackageManager().setComponentEnabledSetting方法解禁SystemUserHomeActivity蕴潦,其flag為PackageManager.COMPONENT_ENABLED_STATE_ENABLED像啼,最后通過(guò)startHomeActivityLocked(currentUserId, "systemReady")來(lái)啟動(dòng)HomeActivity


2. startHomeActivityLocked開(kāi)始啟動(dòng)HomeActivity

這里的代碼不多潭苞,也很好理解:

boolean startHomeActivityLocked(int userId, String reason) {
    //測(cè)試模式下不用管
    if (mFactoryTest == FactoryTest.FACTORY_TEST_LOW_LEVEL
            && mTopAction == null) {
        // We are running in factory test mode, but unable to find
        // the factory test app, so just sit around displaying the
        // error message and don't try to start anything.
        return false;
    }
    //得到要啟動(dòng)Activity的Intent
    Intent intent = getHomeIntent();
    //將Intent解析成ActivityInfo
    ActivityInfo aInfo = resolveActivityInfo(intent, STOCK_PM_FLAGS, userId);
    if (aInfo != null) {
        intent.setComponent(new ComponentName(aInfo.applicationInfo.packageName, aInfo.name));
        // Don't do this if the home app is currently being
        // instrumented.
        aInfo = new ActivityInfo(aInfo);
        aInfo.applicationInfo = getAppInfoForUser(aInfo.applicationInfo, userId);
        ProcessRecord app = getProcessRecordLocked(aInfo.processName,
                aInfo.applicationInfo.uid, true);
        if (app == null || app.instrumentationClass == null) {
            intent.setFlags(intent.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK);
            //啟動(dòng)Activity
            mActivityStarter.startHomeActivityLocked(intent, aInfo, reason);
        }
    } else {
        Slog.wtf(TAG, "No home screen found for " + intent, new Throwable());
    }

    return true;
}

Intent getHomeIntent() {
    Intent intent = new Intent(mTopAction, mTopData != null ? Uri.parse(mTopData) : null);
    intent.setComponent(mTopComponent);
    intent.addFlags(Intent.FLAG_DEBUG_TRIAGED_MISSING);
    //不是測(cè)試情況下則設(shè)置Category為Intent.CATEGORY_HOME
    if (mFactoryTest != FactoryTest.FACTORY_TEST_LOW_LEVEL) {
        intent.addCategory(Intent.CATEGORY_HOME);
    }
    return intent;
}

首先是判斷當(dāng)前系統(tǒng)是否處于測(cè)試環(huán)境忽冻,接著去獲取(創(chuàng)建)HomeIntent此疹,并將其解析成ActivityInfo僧诚,接下來(lái)調(diào)用ActivityStarter中的startHomeActivityLocked(intent, aInfo, reason)方法去啟動(dòng)HomeActivity


3. startHomeActivityLockedstartActivityLocked

我們進(jìn)入ActivityStarter中可以看到startHomeActivityLocked調(diào)用了startActivityLocked方法蝗碎,其中傳入的參數(shù)只有IntentActivityInfo不為空湖笨,其他的都為null或者0或者false,由于大部分參數(shù)都為null蹦骑,startActivityLocked中真正執(zhí)行的代碼并不是很多慈省。

void startHomeActivityLocked(Intent intent, ActivityInfo aInfo, String reason) {
    mSupervisor.moveHomeStackTaskToTop(HOME_ACTIVITY_TYPE, reason);
    //調(diào)用了參數(shù)居多的方法
    startActivityLocked(null /*caller*/, intent, null /*ephemeralIntent*/,
            null /*resolvedType*/, aInfo, null /*rInfo*/, null /*voiceSession*/,
            null /*voiceInteractor*/, null /*resultTo*/, null /*resultWho*/,
            0 /*requestCode*/, 0 /*callingPid*/, 0 /*callingUid*/, null /*callingPackage*/,
            0 /*realCallingPid*/, 0 /*realCallingUid*/, 0 /*startFlags*/, null /*options*/,
            false /*ignoreTargetSecurity*/, false /*componentSpecified*/, null /*outActivity*/,
            null /*container*/, null /*inTask*/);
    if (mSupervisor.inResumeTopActivity) {
        // If we are in resume section already, home activity will be initialized, but not
        // resumed (to avoid recursive resume) and will stay that way until something pokes it
        // again. We need to schedule another resume.
        mSupervisor.scheduleResumeTopActivities();
    }
}
//這個(gè)參數(shù)是真TM的多啊
final 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, ActivityStackSupervisor.ActivityContainer container,
        TaskRecord inTask) {
    //啟動(dòng)結(jié)果代碼
    int err = ActivityManager.START_SUCCESS;
    
    ProcessRecord callerApp = null;
    //caller == null
    if (caller != null) {
        ......
    }
    
    final int userId = aInfo != null ? UserHandle.getUserId(aInfo.applicationInfo.uid) : 0;

    if (err == ActivityManager.START_SUCCESS) {
        ..Log..
    }

    ActivityRecord sourceRecord = null;
    ActivityRecord resultRecord = null;
    //resultTo == null
    if (resultTo != null) {
        ......
    }

    final int launchFlags = intent.getFlags();
    //Activity啟動(dòng)是設(shè)置的flags
    if ((launchFlags & Intent.FLAG_ACTIVITY_FORWARD_RESULT) != 0 && sourceRecord != null) {
        ......
    }

    ......
    
    //檢查啟動(dòng)Activity的權(quán)限
    boolean abort = !mSupervisor.checkStartAnyActivityPermission(intent, aInfo, resultWho,
            requestCode, callingPid, callingUid, callingPackage, ignoreTargetSecurity, callerApp,
            resultRecord, resultStack, options);
    abort |= !mService.mIntentFirewall.checkStartActivity(intent, callingUid,
            callingPid, resolvedType, aInfo.applicationInfo);

    if (mService.mController != null) {
        try {
            // The Intent we give to the watcher has the extra data
            // stripped off, since it can contain private information.
            Intent watchIntent = intent.cloneFilter();
            abort |= !mService.mController.activityStarting(watchIntent,
                    aInfo.applicationInfo.packageName);
        } catch (RemoteException e) {
            mService.mController = null;
        }
    }

    ......

    // If we have an ephemeral app, abort the process of launching the resolved intent.
    // Instead, launch the ephemeral installer. Once the installer is finished, it
    // starts either the intent we resolved here [on install error] or the ephemeral
    // app [on install success].
    if (rInfo != null && rInfo.ephemeralResolveInfo != null) {
        intent = buildEphemeralInstallerIntent(intent, ephemeralIntent,
                rInfo.ephemeralResolveInfo.getPackageName(), callingPackage, resolvedType,
                userId);
        resolvedType = null;
        callingUid = realCallingUid;
        callingPid = realCallingPid;

        aInfo = mSupervisor.resolveActivity(intent, rInfo, startFlags, null /*profilerInfo*/);
    }
    //創(chuàng)建了ActivityRecord,用于存儲(chǔ)保存Activity
    ActivityRecord r = new ActivityRecord(mService, callerApp, callingUid, callingPackage,
            intent, resolvedType, aInfo, mService.mConfiguration, resultRecord, resultWho,
            requestCode, componentSpecified, voiceSession != null, mSupervisor, container,
            options, sourceRecord);
    ......

    try {
        mService.mWindowManager.deferSurfaceLayout();
        //啟動(dòng)Activity
        err = startActivityUnchecked(r, sourceRecord, voiceSession, voiceInteractor, startFlags,
                true, options, inTask);
    } finally {
        mService.mWindowManager.continueSurfaceLayout();
    }
    postStartActivityUncheckedProcessing(r, err, stack.mStackId, mSourceRecord, mTargetStack);
    return err;
}

雖然上面的方法參數(shù)很多眠菇,但是由于大部分都為null或者0边败,所以真正執(zhí)行的部分很少。先進(jìn)行對(duì)flag的檢查捎废,我們的intent只設(shè)置了Intent.FLAG_DEBUG_TRIAGED_MISSING笑窜,所以對(duì)flag判斷的部分執(zhí)行也不執(zhí)行。權(quán)限檢查!mSupervisor.checkStartAnyActivityPermission登疗,如果沒(méi)有權(quán)限排截,則最終不會(huì)啟動(dòng)。我們要啟動(dòng)的是HomeActivity辐益,所有需要的權(quán)限都有断傲,因此創(chuàng)建一個(gè)ActivityRecord,用于存放Activity的信息智政,最后調(diào)用startActivityUnchecked執(zhí)行啟動(dòng)過(guò)程艳悔。


4. startActivityUnchecked過(guò)程

startActivityUnchecked這個(gè)方法做了很多事情,包括了對(duì)當(dāng)前的ActivitylaunchMode的設(shè)置女仰、處理猜年,我們要啟動(dòng)的是HomeActivity,所以呢疾忍,我們并不care這些東西乔外。

private int startActivityUnchecked(final ActivityRecord r, ActivityRecord sourceRecord,
        IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
        int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask) {
    //初始化,這里需要注意的一點(diǎn)是doResume這個(gè)參數(shù)我們傳過(guò)來(lái)的是true一罩,下面會(huì)用到
    setInitialState(r, options, inTask, doResume, startFlags, sourceRecord, voiceSession,
            voiceInteractor);
    //設(shè)置launchMode flag
    computeLaunchingTaskFlags();

    computeSourceStack();

    mIntent.setFlags(mLaunchFlags);

    mReusedActivity = getReusableIntentActivity();

    final int preferredLaunchStackId =
            (mOptions != null) ? mOptions.getLaunchStackId() : INVALID_STACK_ID;

    if (mReusedActivity != null) {
        // When the flags NEW_TASK and CLEAR_TASK are set, then the task gets reused but
        // still needs to be a lock task mode violation since the task gets cleared out and
        // the device would otherwise leave the locked task.
        if (mSupervisor.isLockTaskModeViolation(mReusedActivity.task,
                (mLaunchFlags & (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK))
                        == (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK))) {
            mSupervisor.showLockTaskToast();
            Slog.e(TAG, "startActivityUnchecked: Attempt to violate Lock Task Mode");
            return START_RETURN_LOCK_TASK_MODE_VIOLATION;
        }

        if (mStartActivity.task == null) {
            mStartActivity.task = mReusedActivity.task;
        }
        if (mReusedActivity.task.intent == null) {
            // This task was started because of movement of the activity based on affinity...
            // Now that we are actually launching it, we can assign the base intent.
            mReusedActivity.task.setIntent(mStartActivity);
        }

        // This code path leads to delivering a new intent, we want to make sure we schedule it
        // as the first operation, in case the activity will be resumed as a result of later
        // operations.
        //這里是處理launchMode為FLAG_ACTIVITY_CLEAR_TOP的情況
        if ((mLaunchFlags & FLAG_ACTIVITY_CLEAR_TOP) != 0
                || mLaunchSingleInstance || mLaunchSingleTask) {
            // In this situation we want to remove all activities from the task up to the one
            // being started. In most cases this means we are resetting the task to its initial
            // state.
            final ActivityRecord top = mReusedActivity.task.performClearTaskForReuseLocked(
                    mStartActivity, mLaunchFlags);
            if (top != null) {
                if (top.frontOfTask) {
                    // Activity aliases may mean we use different intents for the top activity,
                    // so make sure the task now has the identity of the new intent.
                    top.task.setIntent(mStartActivity);
                }
                ActivityStack.logStartActivity(AM_NEW_INTENT, mStartActivity, top.task);
                //去掉用Activity的onNewIntent方法
                top.deliverNewIntentLocked(mCallingUid, mStartActivity.intent,
                        mStartActivity.launchedFromPackage);
            }
        }

        sendPowerHintForLaunchStartIfNeeded(false /* forceSend */);

        mReusedActivity = setTargetStackAndMoveToFrontIfNeeded(mReusedActivity);

        if ((mStartFlags & START_FLAG_ONLY_IF_NEEDED) != 0) {
            ......
        }
        setTaskFromIntentActivity(mReusedActivity);

        if (!mAddingToTask && mReuseTask == null) {
            ......
        }
    }

    if (mStartActivity.packageName == null) {
        if (mStartActivity.resultTo != null && mStartActivity.resultTo.task.stack != null) {
            mStartActivity.resultTo.task.stack.sendActivityResultLocked(
                    -1, mStartActivity.resultTo, mStartActivity.resultWho,
                    mStartActivity.requestCode, RESULT_CANCELED, null);
        }
        ActivityOptions.abort(mOptions);
        //啟動(dòng)失敗
        return START_CLASS_NOT_FOUND;
    }

    // If the activity being launched is the same as the one currently at the top, then
    // we need to check if it should only be launched once.
    //如果當(dāng)前的Activity的launchModeFLAG_ACTIVITY_SINGLE_TOP杨幼,并且是處于任務(wù)棧的棧頂,
    //則不用啟動(dòng)聂渊,并調(diào)用Activity的onNewIntent方法
    final ActivityStack topStack = mSupervisor.mFocusedStack;
    final ActivityRecord top = topStack.topRunningNonDelayedActivityLocked(mNotTop);
    final boolean dontStart = top != null && mStartActivity.resultTo == null
            && top.realActivity.equals(mStartActivity.realActivity)
            && top.userId == mStartActivity.userId
            && top.app != null && top.app.thread != null
            && ((mLaunchFlags & FLAG_ACTIVITY_SINGLE_TOP) != 0
            || mLaunchSingleTop || mLaunchSingleTask);
    if (dontStart) {
        ActivityStack.logStartActivity(AM_NEW_INTENT, top, top.task);
        // For paranoia, make sure we have correctly resumed the top activity.
        topStack.mLastPausedActivity = null;
        if (mDoResume) {
            mSupervisor.resumeFocusedStackTopActivityLocked();
        }
        ActivityOptions.abort(mOptions);
        if ((mStartFlags & START_FLAG_ONLY_IF_NEEDED) != 0) {
            // We don't need to start a new activity, and the client said not to do
            // anything if that is the case, so this is it!
            return START_RETURN_INTENT_TO_CALLER;
        }
        //調(diào)用Activity的onNewIntent方法
        top.deliverNewIntentLocked(
                mCallingUid, mStartActivity.intent, mStartActivity.launchedFromPackage);

        // Don't use mStartActivity.task to show the toast. We're not starting a new activity
        // but reusing 'top'. Fields in mStartActivity may not be fully initialized.
        mSupervisor.handleNonResizableTaskIfNeeded(
                top.task, preferredLaunchStackId, topStack.mStackId);

        return START_DELIVERED_TO_TOP;
    }

    boolean newTask = false;
    final TaskRecord taskToAffiliate = (mLaunchTaskBehind && mSourceRecord != null)
            ? mSourceRecord.task : null;

    // Should this be considered a new task?
    if (mStartActivity.resultTo == null && mInTask == null && !mAddingToTask
            && (mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0) {
        newTask = true;
        setTaskFromReuseOrCreateNewTask(taskToAffiliate);

        if (mSupervisor.isLockTaskModeViolation(mStartActivity.task)) {
            Slog.e(TAG, "Attempted Lock Task Mode violation mStartActivity=" + mStartActivity);
            return START_RETURN_LOCK_TASK_MODE_VIOLATION;
        }
        if (!mMovedOtherTask) {
            // If stack id is specified in activity options, usually it means that activity is
            // launched not from currently focused stack (e.g. from SysUI or from shell) - in
            // that case we check the target stack.
            updateTaskReturnToType(mStartActivity.task, mLaunchFlags,
                    preferredLaunchStackId != INVALID_STACK_ID ? mTargetStack : topStack);
        }
    } else if (mSourceRecord != null) {
        if (mSupervisor.isLockTaskModeViolation(mSourceRecord.task)) {
            Slog.e(TAG, "Attempted Lock Task Mode violation mStartActivity=" + mStartActivity);
            return START_RETURN_LOCK_TASK_MODE_VIOLATION;
        }

        final int result = setTaskFromSourceRecord();
        if (result != START_SUCCESS) {
            return result;
        }
    } else if (mInTask != null) {
        // The caller is asking that the new activity be started in an explicit
        // task it has provided to us.
        if (mSupervisor.isLockTaskModeViolation(mInTask)) {
            Slog.e(TAG, "Attempted Lock Task Mode violation mStartActivity=" + mStartActivity);
            return START_RETURN_LOCK_TASK_MODE_VIOLATION;
        }

        final int result = setTaskFromInTask();
        if (result != START_SUCCESS) {
            return result;
        }
    } else {
        // This not being started from an existing activity, and not part of a new task...
        // just put it in the top task, though these days this case should never happen.
        setTaskToCurrentTopOrCreateNewTask();
    }

    mService.grantUriPermissionFromIntentLocked(mCallingUid, mStartActivity.packageName,
            mIntent, mStartActivity.getUriPermissionsLocked(), mStartActivity.userId);

    if (mSourceRecord != null && mSourceRecord.isRecentsActivity()) {
        mStartActivity.task.setTaskToReturnTo(RECENTS_ACTIVITY_TYPE);
    }
    if (newTask) {
        EventLog.writeEvent(
                EventLogTags.AM_CREATE_TASK, mStartActivity.userId, mStartActivity.task.taskId);
    }
    ActivityStack.logStartActivity(
            EventLogTags.AM_CREATE_ACTIVITY, mStartActivity, mStartActivity.task);
    mTargetStack.mLastPausedActivity = null;

    sendPowerHintForLaunchStartIfNeeded(false /* forceSend */);
    
    mTargetStack.startActivityLocked(mStartActivity, newTask, mKeepCurTransition, mOptions);
    //mDoResume設(shè)置為true
    if (mDoResume) {
        ......
        final ActivityRecord topTaskActivity = mStartActivity.task.topRunningActivityLocked();
        if (!mTargetStack.isFocusable()
                || (topTaskActivity != null && topTaskActivity.mTaskOverlay
                && mStartActivity != topTaskActivity)) {
            // If the activity is not focusable, we can't resume it, but still would like to
            // make sure it becomes visible as it starts (this will also trigger entry
            // animation). An example of this are PIP activities.
            // Also, we don't want to resume activities in a task that currently has an overlay
            // as the starting activity just needs to be in the visible paused state until the
            // over is removed.
            mTargetStack.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
            // Go ahead and tell window manager to execute app transition for this activity
            // since the app transition will not be triggered through the resume channel.
            mWindowManager.executeAppTransition();
        } else {
            //執(zhí)行此方法
            mSupervisor.resumeFocusedStackTopActivityLocked(mTargetStack, mStartActivity,
                    mOptions);
        }
    } else {
        mTargetStack.addRecentActivityLocked(mStartActivity);
    }
    mSupervisor.updateUserStackLocked(mStartActivity.userId, mTargetStack);

    mSupervisor.handleNonResizableTaskIfNeeded(
            mStartActivity.task, preferredLaunchStackId, mTargetStack.mStackId);

    return START_SUCCESS;
}

在這個(gè)方法中差购,我們看到了對(duì)Activity的不同的launchMode的處理,我們要啟動(dòng)的HomeActivity只會(huì)是啟動(dòng)一個(gè)新的Task汉嗽,所以大部分操作還是啟動(dòng)其他Activity時(shí)進(jìn)行的欲逃。代碼里面也講到了,我們?cè)诔跏蓟?code>mDoResume傳入了true饼暑,代碼會(huì)執(zhí)行mSupervisor.resumeFocusedStackTopActivityLocked(mTargetStack, mStartActivity,mOptions);稳析。


5. 方法的各種調(diào)用直至resumeTopActivityInnerLocked

上面講到我們會(huì)執(zhí)行ActivityStackSupervisorresumeFocusedStackTopActivityLocked方法,跟進(jìn)方法后會(huì)發(fā)現(xiàn)其又會(huì)調(diào)用ActivityStackresumeTopActivityUncheckedLocked方法弓叛,這個(gè)方法會(huì)調(diào)用自身的resumeTopActivityInnerLocked方法彰居,在resumeTopActivityInnerLocked中,會(huì)根據(jù)當(dāng)前的Activity Task中是否有需要pause的Activity撰筷,如果有先去執(zhí)行pause陈惰,沒(méi)有的話則去啟動(dòng)Activity。
大致代碼

private boolean resumeTopActivityInnerLocked(ActivityRecord prev, ActivityOptions options) {
    // Find the first activity that is not finishing.
    //這里看了很久毕籽,后來(lái)從前面的代碼慢慢理清的話抬闯,這個(gè)next就是指的就是當(dāng)前要啟動(dòng)的Activity的Record
    //這里是自己的理解,希望不會(huì)有什么差錯(cuò)
    final ActivityRecord next = topRunningActivityLocked();
    ......
    final boolean dontWaitForPause = (next.info.flags & FLAG_RESUME_WHILE_PAUSING) != 0;
    boolean pausing = mStackSupervisor.pauseBackStacks(userLeaving, next, dontWaitForPause);
    if (mResumedActivity != null) {
        if (DEBUG_STATES) Slog.d(TAG_STATES,
                "resumeTopActivityLocked: Pausing " + mResumedActivity);
        //執(zhí)行pause
        pausing |= startPausingLocked(userLeaving, false, next, dontWaitForPause);
    }
    ......
    ActivityStack lastStack = mStackSupervisor.getLastStack();
    //要啟動(dòng)的Activity的app是不是為空影钉,顯然是null
    if (next.app != null && next.app.thread != null) {
        ......
    } else {
        //啟動(dòng)
        mStackSupervisor.startSpecificActivityLocked(next, true, true);
    }

    if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
    return true;
}

上面的代碼我省略了很多画髓,主要是先拿到最上面運(yùn)行的先判斷其是否為null,不為null則去判斷是否需要執(zhí)行pause平委,我們這里面是不需要執(zhí)行的奈虾,在后面有個(gè)對(duì)next.app的非空判斷,因?yàn)槲覀兪堑谝淮螁?dòng)廉赔,所以并沒(méi)有app肉微,最后會(huì)執(zhí)行mStackSupervisor.startSpecificActivityLocked(next, true, true)這部分代碼。


6. ActivityStackSupervisor.startSpecificActivityLocked過(guò)程

這個(gè)過(guò)程就比較簡(jiǎn)單了蜡塌,通過(guò)AMS去啟動(dòng)一個(gè)新的進(jìn)程(Process)碉纳。

void startSpecificActivityLocked(ActivityRecord r,
        boolean andResume, boolean checkConfig) {
    // Is this activity's application already running?
    ProcessRecord app = mService.getProcessRecordLocked(r.processName,
            r.info.applicationInfo.uid, true);

    r.task.stack.setLaunchTime(r);
    //很明顯,這里的app為null馏艾,我們需要?jiǎng)?chuàng)建新的process
    if (app != null && app.thread != null) {
        ......
    }
    //啟動(dòng)新的process
    mService.startProcessLocked(r.processName, r.info.applicationInfo, true, 0,
            "activity", r.intent.getComponent(), false, false, true);
}   

7. AMS啟動(dòng)新的進(jìn)程

AMS中startProcessLocked中調(diào)用了Process.start這個(gè)方法劳曹。Process.start中調(diào)用了startViaZygote方法奴愉,接著調(diào)用zygoteSendArgsAndGetResult方法,通過(guò)socket進(jìn)行通信執(zhí)行ActivityThreadmain方法铁孵。
部分代碼:

AMS:
private final void startProcessLocked(ProcessRecord app, String hostingType,
        String hostingNameStr, String abiOverride, String entryPoint, String[] entryPointArgs) {
        ......
        //去啟動(dòng)進(jìn)程
        Process.ProcessStartResult startResult = Process.start(entryPoint,
                app.processName, uid, uid, gids, debugFlags, mountExternal,
                app.info.targetSdkVersion, app.info.seinfo, requiredAbi, instructionSet,
                app.info.dataDir, entryPointArgs);
        ......
}
Process.java:
public static final ProcessStartResult start(final String processClass,
                              final String niceName,
                              int uid, int gid, int[] gids,
                              int debugFlags, int mountExternal,
                              int targetSdkVersion,
                              String seInfo,
                              String abi,
                              String instructionSet,
                              String appDataDir,
                              String[] zygoteArgs) {
    try {
        return startViaZygote(processClass, niceName, uid, gid, gids,
                debugFlags, mountExternal, targetSdkVersion, seInfo,
                abi, instructionSet, appDataDir, zygoteArgs);
    } catch (ZygoteStartFailedEx ex) {
        
    }
}
private static ProcessStartResult startViaZygote(final String processClass,
                              final String niceName,
                              final int uid, final int gid,
                              final int[] gids,
                              int debugFlags, int mountExternal,
                              int targetSdkVersion,
                              String seInfo,
                              String abi,
                              String instructionSet,
                              String appDataDir,
                              String[] extraArgs)
                              throws ZygoteStartFailedEx {
    synchronized(Process.class) {
        ......
        return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi)/**這里可以看到通過(guò)socket啟動(dòng)*/, argsForZygote);
    }
}

private static ProcessStartResult zygoteSendArgsAndGetResult(
        ZygoteState zygoteState, ArrayList<String> args)
        throws ZygoteStartFailedEx {
    try {
        
        int sz = args.size();
        for (int i = 0; i < sz; i++) {
            if (args.get(i).indexOf('\n') >= 0) {
                throw new ZygoteStartFailedEx("embedded newlines not allowed");
            }
        }

        final BufferedWriter writer = zygoteState.writer;
        final DataInputStream inputStream = zygoteState.inputStream;
        
        writer.write(Integer.toString(args.size()));
        writer.newLine();

        for (int i = 0; i < sz; i++) {
            String arg = args.get(i);
            writer.write(arg);
            writer.newLine();
        }

        writer.flush();

        // Should there be a timeout on this?
        ProcessStartResult result = new ProcessStartResult();

        // Always read the entire result from the input stream to avoid leaving
        // bytes in the stream for future process starts to accidentally stumble
        // upon.
        result.pid = inputStream.readInt();
        result.usingWrapper = inputStream.readBoolean();

        if (result.pid < 0) {
            throw new ZygoteStartFailedEx("fork() failed");
        }
        //返回結(jié)果
        return result;
    } catch (IOException ex) {
        zygoteState.close();
        throw new ZygoteStartFailedEx(ex);
    }
}

通過(guò)Zygote可以執(zhí)行ActivityThread中的main方法锭硼。


8. ActivityThreadmain方法

main方法中在上面一篇中也提到過(guò),上次SystemServer啟動(dòng)過(guò)程中會(huì)調(diào)用attach(true)方法蜕劝,而我們通過(guò)Zygote啟動(dòng)的時(shí)候檀头,會(huì)調(diào)用attach(false)這個(gè)方法。

public static void main(String[] args) {
    ......
    //主線程
    Looper.prepareMainLooper();
    
    ActivityThread thread = new ActivityThread();
    thread.attach(false);
    ......
    //一直循環(huán)岖沛,如果退出暑始,說(shuō)明程序關(guān)閉
    Looper.loop();
    ......
}
private void attach(boolean system) {
    sCurrentActivityThread = this;
    mSystemThread = system;
    if (!system) {
        ViewRootImpl.addFirstDrawHandler(new Runnable() {
            @Override
            public void run() {
                ensureJitEnabled();
            }
        });
        android.ddm.DdmHandleAppName.setAppName("<pre-initialized>",
                                                UserHandle.myUserId());
        RuntimeInit.setApplicationObject(mAppThread.asBinder());
        //獲取AMS的本地代理類
        final IActivityManager mgr = ActivityManagerNative.getDefault();
        try {
            //調(diào)用AMS的attachApplication方法
            mgr.attachApplication(mAppThread);
        } catch (RemoteException ex) {
            throw ex.rethrowFromSystemServer();
        }
        // Watch for getting close to heap limit.
        BinderInternal.addGcWatcher(new Runnable() {
            @Override public void run() {
                if (!mSomeActivitiesChanged) {
                    return;
                }
                Runtime runtime = Runtime.getRuntime();
                long dalvikMax = runtime.maxMemory();
                long dalvikUsed = runtime.totalMemory() - runtime.freeMemory();
                if (dalvikUsed > ((3*dalvikMax)/4)) {
                    if (DEBUG_MEMORY_TRIM) Slog.d(TAG, "Dalvik max=" + (dalvikMax/1024)
                            + " total=" + (runtime.totalMemory()/1024)
                            + " used=" + (dalvikUsed/1024));
                    mSomeActivitiesChanged = false;
                    try {
                        mgr.releaseSomeActivities(mAppThread);
                    } catch (RemoteException e) {
                        throw e.rethrowFromSystemServer();
                    }
                }
            }
        });
    } else {
       ......
    }
    ......
    ViewRootImpl.addConfigCallback(new ComponentCallbacks2() {......});
}

上面的主要是就是通過(guò)Binder調(diào)用AMSattachApplication方法。


9. AMS的attachApplication方法

attachApplication中主要是判斷當(dāng)前的Process是否存在婴削,如果存在涧偷,則需要將其kill栈暇。接著通過(guò)thread.bindApplication來(lái)將一些需要的變量初始化察署,進(jìn)行Application初始化是尔,調(diào)用ApplicationonCreate()方法。最后通過(guò)mStackSupervisor.attachApplicationLocked(app)方法去真正啟動(dòng)Activity互躬。下面大概看下整個(gè)流程:

//這里沒(méi)啥講的播赁,就是調(diào)用了attachApplicationLocked方法
@Override
public final void attachApplication(IApplicationThread thread) {
    synchronized (this) {
        int callingPid = Binder.getCallingPid();
        final long origId = Binder.clearCallingIdentity();
        attachApplicationLocked(thread, callingPid);
        Binder.restoreCallingIdentity(origId);
    }
}

private final boolean attachApplicationLocked(IApplicationThread thread,
        int pid) {

    // Find the application record that is being attached...  either via
    // the pid if we are running in multiple processes, or just pull the
    // next app record if we are emulating process with anonymous threads.
    //從進(jìn)程中查找是否有,如果有的話去殺掉這個(gè)進(jìn)程
    ProcessRecord app;
    if (pid != MY_PID && pid >= 0) {
        synchronized (mPidsSelfLocked) {
            app = mPidsSelfLocked.get(pid);
        }
    } else {
        app = null;
    }

    if (app == null) {
        Slog.w(TAG, "No pending application record for pid " + pid
                + " (IApplicationThread " + thread + "); dropping process");
        EventLog.writeEvent(EventLogTags.AM_DROP_PROCESS, pid);
        if (pid > 0 && pid != MY_PID) {
            Process.killProcessQuiet(pid);
            //TODO: killProcessGroup(app.info.uid, pid);
        } else {
            try {
                //退出
                thread.scheduleExit();
            } catch (Exception e) {
                // Ignore exceptions.
            }
        }
        return false;
    }

    // If this application record is still attached to a previous
    // process, clean it up now.
    //如果當(dāng)前的Application記錄仍然依附到之前的進(jìn)程中吼渡,則清理掉
    if (app.thread != null) {
        handleAppDiedLocked(app, true, true);
    }

    // Tell the process all about itself.

    if (DEBUG_ALL) Slog.v(
            TAG, "Binding process pid " + pid + " to record " + app);

    final String processName = app.processName;
    try {
        AppDeathRecipient adr = new AppDeathRecipient(
                app, pid, thread);
        thread.asBinder().linkToDeath(adr, 0);
        app.deathRecipient = adr;
    } catch (RemoteException e) {
        app.resetPackageList(mProcessStats);
        startProcessLocked(app, "link fail", processName);
        return false;
    }

    EventLog.writeEvent(EventLogTags.AM_PROC_BOUND, app.userId, app.pid, app.processName);

    app.makeActive(thread, mProcessStats);
    app.curAdj = app.setAdj = app.verifiedAdj = ProcessList.INVALID_ADJ;
    app.curSchedGroup = app.setSchedGroup = ProcessList.SCHED_GROUP_DEFAULT;
    app.forcingToForeground = null;
    updateProcessForegroundLocked(app, false, false);
    app.hasShownUi = false;
    app.debugging = false;
    app.cached = false;
    app.killedByAm = false;

    // We carefully use the same state that PackageManager uses for
    // filtering, since we use this flag to decide if we need to install
    // providers when user is unlocked later
    app.unlocked = StorageManager.isUserKeyUnlocked(app.userId);

    mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
    //mProcessesReady這個(gè)變量在AMS的systemReady中被賦值為true容为,所以這里的normalMode也為true
    boolean normalMode = mProcessesReady || isAllowedWhileBooting(app.info);
    ......
    try {
        int testMode = IApplicationThread.DEBUG_OFF;
        if (mDebugApp != null && mDebugApp.equals(processName)) {
            testMode = mWaitForDebugger
                ? IApplicationThread.DEBUG_WAIT
                : IApplicationThread.DEBUG_ON;
            app.debugging = true;
            if (mDebugTransient) {
                mDebugApp = mOrigDebugApp;
                mWaitForDebugger = mOrigWaitForDebugger;
            }
        }
        String profileFile = app.instrumentationProfileFile;
        ParcelFileDescriptor profileFd = null;
        int samplingInterval = 0;
        boolean profileAutoStop = false;
        if (mProfileApp != null && mProfileApp.equals(processName)) {
            mProfileProc = app;
            profileFile = mProfileFile;
            profileFd = mProfileFd;
            samplingInterval = mSamplingInterval;
            profileAutoStop = mAutoStopProfiler;
        }
        boolean enableTrackAllocation = false;
        if (mTrackAllocationApp != null && mTrackAllocationApp.equals(processName)) {
            enableTrackAllocation = true;
            mTrackAllocationApp = null;
        }

        // If the app is being launched for restore or full backup, set it up specially
        boolean isRestrictedBackupMode = false;
        if (mBackupTarget != null && mBackupAppName.equals(processName)) {
            isRestrictedBackupMode = mBackupTarget.appInfo.uid >= Process.FIRST_APPLICATION_UID
                    && ((mBackupTarget.backupMode == BackupRecord.RESTORE)
                            || (mBackupTarget.backupMode == BackupRecord.RESTORE_FULL)
                            || (mBackupTarget.backupMode == BackupRecord.BACKUP_FULL));
        }

        if (app.instrumentationClass != null) {
            notifyPackageUse(app.instrumentationClass.getPackageName(),
                             PackageManager.NOTIFY_PACKAGE_USE_INSTRUMENTATION);
        }
        if (DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION, "Binding proc "
                + processName + " with config " + mConfiguration);
        ApplicationInfo appInfo = app.instrumentationInfo != null
                ? app.instrumentationInfo : app.info;
        app.compat = compatibilityInfoForPackageLocked(appInfo);
        if (profileFd != null) {
            profileFd = profileFd.dup();
        }
        ProfilerInfo profilerInfo = profileFile == null ? null
                : new ProfilerInfo(profileFile, profileFd, samplingInterval, profileAutoStop);
        //真正執(zhí)行綁定操作是通過(guò)thread的bindApplication方法。
        thread.bindApplication(processName, appInfo, providers, app.instrumentationClass,
                profilerInfo, app.instrumentationArguments, app.instrumentationWatcher,
                app.instrumentationUiAutomationConnection, testMode,
                mBinderTransactionTrackingEnabled, enableTrackAllocation,
                isRestrictedBackupMode || !normalMode, app.persistent,
                new Configuration(mConfiguration), app.compat,
                getCommonServicesLocked(app.isolated),
                mCoreSettingsObserver.getCoreSettingsLocked());
        updateLruProcessLocked(app, false, null);
        app.lastRequestedGc = app.lastLowMemory = SystemClock.uptimeMillis();
    } catch (Exception e) {
        ......
        return false;
    }

    // Remove this record from the list of starting applications.
    mPersistentStartingProcesses.remove(app);
    if (DEBUG_PROCESSES && mProcessesOnHold.contains(app)) Slog.v(TAG_PROCESSES,
            "Attach application locked removing on hold: " + app);
    mProcessesOnHold.remove(app);

    boolean badApp = false;
    boolean didSomething = false;

    // See if the top visible activity is waiting to run in this process...
    //上面說(shuō)到寺酪,這里為true坎背,進(jìn)入StackSupervisor的attachApplicationLocked方法去真正啟動(dòng)Activity
    if (normalMode) {
        try {
            if (mStackSupervisor.attachApplicationLocked(app)) {
                didSomething = true;
            }
        } catch (Exception e) {
            Slog.wtf(TAG, "Exception thrown launching activities in " + app, e);
            badApp = true;
        }
    }
    
    ......

    return true;
}


10. StackSupervisorattachApplicationLocked方法

這個(gè)方法會(huì)調(diào)用realStartActivityLocked方法,去真正啟動(dòng)Activity

boolean attachApplicationLocked(ProcessRecord app) throws RemoteException {
    final String processName = app.processName;
    boolean didSomething = false;
    for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
        ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
        for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
            final ActivityStack stack = stacks.get(stackNdx);
            if (!isFocusedStack(stack)) {
                continue;
            }
            ActivityRecord hr = stack.topRunningActivityLocked();
            if (hr != null) {
                if (hr.app == null && app.uid == hr.info.applicationInfo.uid
                        && processName.equals(hr.processName)) {
                    try {
                        //真正啟動(dòng)Activity
                        if (realStartActivityLocked(hr, app, true, true)) {
                            didSomething = true;
                        }
                    } catch (RemoteException e) {
                        Slog.w(TAG, "Exception in new application when starting activity "
                              + hr.intent.getComponent().flattenToShortString(), e);
                        throw e;
                    }
                }
            }
        }
    }
    if (!didSomething) {
        ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
    }
    return didSomething;
}

final boolean realStartActivityLocked(ActivityRecord r, ProcessRecord app,
        boolean andResume, boolean checkConfig) throws RemoteException {
    ......
    try {
        ......
        app.forceProcessStateUpTo(mService.mTopProcessState);
        //最主要的就是這里寄雀,也是通過(guò)Activity中的方法去啟動(dòng)Activity得滤。
        app.thread.scheduleLaunchActivity(new Intent(r.intent), r.appToken,
                System.identityHashCode(r), r.info, new Configuration(mService.mConfiguration),
                new Configuration(task.mOverrideConfig), r.compat, r.launchedFromPackage,
                task.voiceInteractor, app.repProcState, r.icicle, r.persistentState, results,
                newIntents, !andResume, mService.isNextTransitionForward(), profilerInfo);
        ......
    } catch (RemoteException e) {
        
    }
    ......
    return true;
}

講到這里基本上啟動(dòng)流程就完成了,后面就是ActivityThread進(jìn)行Activity的啟動(dòng)過(guò)程盒犹,這個(gè)下次在繼續(xù)分析懂更。


11. 總結(jié)

這里面所有的關(guān)于Activity啟動(dòng),resume急膀,pause等等都是通過(guò)ActivityThread來(lái)進(jìn)行操作沮协,而這之中又會(huì)涉及到ActivityThread將任務(wù)交給Instrumentation去處理。這個(gè)處理過(guò)程包括了與跨進(jìn)程與遠(yuǎn)端的AMS進(jìn)行通信卓嫂,AMS又通過(guò)ActivityStarter,ActivityStackSupervisor,ActivityStack等進(jìn)行處理慷暂,最終將所啟動(dòng)的Activity真正啟動(dòng)。
其實(shí)還有個(gè)時(shí)序圖的晨雳,但是太大了行瑞,截圖也不全奸腺,等下次直接上傳,方便自己查看蘑辑。
下面計(jì)劃分析ActivityThread啟動(dòng)Activity洋机。


12. 寫(xiě)在后面的話

想寫(xiě)關(guān)于Activity啟動(dòng)流程想了好久,說(shuō)實(shí)話其中有一部分只能按自己的理解來(lái)解釋洋魂,可能會(huì)有一些問(wèn)題。但是起碼自己認(rèn)為是對(duì)的喜鼓。嗯副砍,這就夠了。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末庄岖,一起剝皮案震驚了整個(gè)濱河市豁翎,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌隅忿,老刑警劉巖心剥,帶你破解...
    沈念sama閱讀 212,816評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異背桐,居然都是意外死亡优烧,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,729評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門(mén)链峭,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)畦娄,“玉大人,你說(shuō)我怎么就攤上這事弊仪∥蹩ǎ” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 158,300評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵励饵,是天一觀的道長(zhǎng)驳癌。 經(jīng)常有香客問(wèn)我,道長(zhǎng)役听,這世上最難降的妖魔是什么颓鲜? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,780評(píng)論 1 285
  • 正文 為了忘掉前任,我火速辦了婚禮禾嫉,結(jié)果婚禮上灾杰,老公的妹妹穿的比我還像新娘。我一直安慰自己熙参,他們只是感情好艳吠,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,890評(píng)論 6 385
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著孽椰,像睡著了一般昭娩。 火紅的嫁衣襯著肌膚如雪凛篙。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 50,084評(píng)論 1 291
  • 那天栏渺,我揣著相機(jī)與錄音呛梆,去河邊找鬼。 笑死磕诊,一個(gè)胖子當(dāng)著我的面吹牛填物,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播霎终,決...
    沈念sama閱讀 39,151評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼滞磺,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了莱褒?” 一聲冷哼從身側(cè)響起击困,我...
    開(kāi)封第一講書(shū)人閱讀 37,912評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎广凸,沒(méi)想到半個(gè)月后阅茶,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,355評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡谅海,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,666評(píng)論 2 327
  • 正文 我和宋清朗相戀三年脸哀,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片胁赢。...
    茶點(diǎn)故事閱讀 38,809評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡企蹭,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出智末,到底是詐尸還是另有隱情谅摄,我是刑警寧澤,帶...
    沈念sama閱讀 34,504評(píng)論 4 334
  • 正文 年R本政府宣布系馆,位于F島的核電站送漠,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏由蘑。R本人自食惡果不足惜闽寡,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,150評(píng)論 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望尼酿。 院中可真熱鬧爷狈,春花似錦、人聲如沸裳擎。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,882評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至羡微,卻和暖如春谷饿,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背妈倔。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,121評(píng)論 1 267
  • 我被黑心中介騙來(lái)泰國(guó)打工博投, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人盯蝴。 一個(gè)月前我還...
    沈念sama閱讀 46,628評(píng)論 2 362
  • 正文 我出身青樓毅哗,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親捧挺。 傳聞我的和親對(duì)象是個(gè)殘疾皇子黎做,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,724評(píng)論 2 351

推薦閱讀更多精彩內(nèi)容