接上篇《深入理解Android2》读书笔记(四)

startActivity

Am

void run() throws RemoteException {try {printMessageForState();mAm.setActivityController(this);mState = STATE_NORMAL;InputStreamReader converter = new InputStreamReader(System.in);BufferedReader in = new BufferedReader(converter);String line;while ((line = in.readLine()) != null) {boolean addNewline = true;if (line.length() <= 0) {addNewline = false;} else if ("q".equals(line) || "quit".equals(line)) {resumeController(RESULT_DEFAULT);break;} else if (mState == STATE_CRASHED) {if ("c".equals(line) || "continue".equals(line)) {resumeController(RESULT_CRASH_DIALOG);} else if ("k".equals(line) || "kill".equals(line)) {resumeController(RESULT_CRASH_KILL);} else {System.out.println("Invalid command: " + line);}} else if (mState == STATE_ANR) {if ("c".equals(line) || "continue".equals(line)) {resumeController(RESULT_ANR_DIALOG);} else if ("k".equals(line) || "kill".equals(line)) {resumeController(RESULT_ANR_KILL);} else if ("w".equals(line) || "wait".equals(line)) {resumeController(RESULT_ANR_WAIT);} else {System.out.println("Invalid command: " + line);}} else if (mState == STATE_EARLY_ANR) {if ("c".equals(line) || "continue".equals(line)) {resumeController(RESULT_EARLY_ANR_CONTINUE);} else if ("k".equals(line) || "kill".equals(line)) {resumeController(RESULT_EARLY_ANR_KILL);} else {System.out.println("Invalid command: " + line);}} else {System.out.println("Invalid command: " + line);}synchronized (this) {if (addNewline) {System.out.println("");}printMessageForState();}}} catch (IOException e) {e.printStackTrace();} finally {mAm.setActivityController(null);}
}

private void runStart() throws Exception {Intent intent = makeIntent(UserHandle.USER_CURRENT);if (mUserId == UserHandle.USER_ALL) {System.err.println("Error: Can't start service with user 'all'");return;}String mimeType = intent.getType();if (mimeType == null && intent.getData() != null&& "content".equals(intent.getData().getScheme())) {mimeType = mAm.getProviderMimeType(intent.getData(), mUserId);}do {if (mStopOption) {String packageName;if (intent.getComponent() != null) {packageName = intent.getComponent().getPackageName();} else {IPackageManager pm = IPackageManager.Stub.asInterface(ServiceManager.getService("package"));if (pm == null) {System.err.println("Error: Package manager not running; aborting");return;}List<ResolveInfo> activities = pm.queryIntentActivities(intent, mimeType, 0,mUserId);if (activities == null || activities.size() <= 0) {System.err.println("Error: Intent does not match any activities: "+ intent);return;} else if (activities.size() > 1) {System.err.println("Error: Intent matches multiple activities; can't stop: "+ intent);return;}packageName = activities.get(0).activityInfo.packageName;}System.out.println("Stopping: " + packageName);mAm.forceStopPackage(packageName, mUserId);Thread.sleep(250);}System.out.println("Starting: " + intent);intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);ParcelFileDescriptor fd = null;ProfilerInfo profilerInfo = null;if (mProfileFile != null) {try {fd = openForSystemServer(new File(mProfileFile),ParcelFileDescriptor.MODE_CREATE |ParcelFileDescriptor.MODE_TRUNCATE |ParcelFileDescriptor.MODE_READ_WRITE);} catch (FileNotFoundException e) {System.err.println("Error: Unable to open file: " + mProfileFile);System.err.println("Consider using a file under /data/local/tmp/");return;}profilerInfo = new ProfilerInfo(mProfileFile, fd, mSamplingInterval, mAutoStop);}IActivityManager.WaitResult result = null;int res;final long startTime = SystemClock.uptimeMillis();if (mWaitOption) {result = mAm.startActivityAndWait(null, null, intent, mimeType,null, null, 0, mStartFlags, profilerInfo, null, mUserId);res = result.result;} else {res = mAm.startActivityAsUser(null, null, intent, mimeType,null, null, 0, mStartFlags, profilerInfo, null, mUserId);}final long endTime = SystemClock.uptimeMillis();PrintStream out = mWaitOption ? System.out : System.err;boolean launched = false;switch (res) {case ActivityManager.START_SUCCESS:launched = true;break;case ActivityManager.START_SWITCHES_CANCELED:launched = true;out.println("Warning: Activity not started because the "+ " current activity is being kept for the user.");break;case ActivityManager.START_DELIVERED_TO_TOP:launched = true;out.println("Warning: Activity not started, intent has "+ "been delivered to currently running "+ "top-most instance.");break;case ActivityManager.START_RETURN_INTENT_TO_CALLER:launched = true;out.println("Warning: Activity not started because intent "+ "should be handled by the caller");break;case ActivityManager.START_TASK_TO_FRONT:launched = true;out.println("Warning: Activity not started, its current "+ "task has been brought to the front");break;case ActivityManager.START_INTENT_NOT_RESOLVED:out.println("Error: Activity not started, unable to "+ "resolve " + intent.toString());break;case ActivityManager.START_CLASS_NOT_FOUND:out.println(NO_CLASS_ERROR_CODE);out.println("Error: Activity class " +intent.getComponent().toShortString()+ " does not exist.");break;case ActivityManager.START_FORWARD_AND_REQUEST_CONFLICT:out.println("Error: Activity not started, you requested to "+ "both forward and receive its result");break;case ActivityManager.START_PERMISSION_DENIED:out.println("Error: Activity not started, you do not "+ "have permission to access it.");break;case ActivityManager.START_NOT_VOICE_COMPATIBLE:out.println("Error: Activity not started, voice control not allowed for: "+ intent);break;case ActivityManager.START_NOT_CURRENT_USER_ACTIVITY:out.println("Error: Not allowed to start background user activity"+ " that shouldn't be displayed for all users.");break;default:out.println("Error: Activity not started, unknown error code " + res);break;}if (mWaitOption && launched) {if (result == null) {result = new IActivityManager.WaitResult();result.who = intent.getComponent();}System.out.println("Status: " + (result.timeout ? "timeout" : "ok"));if (result.who != null) {System.out.println("Activity: " + result.who.flattenToShortString());}if (result.thisTime >= 0) {System.out.println("ThisTime: " + result.thisTime);}if (result.totalTime >= 0) {System.out.println("TotalTime: " + result.totalTime);}System.out.println("WaitTime: " + (endTime-startTime));System.out.println("Complete");}mRepeat--;if (mRepeat > 1) {mAm.unhandledBack();}} while (mRepeat > 1);
}

am最终将调用AMS的startActivityAndWait函数来处理这次启动请求。

@Override
public final WaitResult startActivityAndWait(IApplicationThread caller, String callingPackage,Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,int startFlags, ProfilerInfo profilerInfo, Bundle options, int userId) {enforceNotIsolatedCaller("startActivityAndWait");userId = handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), userId,false, ALLOW_FULL_ONLY, "startActivityAndWait", null);WaitResult res = new WaitResult();// TODO: Switch to user app stacks here.mStackSupervisor.startActivityMayWait(caller, -1, callingPackage, intent, resolvedType,null, null, resultTo, resultWho, requestCode, startFlags, profilerInfo, res, null,options, false, userId, null, null);return res;
}

final void startActivityLocked(ActivityRecord r, boolean newTask,boolean doResume, boolean keepCurTransition, Bundle options) {TaskRecord rTask = r.task;final int taskId = rTask.taskId;// mLaunchTaskBehind tasks get placed at the back of the task stack.if (!r.mLaunchTaskBehind && (taskForIdLocked(taskId) == null || newTask)) {// Last activity in task had been removed or ActivityManagerService is reusing task.// Insert or replace.// Might not even be in.
        insertTaskAtTop(rTask, r);mWindowManager.moveTaskToTop(taskId);}TaskRecord task = null;if (!newTask) {// If starting in an existing task, find where that is...boolean startIt = true;for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {task = mTaskHistory.get(taskNdx);if (task.getTopActivity() == null) {// All activities in task are finishing.continue;}if (task == r.task) {// Here it is!  Now, if this is not yet visible to the// user, then just add it without starting; it will// get started when the user navigates back to it.if (!startIt) {if (DEBUG_ADD_REMOVE) Slog.i(TAG, "Adding activity " + r + " to task "+ task, new RuntimeException("here").fillInStackTrace());task.addActivityToTop(r);r.putInHistory();mWindowManager.addAppToken(task.mActivities.indexOf(r), r.appToken,r.task.taskId, mStackId, r.info.screenOrientation, r.fullscreen,(r.info.flags & ActivityInfo.FLAG_SHOW_FOR_ALL_USERS) != 0,r.userId, r.info.configChanges, task.voiceSession != null,r.mLaunchTaskBehind);if (VALIDATE_TOKENS) {validateAppTokensLocked();}ActivityOptions.abort(options);return;}break;} else if (task.numFullscreen > 0) {startIt = false;}}}// Place a new activity at top of stack, so it is next to interact// with the user.// If we are not placing the new activity frontmost, we do not want// to deliver the onUserLeaving callback to the actual frontmost// activityif (task == r.task && mTaskHistory.indexOf(task) != (mTaskHistory.size() - 1)) {mStackSupervisor.mUserLeaving = false;if (DEBUG_USER_LEAVING) Slog.v(TAG_USER_LEAVING,"startActivity() behind front, mUserLeaving=false");}task = r.task;// Slot the activity into the history stack and proceedif (DEBUG_ADD_REMOVE) Slog.i(TAG, "Adding activity " + r + " to stack to task " + task,new RuntimeException("here").fillInStackTrace());task.addActivityToTop(r);task.setFrontOfTask();r.putInHistory();if (!isHomeStack() || numActivities() > 0) {// We want to show the starting preview window if we are// switching to a new task, or the next activity's process is// not currently running.boolean showStartingIcon = newTask;ProcessRecord proc = r.app;if (proc == null) {proc = mService.mProcessNames.get(r.processName, r.info.applicationInfo.uid);}if (proc == null || proc.thread == null) {showStartingIcon = true;}if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION,"Prepare open transition: starting " + r);if ((r.intent.getFlags() & Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0) {mWindowManager.prepareAppTransition(AppTransition.TRANSIT_NONE, keepCurTransition);mNoAnimActivities.add(r);} else {mWindowManager.prepareAppTransition(newTask? r.mLaunchTaskBehind? AppTransition.TRANSIT_TASK_OPEN_BEHIND: AppTransition.TRANSIT_TASK_OPEN: AppTransition.TRANSIT_ACTIVITY_OPEN, keepCurTransition);mNoAnimActivities.remove(r);}mWindowManager.addAppToken(task.mActivities.indexOf(r),r.appToken, r.task.taskId, mStackId, r.info.screenOrientation, r.fullscreen,(r.info.flags & ActivityInfo.FLAG_SHOW_FOR_ALL_USERS) != 0, r.userId,r.info.configChanges, task.voiceSession != null, r.mLaunchTaskBehind);boolean doShow = true;if (newTask) {// Even though this activity is starting fresh, we still need// to reset it to make sure we apply affinities to move any// existing activities from other tasks in to it.// If the caller has requested that the target task be// reset, then do so.if ((r.intent.getFlags() & Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) != 0) {resetTaskIfNeededLocked(r, r);doShow = topRunningNonDelayedActivityLocked(null) == r;}} else if (options != null && new ActivityOptions(options).getAnimationType()== ActivityOptions.ANIM_SCENE_TRANSITION) {doShow = false;}if (r.mLaunchTaskBehind) {// Don't do a starting window for mLaunchTaskBehind. More importantly make sure we// tell WindowManager that r is visible even though it is at the back of the stack.mWindowManager.setAppVisibility(r.appToken, true);ensureActivitiesVisibleLocked(null, 0);} else if (SHOW_APP_STARTING_PREVIEW && doShow) {// Figure out if we are transitioning from another activity that is// "has the same starting icon" as the next one.  This allows the// window manager to keep the previous window it had previously// created, if it still had one.ActivityRecord prev = mResumedActivity;if (prev != null) {// We don't want to reuse the previous starting preview if:// (1) The current activity is in a different task.if (prev.task != r.task) {prev = null;}// (2) The current activity is already displayed.else if (prev.nowVisible) {prev = null;}}mWindowManager.setAppStartingWindow(r.appToken, r.packageName, r.theme,mService.compatibilityInfoForPackageLocked(r.info.applicationInfo), r.nonLocalizedLabel,r.labelRes, r.icon, r.logo, r.windowFlags,prev != null ? prev.appToken : null, showStartingIcon);r.mStartingWindowShown = true;}} else {// If this is the first activity, don't do any fancy animations,// because there is nothing for it to animate on top of.
        mWindowManager.addAppToken(task.mActivities.indexOf(r), r.appToken,r.task.taskId, mStackId, r.info.screenOrientation, r.fullscreen,(r.info.flags & ActivityInfo.FLAG_SHOW_FOR_ALL_USERS) != 0, r.userId,r.info.configChanges, task.voiceSession != null, r.mLaunchTaskBehind);ActivityOptions.abort(options);options = null;}if (VALIDATE_TOKENS) {validateAppTokensLocked();}if (doResume) {mStackSupervisor.resumeTopActivitiesLocked(this, r, options);}
}

startActivityLocked函数的主要工作包括

1.处理sourceRecord及resultRecord。其中,sourceRecord表示发起本次请求的Activity,resultRecord表示接收处理结果的Activity(启动一个Activity肯定需要它完成某项事情,当目标Activity将事情成后,就需要告知请求者该事情的处理结果)。在一般情况下,sourceRecord和resultRecord应指向同一个Activity。

2.处理app switch。如果AMS当前禁止app switch,则只能把本次启动请求保存起来,以待允许app switch时再处理。从代码中可知,AMS在处理本次请求前,会先调用doPendingActivityLaunchesLocked函数,在该函数内部将启动之前因系统禁止app switch而保存的Pending请求

3.调用startActivityUncheckedLocked处理本次Activity启动请求

@Override
public void stopAppSwitches() {if (checkCallingPermission(android.Manifest.permission.STOP_APP_SWITCHES)!= PackageManager.PERMISSION_GRANTED) {throw new SecurityException("Requires permission "+ android.Manifest.permission.STOP_APP_SWITCHES);}synchronized(this) {mAppSwitchesAllowedTime = SystemClock.uptimeMillis()+ APP_SWITCH_DELAY_TIME;mDidAppSwitch = false;mHandler.removeMessages(DO_PENDING_ACTIVITY_LAUNCHES_MSG);Message msg = mHandler.obtainMessage(DO_PENDING_ACTIVITY_LAUNCHES_MSG);mHandler.sendMessageDelayed(msg, APP_SWITCH_DELAY_TIME);}
}

public void resumeAppSwitches() {if (checkCallingPermission(android.Manifest.permission.STOP_APP_SWITCHES)!= PackageManager.PERMISSION_GRANTED) {throw new SecurityException("Requires permission "+ android.Manifest.permission.STOP_APP_SWITCHES);}synchronized(this) {// Note that we don't execute any pending app switches... we will// let those wait until either the timeout, or the next start// activity request.mAppSwitchesAllowedTime = 0;}
}

startActivityUncheckedLocked函数很长,但是目的比较简单,即为新创建的ActivityRecord找到一个合适的Task。

ActivityStack

final boolean resumeTopActivityLocked(ActivityRecord prev, Bundle options) {if (mStackSupervisor.inResumeTopActivity) {// Don't even start recursing.return false;}boolean result = false;try {// Protect against recursion.mStackSupervisor.inResumeTopActivity = true;if (mService.mLockScreenShown == ActivityManagerService.LOCK_SCREEN_LEAVING) {mService.mLockScreenShown = ActivityManagerService.LOCK_SCREEN_HIDDEN;mService.updateSleepIfNeededLocked();}result = resumeTopActivityInnerLocked(prev, options);} finally {mStackSupervisor.inResumeTopActivity = false;}return result;
}

resumeTopActivityLocked函数中有两个非常重要的关键点

1.如果mResumedActivity不为空,则需要先暂停(pause)这个Activity。由代码中的注释可知,mResumedActivity代表上一次启动的(即当前正显示的)Activity。现在要启动一个新的Activity,须先停止当前Activity,这部分工作由startPausingLocked函数完成

2.mResumeActivity什么时候为空呢?当然是在启动全系统第一个Activity时,即启动Home界面的时候。除此之外,该值都不会为空

resumeTopActivityLocked最后将调用另外一个startSpecificActivityLocked,该函数将真正创建一个应用进程。

private final void startProcessLocked(ProcessRecord app, String hostingType,String hostingNameStr, String abiOverride, String entryPoint, String[] entryPointArgs) {long startTime = SystemClock.elapsedRealtime();if (app.pid > 0 && app.pid != MY_PID) {checkTime(startTime, "startProcess: removing from pids map");synchronized (mPidsSelfLocked) {mPidsSelfLocked.remove(app.pid);mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);}checkTime(startTime, "startProcess: done removing from pids map");app.setPid(0);}if (DEBUG_PROCESSES && mProcessesOnHold.contains(app)) Slog.v(TAG_PROCESSES,"startProcessLocked removing on hold: " + app);mProcessesOnHold.remove(app);checkTime(startTime, "startProcess: starting to update cpu stats");updateCpuStats();checkTime(startTime, "startProcess: done updating cpu stats");try {try {if (AppGlobals.getPackageManager().isPackageFrozen(app.info.packageName)) {// This is caught below as if we had failed to fork zygotethrow new RuntimeException("Package " + app.info.packageName + " is frozen!");}} catch (RemoteException e) {throw e.rethrowAsRuntimeException();}int uid = app.uid;int[] gids = null;int mountExternal = Zygote.MOUNT_EXTERNAL_NONE;if (!app.isolated) {int[] permGids = null;try {checkTime(startTime, "startProcess: getting gids from package manager");final IPackageManager pm = AppGlobals.getPackageManager();permGids = pm.getPackageGids(app.info.packageName, app.userId);MountServiceInternal mountServiceInternal = LocalServices.getService(MountServiceInternal.class);mountExternal = mountServiceInternal.getExternalStorageMountMode(uid,app.info.packageName);} catch (RemoteException e) {throw e.rethrowAsRuntimeException();}/** Add shared application and profile GIDs so applications can share some* resources like shared libraries and access user-wide resources*/if (ArrayUtils.isEmpty(permGids)) {gids = new int[2];} else {gids = new int[permGids.length + 2];System.arraycopy(permGids, 0, gids, 2, permGids.length);}gids[0] = UserHandle.getSharedAppGid(UserHandle.getAppId(uid));gids[1] = UserHandle.getUserGid(UserHandle.getUserId(uid));}checkTime(startTime, "startProcess: building args");if (mFactoryTest != FactoryTest.FACTORY_TEST_OFF) {if (mFactoryTest == FactoryTest.FACTORY_TEST_LOW_LEVEL&& mTopComponent != null&& app.processName.equals(mTopComponent.getPackageName())) {uid = 0;}if (mFactoryTest == FactoryTest.FACTORY_TEST_HIGH_LEVEL&& (app.info.flags&ApplicationInfo.FLAG_FACTORY_TEST) != 0) {uid = 0;}}int debugFlags = 0;if ((app.info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) {debugFlags |= Zygote.DEBUG_ENABLE_DEBUGGER;// Also turn on CheckJNI for debuggable apps. It's quite// awkward to turn on otherwise.debugFlags |= Zygote.DEBUG_ENABLE_CHECKJNI;}// Run the app in safe mode if its manifest requests so or the// system is booted in safe mode.if ((app.info.flags & ApplicationInfo.FLAG_VM_SAFE_MODE) != 0 ||mSafeMode == true) {debugFlags |= Zygote.DEBUG_ENABLE_SAFEMODE;}if ("1".equals(SystemProperties.get("debug.checkjni"))) {debugFlags |= Zygote.DEBUG_ENABLE_CHECKJNI;}String jitDebugProperty = SystemProperties.get("debug.usejit");if ("true".equals(jitDebugProperty)) {debugFlags |= Zygote.DEBUG_ENABLE_JIT;} else if (!"false".equals(jitDebugProperty)) {// If we didn't force disable by setting false, defer to the dalvik vm options.if ("true".equals(SystemProperties.get("dalvik.vm.usejit"))) {debugFlags |= Zygote.DEBUG_ENABLE_JIT;}}String genDebugInfoProperty = SystemProperties.get("debug.generate-debug-info");if ("true".equals(genDebugInfoProperty)) {debugFlags |= Zygote.DEBUG_GENERATE_DEBUG_INFO;}if ("1".equals(SystemProperties.get("debug.jni.logging"))) {debugFlags |= Zygote.DEBUG_ENABLE_JNI_LOGGING;}if ("1".equals(SystemProperties.get("debug.assert"))) {debugFlags |= Zygote.DEBUG_ENABLE_ASSERT;}String requiredAbi = (abiOverride != null) ? abiOverride : app.info.primaryCpuAbi;if (requiredAbi == null) {requiredAbi = Build.SUPPORTED_ABIS[0];}String instructionSet = null;if (app.info.primaryCpuAbi != null) {instructionSet = VMRuntime.getInstructionSet(app.info.primaryCpuAbi);}app.gids = gids;app.requiredAbi = requiredAbi;app.instructionSet = instructionSet;// Start the process.  It will either succeed and return a result containing// the PID of the new process, or else throw a RuntimeException.boolean isActivityProcess = (entryPoint == null);if (entryPoint == null) entryPoint = "android.app.ActivityThread";Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "Start proc: " +app.processName);checkTime(startTime, "startProcess: asking zygote to start proc");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);checkTime(startTime, "startProcess: returned from zygote!");Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);if (app.isolated) {mBatteryStatsService.addIsolatedUid(app.uid, app.info.uid);}mBatteryStatsService.noteProcessStart(app.processName, app.info.uid);checkTime(startTime, "startProcess: done updating battery stats");EventLog.writeEvent(EventLogTags.AM_PROC_START,UserHandle.getUserId(uid), startResult.pid, uid,app.processName, hostingType,hostingNameStr != null ? hostingNameStr : "");if (app.persistent) {Watchdog.getInstance().processStarted(app.processName, startResult.pid);}checkTime(startTime, "startProcess: building log message");StringBuilder buf = mStringBuilder;buf.setLength(0);buf.append("Start proc ");buf.append(startResult.pid);buf.append(':');buf.append(app.processName);buf.append('/');UserHandle.formatUid(buf, uid);if (!isActivityProcess) {buf.append(" [");buf.append(entryPoint);buf.append("]");}buf.append(" for ");buf.append(hostingType);if (hostingNameStr != null) {buf.append(" ");buf.append(hostingNameStr);}Slog.i(TAG, buf.toString());app.setPid(startResult.pid);app.usingWrapper = startResult.usingWrapper;app.removed = false;app.killed = false;app.killedByAm = false;checkTime(startTime, "startProcess: starting to update pids map");synchronized (mPidsSelfLocked) {this.mPidsSelfLocked.put(startResult.pid, app);if (isActivityProcess) {Message msg = mHandler.obtainMessage(PROC_START_TIMEOUT_MSG);msg.obj = app;mHandler.sendMessageDelayed(msg, startResult.usingWrapper? PROC_START_TIMEOUT_WITH_WRAPPER : PROC_START_TIMEOUT);}}checkTime(startTime, "startProcess: done updating pids map");} catch (RuntimeException e) {// XXX do better error recovery.app.setPid(0);mBatteryStatsService.noteProcessFinish(app.processName, app.info.uid);if (app.isolated) {mBatteryStatsService.removeIsolatedUid(app.uid, app.info.uid);}Slog.e(TAG, "Failure starting process " + app.processName, e);}
}

1.FLAG_FROM_BACKGROUND标志发起这次启动的Task属于后台任务。很显然,手机中没有界面供用户操作位于后台Task中的Activity。如果没有设置该标志,那么这次启动请求就是由前台Task因某种原因而触发的(例如:用户单击某个按钮)

2.如果一个应用进程在1分钟内连续崩溃超过2次,则AMS会将其ProcessRecord加入所谓的mBadProcesses中。一个应用崩溃后,系统会弹出一个警告框以提醒用户。

ActivityThread

public static void main(String[] args) {Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");SamplingProfilerIntegration.start();// CloseGuard defaults to true and can be quite spammy.  We// disable it here, but selectively enable it later (via// StrictMode) on debug builds, but using DropBox, not logs.CloseGuard.setEnabled(false);Environment.initForCurrentUser();// Set the reporter for event logging in libcoreEventLogger.setReporter(new EventLoggingReporter());AndroidKeyStoreProvider.install();// Make sure TrustedCertificateStore looks in the right place for CA certificatesfinal File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());TrustedCertificateStore.setDefaultUserDirectory(configDir);Process.setArgV0("<pre-initialized>");Looper.prepareMainLooper();ActivityThread thread = new ActivityThread();thread.attach(false);if (sMainThreadHandler == null) {sMainThreadHandler = thread.getHandler();}if (false) {Looper.myLooper().setMessageLogging(newLogPrinter(Log.DEBUG, "ActivityThread"));}// End of event ActivityThreadMain.
    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);Looper.loop();throw new RuntimeException("Main thread loop unexpectedly exited");
}

在main函数内部将创建一个消息循环Loop,接着调用ActivityThread的attach函数,最终将主线程加入消息循环

private void attach(boolean system) {sCurrentActivityThread = this;mSystemThread = system;if (!system) {ViewRootImpl.addFirstDrawHandler(new Runnable() {@Overridepublic void run() {ensureJitEnabled();}});android.ddm.DdmHandleAppName.setAppName("<pre-initialized>",UserHandle.myUserId());RuntimeInit.setApplicationObject(mAppThread.asBinder());final IActivityManager mgr = ActivityManagerNative.getDefault();try {mgr.attachApplication(mAppThread);} catch (RemoteException ex) {// Ignore
        }// 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) {}}}});} else {// Don't set application object here -- if the system crashes,// we can't display an alert, we just want to die die die.android.ddm.DdmHandleAppName.setAppName("system_process",UserHandle.myUserId());try {mInstrumentation = new Instrumentation();ContextImpl context = ContextImpl.createAppContext(this, getSystemContext().mPackageInfo);mInitialApplication = context.mPackageInfo.makeApplication(true, null);mInitialApplication.onCreate();} catch (Exception e) {throw new RuntimeException("Unable to instantiate Application():" + e.toString(), e);}}// add dropbox logging to libcoreDropBox.setReporter(new DropBoxReporter());ViewRootImpl.addConfigCallback(new ComponentCallbacks2() {@Overridepublic void onConfigurationChanged(Configuration newConfig) {synchronized (mResourcesManager) {// We need to apply this change to the resources// immediately, because upon returning the view// hierarchy will be informed about it.if (mResourcesManager.applyConfigurationToResourcesLocked(newConfig, null)) {// This actually changed the resources!  Tell// everyone about it.if (mPendingConfiguration == null ||mPendingConfiguration.isOtherSeqNewer(newConfig)) {mPendingConfiguration = newConfig;sendMessage(H.CONFIGURATION_CHANGED, newConfig);}}}}@Overridepublic void onLowMemory() {}@Overridepublic void onTrimMemory(int level) {}});
}

AMS创建一个应用进程后,会设置一个超时时间(一般是10秒)。如果超过这个时间,应用进程还没有和AMS交互,则断定该进程创建失败。所以,应用进程启动后,需要尽快和AMS交互,即调用AMS的attachApplication函数。在该函数内部将调用attachApplicationLocked。

attachApplicationLocked第一阶段的工作比较简单:

1.设置代表该应用进程的ProcessRecord对象的一些成员变量,如用于和应用进程交互的thread对象、jincheng 调度优先级及oom_adj的值等

2.从消息队列中撤销PROC_START_TIMEOUT_MSG

应用进程的创建及初始化总结

1.在应用进程启动后,需要尽快调用AMS的attachApplication函数,该函数是这个刚创建的应用进程第一次和AMS交互。此时的它还默默无名,连一个确定的进程名都没有。不过没关系,attachApplication函数将根据创建该应用进程之前所保存的ProcessRecord为其准备一切手续

2.attachApplication准备好一切后,将调用应用进程的bindApplication函数,在该函数内部将发消息给主线程,最终该消息由handleBindApplication处理。handleBindApplication将为该进程设置进程名,初始化一些策略和参数信息等。另外,它还创建一个Application对象。同时,如果该Application声明了ContentProvider,还需要为该进程安装ContentProvider。

AMS调用完bindApplication后,将通过realStartActivityLocked启动Activity。在此之前,要创建完应用进程并初始化Android运行环境

private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) {// If we are getting ready to gc after going to the background, well// we are back active so skip it.
    unscheduleGcIdler();mSomeActivitiesChanged = true;if (r.profilerInfo != null) {mProfiler.setProfiler(r.profilerInfo);mProfiler.startProfiling();}// Make sure we are running with the most recent config.handleConfigurationChanged(null, null);if (localLOGV) Slog.v(TAG, "Handling launch of " + r);// Initialize before creating the activity
    WindowManagerGlobal.initialize();Activity a = performLaunchActivity(r, customIntent);if (a != null) {r.createdConfig = new Configuration(mConfiguration);Bundle oldState = r.state;handleResumeActivity(r.token, false, r.isForward,!r.activity.mFinished && !r.startsNotResumed);if (!r.activity.mFinished && r.startsNotResumed) {// The activity manager actually wants this one to start out// paused, because it needs to be visible but isn't in the// foreground.  We accomplish this by going through the// normal startup (because activities expect to go through// onResume() the first time they run, before their window// is displayed), and then pausing it.  However, in this case// we do -not- need to do the full pause cycle (of freezing// and such) because the activity manager assumes it can just// retain the current state it has.try {r.activity.mCalled = false;mInstrumentation.callActivityOnPause(r.activity);// We need to keep around the original state, in case// we need to be created again.  But we only do this// for pre-Honeycomb apps, which always save their state// when pausing, so we can not have them save their state// when restarting from a paused state.  For HC and later,// we want to (and can) let the state be saved as the normal// part of stopping the activity.if (r.isPreHoneycomb()) {r.state = oldState;}if (!r.activity.mCalled) {throw new SuperNotCalledException("Activity " + r.intent.getComponent().toShortString() +" did not call through to super.onPause()");}} catch (SuperNotCalledException e) {throw e;} catch (Exception e) {if (!mInstrumentation.onException(r.activity, e)) {throw new RuntimeException("Unable to pause activity "+ r.intent.getComponent().toShortString()+ ": " + e.toString(), e);}}r.paused = true;}} else {// If there was an error, for any reason, tell the activity// manager to stop us.try {ActivityManagerNative.getDefault().finishActivity(r.token, Activity.RESULT_CANCELED, null, false);} catch (RemoteException ex) {// Ignore
        }}
}

1.首先调用performLaunchActivity,该在函数内部通过Java反射机制创建目标Activity,然后调用它的onCreate及onStart函数

2.调用handleResumeActivity,会在其内部调用目标Activity的onResume函数。

startActivity总结

1.起点是am。我们利用am start命令,发起本次目标Activity的启动请求

2.接下来进入ActivityManagerService和ActivityStack这两个核心类。对于启动Activity来说,这段行程又可细分为两个阶段:第一阶段就是根据启动模式和启动标志找到活创建ActivityRecord及对应的TaskRecord;第二阶段就是处理Activity启动或切换相关的工作

3.然后AMS直接创建目标进程并运行Activity的流程,其中涉及目标进程的创建,目标进程中Android运行环境的初始化,目标Activity的创建以及onCreate、onStart及onResume等生命周期中重要函数的调用等相关知识

4.AMS先暂停当前Activity,然后再创建目标进程并运行Activity的流程,其中两个应用进程和AMS交互。

《深入理解Android2》读书笔记(五)相关推荐

  1. FFmpeg从入门到精通读书笔记(1)

    笔者才开始学习音视频开发,FFmpeg从入门到精通读书笔记系列主要是基于阅读刘歧.赵文杰编著的<FFmpeg从入门到精通>以及雷霄骅博士博客总结写的入门心得体会. 官方文档资料 FFmpe ...

  2. mysql数据应用从入门_MYSQL数据库应用从入门到精通----读书笔记

    mysql 1.创建数据库 create database database_name; 2.查看数据库 show database_name; 3.选择数据库 use database_name; ...

  3. Mybatis从入门到精通读书笔记

    Mybatis从入门到精通 resultMap resultMap简介 resultMap resultMap简介 P25-P26

  4. python编程从入门到精通读书笔记(基础知识)

    第一部分:基础知识 学习python想要做的软件  1.开机答题软件,(电脑一开机的输入密码,改为答题,初步设定为选择题,答对了才可以进入.)  2.   第二章 2.1第一个程序:  print(& ...

  5. oracle 9i从入门到精通读书笔记2

    第二章:PL/SQL基础  2.1 PL/SQL程序结构 2.1.1 PL/SQL块的类型 所有的PL/SQL程序都是以块作为基本单位,以及都是由块组成的. 这些块可以是按顺序出现的,也可以是嵌套的 ...

  6. 《分析服务从入门到精通读书笔记》第四章、创建父子维度(7)

    目的 父子维度的不同之处在于处于其包含了一个基于递归关系(Recursive relationship)的层次关系,比如,上级和下级雇员的层次结构关系是典型的递归关系.在一线工作的雇员会有一个主管,而 ...

  7. php 到精通 书,PHP从入门到精通——读书笔记(第20章:Zend Framwork框架)

    第二十章:Zend Framwork 框架 1:概述 2:Zend Framwork 环境搭建1)环境配置:使用ZF框架进行项目开发,首先需要对PHP运行环境进行配置,从而使整个运行环境能够支持ZF的 ...

  8. 《分析服务从入门到精通读书笔记》第一章、维度数据仓库(4)

    简介 商业智能系统将维度数据仓库作为数据存取层.数据仓库存储在关系型数据库管理系统(RDBMS)中,打一个非常简单的比方,你可以将关系数据库简单地想作一系列的表格.每个表格有行和列,就行Excel电子 ...

  9. HTML5 从入门到精通读书笔记

    此书太水, 没什么有营养的内容. HTML5中新添加的 thead, tbody, tfoot 为语义化标签, 没什么实际效果. table 中元素的 colspan 和 rowspan 用来设定单元 ...

  10. 《分析服务从入门到精通读书笔记》第一章、数据分析基础(1)

    目的 学习一些商业智能的基本概念,如属性.层次结构和维度 数据分析中的属性 假设如果你是AWC公司的总经理,希望了解公司的业绩,于是从业务人员那里得到一份报表 表1.1 AWC公司业绩     42  ...

最新文章

  1. no typehandler found for property XXXX 解决
  2. live555编译、播放示例
  3. 软件项目管理的20条锦囊妙计
  4. 5 questions
  5. 第 6 章 工厂模式
  6. shell如何快速执行上一个命令的最后一个参数
  7. 产品 电信nb接口调用_NB-IOT开发流程---基于中国电信物联网平台实现平台对接
  8. 30岁,我从前端转型管理成功了
  9. Virtualbox安装Windows7虚拟机
  10. 计算机仿真课程的心得体会,数学建模心得体会
  11. 第三方银联支付接口对接_第三方支付接口集成安装,网站支付接口对接,网站收款接口...
  12. linux怎么添加桌面图标,linux下添加桌面图标
  13. 高通骁龙845与骁龙710处理器参数对比分析
  14. Ruby‘s Adventrue游戏制作笔记(十四)Unity播放游戏音效
  15. 408计算机组成原理学习笔记——指令系统
  16. [Python从零到壹] 五十六.图像增强及运算篇之图像平滑(中值滤波、双边滤波)
  17. 学生教育云平台登录入口_甘肃省教育云平台
  18. 新型智慧园区规划设计方案 PPT
  19. 今日头条、UC头条(大鱼号)、企鹅号文章分类、推送、拉取业务实现及接口api说明文档
  20. Linux中drwxr-xr-x.的意思和权限

热门文章

  1. 捷信达会员管理系统SQL语句相关
  2. (转)Fabric 1.0 读写集
  3. Inondb中的checkpoint
  4. ArcGIS 生成要素轮廓线掩膜
  5. mysql Substr与char_length函数的应用
  6. 安装不上vc++环境,导致部分游戏和qq不能用的解决方案
  7. ERROR: “System.Web.Mvc.Controller.File(string, string, string)”是一个“方法”
  8. 数据结构笔记(二十四)-- 哈夫曼编译码
  9. stl变易算法(三)
  10. android 多线程 加锁,android 多线程 — 从一个小例子再次品位多线程