android activity 启动过程分析(source code 4.4)
说实话,android source code从2.3到4.4变化是蛮多的,尤其是media部分,虽然总的框架是没有多大变化,但是找起代码来看还是挺麻烦的。在android里面最受伤的是使用了java,jni,jvm,Nativity c++等等,各种设计模式横行,当然在学习源码过程中也意识了编程语言基础,数据结构,设计模式的重要性。
android source code 经典的地方:
1. 大量使用了各种设计模式如单例模式,装饰模式,工程工厂模式,适配器模式等等。
2. 使用了binder驱动进行了IPC通信。
3. 重新设计了消息处理机制。
4. 加入了强弱指针管理。
5. 设计了activity安全的权限管理。
6. 对应用开发者可扩展,开发维护做出突出的贡献。
7. 使用了JNI技术。
8. 优化了电源管理服务。
。。。。。等等
当然,也有缺点地方。
1. android启动慢。
因为使用了jni,jvm原因需要加载很多类及类库,和各种服务的启动。为了用户体验,使用了sureface。
上述启动完后还要启动launcher,扫描各个应用信息等等。
2. android 是为嵌入式产品而开发的。实际上内存使用效果来看不是很理想,容易产生碎片,导致android使用越用越慢。
3. 主题定制少,缺乏大型游戏。
4. app常驻内存导致无法长时间待机。
好吧,不要抱怨那么多了,生活已经够无奈的了。开始分析下吧。
1. 在Android系统中,应用程序是由Activity组成的,因此,应用程序的启动过程实际上就是应用程序中的默认Activity的启动过程。即在桌面程序(launcher)启动时候,扫描各个app信息,在设备屏幕中点击应用程序图标的情景就会引发Android应用程序中的默认Activity的启动,从而把应用程序启动起来。这种启动方式的特点是会启动一个新的进程来加载相应的Activity。
2. 由上可知:默认启动activity是通过launcher(onClick方法)来启动的,其实就是解析AndroidManifest.xml中的activity标签及<intent-filter>标签。然后通过startActivitySafely方法启动activity。
- Launcher.java (e:\baiduyundownload\intel_x86_sysimg_4.4_source_files_20131206\intel_x86_sysimg_4.4_source_files_20131206\packages\apps\launcher2\src\com\android\launcher2)
- public void onClick(View v) {
- // Make sure that rogue clicks don't get through while allapps is launching, or after the
- // view has detached (it's possible for this to happen if the view is removed mid touch).
- if (v.getWindowToken() == null) {
- return;
- }
- if (!mWorkspace.isFinishedSwitchingState()) {
- return;
- }
- Object tag = v.getTag();
- if (tag instanceof ShortcutInfo) {
- // Open shortcut
- final Intent intent = ((ShortcutInfo) tag).intent;
- int[] pos = new int[];
- v.getLocationOnScreen(pos);
- intent.setSourceBounds(new Rect(pos[], pos[],
- pos[] + v.getWidth(), pos[] + v.getHeight()));
- boolean success = startActivitySafely(v, intent, tag);
- if (success && v instanceof BubbleTextView) {
- mWaitingForResume = (BubbleTextView) v;
- mWaitingForResume.setStayPressed(true);
- }
- } else if (tag instanceof FolderInfo) {
- if (v instanceof FolderIcon) {
- FolderIcon fi = (FolderIcon) v;
- handleFolderClick(fi);
- }
- } else if (v == mAllAppsButton) {
- if (isAllAppsVisible()) {
- showWorkspace(true);
- } else {
- onClickAllAppsButton(v);
- }
- }
- }
- boolean startActivitySafely(View v, Intent intent, Object tag) {
- boolean success = false;
- try {
- success = startActivity(v, intent, tag);
- } catch (ActivityNotFoundException e) {
- Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
- Log.e(TAG, "Unable to launch. tag=" + tag + " intent=" + intent, e);
- }
- return success;
- }
- boolean startActivity(View v, Intent intent, Object tag) {
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- try {
- // Only launch using the new animation if the shortcut has not opted out (this is a
- // private contract between launcher and may be ignored in the future).
- boolean useLaunchAnimation = (v != null) &&
- !intent.hasExtra(INTENT_EXTRA_IGNORE_LAUNCH_ANIMATION);
//useLaunchAnimation表示是否使用点击Launcher桌面上的图标,会先有图标放大的动画,然后才进入应用,2.3之前是没有这个选择的。- if (useLaunchAnimation) {
- ActivityOptions opts = ActivityOptions.makeScaleUpAnimation(v, 0, 0,
- v.getMeasuredWidth(), v.getMeasuredHeight());
- startActivity(intent, opts.toBundle());
- } else {
- startActivity(intent);
- }
- return true;
- } catch (SecurityException e) {
- Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
- Log.e(TAG, "Launcher does not have the permission to launch " + intent +
- ". Make sure to create a MAIN intent-filter for the corresponding activity " +
- "or use the exported attribute for this activity. "
- + "tag="+ tag + " intent=" + intent, e);
- }
- return false;
- }
上面的
- startActivity(intent, opts.toBundle());在launcher.java中没有找到定义,实际上是在launcher的基类中定义的,需要转到activity.java中查找定义。
Activity.java (base\core\java\android\app)
- @Override
- public void startActivity(Intent intent, Bundle options) {
- if (options != null) {
//第二个参数传入-1表示不需要这个Actvity结束后的返回结果。- startActivityForResult(intent, -, options);
- } else {
- // Note we want to go through this call for compatibility with
- // applications that may have overridden the method.
- startActivityForResult(intent, -);
- }
- }
- public void startActivityForResult(Intent intent, int requestCode, Bundle options) {
- if (mParent == null) {
//这里的mParent肯定为null,因为是从launcher启动进来的,肯定没有父activity。
//这里的mInstrumentation是Activity类的成员变量,它的类型是Intrumentation,定义在frameworks/base/core/java/android/app/Instrumentation.java文件中,它用来监控应用程序和系统的交互。
//这里的mMainThread也是Activity类的成员变量,它的类型是ActivityThread,它代表的是应用程序的主线程,这里通过mMainThread.getApplicationThread获得它里面的ApplicationThread成员变量,它是一个Binder对象,后面我们会看到,ActivityManagerService会使用它来和ActivityThread来进行进程间通信。这里我们需注意的是,这里的mMainThread代表的是Launcher应用程序运行的进程。
//这里的mToken也是Activity类的成员变量,它是一个Binder对象的远程接口。- Instrumentation.ActivityResult ar =
- mInstrumentation.execStartActivity(
- this, mMainThread.getApplicationThread(), mToken, this,
- intent, requestCode, options);
- if (ar != null) {
- mMainThread.sendActivityResult(
- mToken, mEmbeddedID, requestCode, ar.getResultCode(),
- ar.getResultData());
- }
- .......
- }
- Instrumentation.java (base\core\java\android\app)
- public ActivityResult execStartActivity(
- Context who, IBinder contextThread, IBinder token, Activity target,
- Intent intent, int requestCode, Bundle options) {
- IApplicationThread whoThread = (IApplicationThread) contextThread;
- if (mActivityMonitors != null) {
- synchronized (mSync) {
- final int N = mActivityMonitors.size();
- for (int i=; i<N; i++) {
- final ActivityMonitor am = mActivityMonitors.get(i);
- if (am.match(who, null, intent)) {
- am.mHits++;
- if (am.isBlocking()) {
- return requestCode >= ? am.getResult() : null;
- }
- break;
- }
- }
- }
- }
- try {
- intent.migrateExtraStreamToClipData();
- intent.prepareToLeaveProcess();
//这里的ActivityManagerNative.getDefault返回ActivityManagerService的远程接口,即ActivityManagerProxy接口,跟进去就知道了(IActivityManager)。
//intent.resolveTypeIfNeeded返回这个intent的MIME类型,一般没有AndroidManifest.xml设置MainActivity的MIME类型,因此,这里返回null。- int result = ActivityManagerNative.getDefault()
- .startActivity(whoThread, who.getBasePackageName(), intent,
- intent.resolveTypeIfNeeded(who.getContentResolver()),
- token, target != null ? target.mEmbeddedID : null,
- requestCode, , null, null, options);
- checkStartActivityResult(result, intent);
- } catch (RemoteException e) {
- }
- return null;
- }
- ActivityManagerNative.java (base\core\java\android\app)
- public int startActivity(IApplicationThread caller, String callingPackage, Intent intent,
- String resolvedType, IBinder resultTo, String resultWho, int requestCode,
- int startFlags, String profileFile,
- ParcelFileDescriptor profileFd, Bundle options) throws RemoteException {
//这里已经非常接近核心了,1. 构造parcel data包和reply包,将相关要启动的intent数据打进去,然后通过远程对象mRemote的transact方法(binder IPC)投递到服务端( ActivityManagerService)
//从调用可以知道,这里的参数resolvedType、grantedUriPermissions和resultWho均为null;参数caller为ApplicationThread类型的Binder实体;参数resultTo为一个Binder实体的远程接口,我们先不关注它;参数grantedMode为0,我们也先不关注它;参数requestCode为-1;参数onlyIfNeeded和debug均空false。- Parcel data = Parcel.obtain();
- Parcel reply = Parcel.obtain();
- data.writeInterfaceToken(IActivityManager.descriptor);
- data.writeStrongBinder(caller != null ? caller.asBinder() : null);
- data.writeString(callingPackage);
- intent.writeToParcel(data, );
- data.writeString(resolvedType);
- data.writeStrongBinder(resultTo);
- data.writeString(resultWho);
- data.writeInt(requestCode);
- data.writeInt(startFlags);
- data.writeString(profileFile);
- if (profileFd != null) {
- data.writeInt();
- profileFd.writeToParcel(data, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
- } else {
- data.writeInt();
- }
- if (options != null) {
- data.writeInt();
- options.writeToParcel(data, );
- } else {
- data.writeInt();
- }
- mRemote.transact(START_ACTIVITY_TRANSACTION, data, reply, );
- reply.readException();
- int result = reply.readInt();
- reply.recycle();
- data.recycle();
- return result;
- }
- ActivityManagerService.java (base\services\java\com\android\server\am)
- @Override
- public final int startActivity(IApplicationThread caller, String callingPackage,
- Intent intent, String resolvedType, IBinder resultTo,
- String resultWho, int requestCode, int startFlags,
- String profileFile, ParcelFileDescriptor profileFd, Bundle options) {
- return startActivityAsUser(caller, callingPackage, intent, resolvedType, resultTo,
- resultWho, requestCode,
- startFlags, profileFile, profileFd, options, UserHandle.getCallingUserId());
- }
- @Override
- public final int startActivityAsUser(IApplicationThread caller, String callingPackage,
- Intent intent, String resolvedType, IBinder resultTo,
- String resultWho, int requestCode, int startFlags,
- String profileFile, ParcelFileDescriptor profileFd, Bundle options, int userId) {
- enforceNotIsolatedCaller("startActivity");
- userId = handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), userId,
- false, true, "startActivity", null);
- // TODO: Switch to user app stacks here.
- return mStackSupervisor.startActivityMayWait(caller, -, callingPackage, intent, resolvedType,
- resultTo, resultWho, requestCode, startFlags, profileFile, profileFd,
- null, null, options, userId);
- }
- ActivityStackSupervisor.java (base\services\java\com\android\server\am)
- final int startActivityMayWait(IApplicationThread caller, int callingUid,
- String callingPackage, Intent intent, String resolvedType, IBinder resultTo,
- String resultWho, int requestCode, int startFlags, String profileFile,
- ParcelFileDescriptor profileFd, WaitResult outResult, Configuration config,
- Bundle options, int userId) {
- // Refuse possible leaked file descriptors
- if (intent != null && intent.hasFileDescriptors()) {
- throw new IllegalArgumentException("File descriptors passed in Intent");
- }
- boolean componentSpecified = intent.getComponent() != null;
- // Don't modify the client's object!
- intent = new Intent(intent);
- // Collect information about the target of the Intent.收集intent的信息()
- ActivityInfo aInfo = resolveActivity(intent, resolvedType, startFlags,
- profileFile, profileFd, userId);
- synchronized (mService) {
- int callingPid;
- if (callingUid >= ) {
- callingPid = -;
- } else if (caller == null) {
- callingPid = Binder.getCallingPid();
- callingUid = Binder.getCallingUid();
- } else {
- callingPid = callingUid = -;
- }
- final ActivityStack stack = getFocusedStack();
- stack.mConfigWillChange = config != null
- && mService.mConfiguration.diff(config) != ;
- if (DEBUG_CONFIGURATION) Slog.v(TAG,
- "Starting activity when config will change = " + stack.mConfigWillChange);
- final long origId = Binder.clearCallingIdentity();
- if (aInfo != null &&
- (aInfo.applicationInfo.flags&ApplicationInfo.FLAG_CANT_SAVE_STATE) != ) {
- // This may be a heavy-weight process! Check to see if we already
- // have another, different heavy-weight process running.
- if (aInfo.processName.equals(aInfo.applicationInfo.packageName)) {
- if (mService.mHeavyWeightProcess != null &&
- (mService.mHeavyWeightProcess.info.uid != aInfo.applicationInfo.uid ||
- !mService.mHeavyWeightProcess.processName.equals(aInfo.processName))) {
- int realCallingUid = callingUid;
- if (caller != null) {
- ProcessRecord callerApp = mService.getRecordForAppLocked(caller);
- if (callerApp != null) {
- realCallingUid = callerApp.info.uid;
- } else {
- Slog.w(TAG, "Unable to find app for caller " + caller
- + " (pid=" + callingPid + ") when starting: "
- + intent.toString());
- ActivityOptions.abort(options);
- return ActivityManager.START_PERMISSION_DENIED;
- }
- }
- IIntentSender target = mService.getIntentSenderLocked(
- ActivityManager.INTENT_SENDER_ACTIVITY, "android",
- realCallingUid, userId, null, null, , new Intent[] { intent },
- new String[] { resolvedType }, PendingIntent.FLAG_CANCEL_CURRENT
- | PendingIntent.FLAG_ONE_SHOT, null);
- Intent newIntent = new Intent();
- if (requestCode >= ) {
- // Caller is requesting a result.
- newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_HAS_RESULT, true);
- }
- newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_INTENT,
- new IntentSender(target));
- if (mService.mHeavyWeightProcess.activities.size() > ) {
- ActivityRecord hist = mService.mHeavyWeightProcess.activities.get();
- newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_CUR_APP,
- hist.packageName);
- newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_CUR_TASK,
- hist.task.taskId);
- }
- newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_NEW_APP,
- aInfo.packageName);
- newIntent.setFlags(intent.getFlags());
- newIntent.setClassName("android",
- HeavyWeightSwitcherActivity.class.getName());
- intent = newIntent;
- resolvedType = null;
- caller = null;
- callingUid = Binder.getCallingUid();
- callingPid = Binder.getCallingPid();
- componentSpecified = true;
- try {
- ResolveInfo rInfo =
- AppGlobals.getPackageManager().resolveIntent(
- intent, null,
- PackageManager.MATCH_DEFAULT_ONLY
- | ActivityManagerService.STOCK_PM_FLAGS, userId);
- aInfo = rInfo != null ? rInfo.activityInfo : null;
- aInfo = mService.getActivityInfoForUser(aInfo, userId);
- } catch (RemoteException e) {
- aInfo = null;
- }
- }
- }
- }
- int res = startActivityLocked(caller, intent, resolvedType,
- aInfo, resultTo, resultWho, requestCode, callingPid, callingUid,
- callingPackage, startFlags, options, componentSpecified, null);
- if (stack.mConfigWillChange) {
- // If the caller also wants to switch to a new configuration,
- // do so now. This allows a clean switch, as we are waiting
- // for the current activity to pause (so we will not destroy
- // it), and have not yet started the next activity.
- mService.enforceCallingPermission(android.Manifest.permission.CHANGE_CONFIGURATION,
- "updateConfiguration()");
- stack.mConfigWillChange = false;
- if (DEBUG_CONFIGURATION) Slog.v(TAG,
- "Updating to new configuration after starting activity.");
- mService.updateConfigurationLocked(config, null, false, false);
- }
- Binder.restoreCallingIdentity(origId);
- if (outResult != null) {
- outResult.result = res;
- if (res == ActivityManager.START_SUCCESS) {
- mWaitingActivityLaunched.add(outResult);
- do {
- try {
- mService.wait();
- } catch (InterruptedException e) {
- }
- } while (!outResult.timeout && outResult.who == null);
- } else if (res == ActivityManager.START_TASK_TO_FRONT) {
- ActivityRecord r = stack.topRunningActivityLocked(null);
- if (r.nowVisible) {
- outResult.timeout = false;
- outResult.who = new ComponentName(r.info.packageName, r.info.name);
- outResult.totalTime = ;
- outResult.thisTime = ;
- } else {
- outResult.thisTime = SystemClock.uptimeMillis();
- mWaitingActivityVisible.add(outResult);
- do {
- try {
- mService.wait();
- } catch (InterruptedException e) {
- }
- } while (!outResult.timeout && outResult.who == null);
- }
- }
- }
- return res;
- }
- }
- final int startActivityLocked(IApplicationThread caller,
- Intent intent, String resolvedType, ActivityInfo aInfo, IBinder resultTo,
- String resultWho, int requestCode,
- int callingPid, int callingUid, String callingPackage, int startFlags, Bundle options,
- boolean componentSpecified, ActivityRecord[] outActivity) {
- int err = ActivityManager.START_SUCCESS;
- ProcessRecord callerApp = null;
- if (caller != null) {
//从传进来的参数caller得到调用者的进程信息,并保存在callerApp变量中,这里就是Launcher应用程序的进程信息了- callerApp = mService.getRecordForAppLocked(caller);
- if (callerApp != null) {
- callingPid = callerApp.pid;
- callingUid = callerApp.info.uid;
- } else {
- Slog.w(TAG, "Unable to find app for caller " + caller
- + " (pid=" + callingPid + ") when starting: "
- + intent.toString());
- err = ActivityManager.START_PERMISSION_DENIED;
- }
- }
- if (err == ActivityManager.START_SUCCESS) {
- final int userId = aInfo != null ? UserHandle.getUserId(aInfo.applicationInfo.uid) : ;
- Slog.i(TAG, "START u" + userId + " {" + intent.toShortString(true, true, true, false)
- + "} from pid " + (callerApp != null ? callerApp.pid : callingPid));
- }
- ActivityRecord sourceRecord = null;
- ActivityRecord resultRecord = null;
- if (resultTo != null) {
//参数resultTo是Launcher这个Activity里面的一个Binder对象,通过它可以获得Launcher这个Activity的相关信息,保存在sourceRecord变量中。- sourceRecord = isInAnyStackLocked(resultTo);
- if (DEBUG_RESULTS) Slog.v(
- TAG, "Will send result to " + resultTo + " " + sourceRecord);
- if (sourceRecord != null) {
- if (requestCode >= && !sourceRecord.finishing) {
- resultRecord = sourceRecord;
- }
- }
- }
- ActivityStack resultStack = resultRecord == null ? null : resultRecord.task.stack;
- int launchFlags = intent.getFlags();
- if ((launchFlags&Intent.FLAG_ACTIVITY_FORWARD_RESULT) !=
- && sourceRecord != null) {
- // Transfer the result target from the source activity to the new
- // one being started, including any failures.
- if (requestCode >= ) {
- ActivityOptions.abort(options);
- return ActivityManager.START_FORWARD_AND_REQUEST_CONFLICT;
- }
- resultRecord = sourceRecord.resultTo;
- resultWho = sourceRecord.resultWho;
- requestCode = sourceRecord.requestCode;
- sourceRecord.resultTo = null;
- if (resultRecord != null) {
- resultRecord.removeResultsLocked(
- sourceRecord, resultWho, requestCode);
- }
- }
- if (err == ActivityManager.START_SUCCESS && intent.getComponent() == null) {
- // We couldn't find a class that can handle the given Intent.
- // That's the end of that!
- err = ActivityManager.START_INTENT_NOT_RESOLVED;
- }
- if (err == ActivityManager.START_SUCCESS && aInfo == null) {
- // We couldn't find the specific class specified in the Intent.
- // Also the end of the line.
- err = ActivityManager.START_CLASS_NOT_FOUND;
- }
- if (err != ActivityManager.START_SUCCESS) {
- if (resultRecord != null) {
- resultStack.sendActivityResultLocked(-,
- resultRecord, resultWho, requestCode,
- Activity.RESULT_CANCELED, null);
- }
- setDismissKeyguard(false);
- ActivityOptions.abort(options);
- return err;
- }
- final int startAnyPerm = mService.checkPermission(
- START_ANY_ACTIVITY, callingPid, callingUid);
- final int componentPerm = mService.checkComponentPermission(aInfo.permission, callingPid,
- callingUid, aInfo.applicationInfo.uid, aInfo.exported);
- if (startAnyPerm != PERMISSION_GRANTED && componentPerm != PERMISSION_GRANTED) {
- if (resultRecord != null) {
- resultStack.sendActivityResultLocked(-,
- resultRecord, resultWho, requestCode,
- Activity.RESULT_CANCELED, null);
- }
- setDismissKeyguard(false);
- String msg;
- if (!aInfo.exported) {
- msg = "Permission Denial: starting " + intent.toString()
- + " from " + callerApp + " (pid=" + callingPid
- + ", uid=" + callingUid + ")"
- + " not exported from uid " + aInfo.applicationInfo.uid;
- } else {
- msg = "Permission Denial: starting " + intent.toString()
- + " from " + callerApp + " (pid=" + callingPid
- + ", uid=" + callingUid + ")"
- + " requires " + aInfo.permission;
- }
- Slog.w(TAG, msg);
- throw new SecurityException(msg);
- }
- boolean 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 (abort) {
- if (resultRecord != null) {
- resultStack.sendActivityResultLocked(-, resultRecord, resultWho, requestCode,
- Activity.RESULT_CANCELED, null);
- }
- // We pretend to the caller that it was really started, but
- // they will just get a cancel result.
- setDismissKeyguard(false);
- ActivityOptions.abort(options);
- return ActivityManager.START_SUCCESS;
- }
- //创建即将要启动的Activity的相关信息,并保存在r变量中
- ActivityRecord r = new ActivityRecord(mService, callerApp, callingUid, callingPackage,
- intent, resolvedType, aInfo, mService.mConfiguration,
- resultRecord, resultWho, requestCode, componentSpecified, this);
- if (outActivity != null) {
- outActivity[] = r;
- }
- final ActivityStack stack = getFocusedStack();
- if (stack.mResumedActivity == null
- || stack.mResumedActivity.info.applicationInfo.uid != callingUid) {
- if (!mService.checkAppSwitchAllowedLocked(callingPid, callingUid, "Activity start")) {
- PendingActivityLaunch pal =
- new PendingActivityLaunch(r, sourceRecord, startFlags, stack);
- mService.mPendingActivityLaunches.add(pal);
- setDismissKeyguard(false);
- ActivityOptions.abort(options);
- return ActivityManager.START_SWITCHES_CANCELED;
- }
- }
- if (mService.mDidAppSwitch) {
- // This is the second allowed switch since we stopped switches,
- // so now just generally allow switches. Use case: user presses
- // home (switches disabled, switch to home, mDidAppSwitch now true);
- // user taps a home icon (coming from home so allowed, we hit here
- // and now allow anyone to switch again).
- mService.mAppSwitchesAllowedTime = ;
- } else {
- mService.mDidAppSwitch = true;
- }
- mService.doPendingActivityLaunchesLocked(false);
- //接下来创建这个来处理下
- err = startActivityUncheckedLocked(r, sourceRecord, startFlags, true, options);
- if (allPausedActivitiesComplete()) {
- // If someone asked to have the keyguard dismissed on the next
- // activity start, but we are not actually doing an activity
- // switch... just dismiss the keyguard now, because we
- // probably want to see whatever is behind it.
- dismissKeyguard();
- }
- return err;
- }
- final int startActivityUncheckedLocked(ActivityRecord r,
- ActivityRecord sourceRecord, int startFlags, boolean doResume,
- Bundle options) {
- final Intent intent = r.intent;
- final int callingUid = r.launchedFromUid;
- // 函数首先获得intent的标志值,保存在launchFlags变量中。这个intent的标志值的位Intent.FLAG_ACTIVITY_NO_USER_ACTION没有置位,因此 ,成员变量mUserLeaving的值为true。这个intent的标志值的位Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP也没有置位,因此,变量notTop的值为null。
- int launchFlags = intent.getFlags();
- // We'll invoke onUserLeaving before onPause only if the launching
- // activity did not explicitly state that this is an automated launch.
- mUserLeaving = (launchFlags&Intent.FLAG_ACTIVITY_NO_USER_ACTION) == ;
- if (DEBUG_USER_LEAVING) Slog.v(TAG, "startActivity() => mUserLeaving=" + mUserLeaving);
- // If the caller has asked not to resume at this point, we make note
- // of this in the record so that we can skip it when trying to find
- // the top running activity.
- if (!doResume) {
- r.delayedResume = true;
- }
- ActivityRecord notTop = (launchFlags&Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP) != ? r : null;
- // If the onlyIfNeeded flag is set, then we can do this if the activity
- // being launched is the same as the one making the call... or, as
- // a special case, if we do not know the caller then we count the
- // current top activity as the caller.
- if ((startFlags&ActivityManager.START_FLAG_ONLY_IF_NEEDED) != ) {
- ActivityRecord checkedCaller = sourceRecord;
- if (checkedCaller == null) {
- checkedCaller = getFocusedStack().topRunningNonDelayedActivityLocked(notTop);
- }
- if (!checkedCaller.realActivity.equals(r.realActivity)) {
- // Caller is not the same as launcher, so always needed.
- startFlags &= ~ActivityManager.START_FLAG_ONLY_IF_NEEDED;
- }
- }
- if (sourceRecord == null) {
- // This activity is not being started from another... in this
- // case we -always- start a new task.
- if ((launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) == ) {
- Slog.w(TAG, "startActivity called from non-Activity context; forcing " +
- "Intent.FLAG_ACTIVITY_NEW_TASK for: " + intent);
- launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
- }
- } else if (sourceRecord.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
- // The original activity who is starting us is running as a single
- // instance... this new activity it is starting must go on its
- // own task.
- launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
- } else if (r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE
- || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK) {
- // The activity being started is a single instance... it always
- // gets launched into its own task.
- launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
- }
- final ActivityStack sourceStack;
- if (sourceRecord != null) {
- if (sourceRecord.finishing) {
- // If the source is finishing, we can't further count it as our source. This
- // is because the task it is associated with may now be empty and on its way out,
- // so we don't want to blindly throw it in to that task. Instead we will take
- // the NEW_TASK flow and try to find a task for it.
- if ((launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) == ) {
- Slog.w(TAG, "startActivity called from finishing " + sourceRecord
- + "; forcing " + "Intent.FLAG_ACTIVITY_NEW_TASK for: " + intent);
- launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
- }
- sourceRecord = null;
- sourceStack = null;
- } else {
- sourceStack = sourceRecord.task.stack;
- }
- } else {
- sourceStack = null;
- }
- //传进来的参数r.resultTo为null,表示Launcher不需要等这个即将要启动的MainActivity的执行结果。
- if (r.resultTo != null && (launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) != ) {
- // For whatever reason this activity is being launched into a new
- // task... yet the caller has requested a result back. Well, that
- // is pretty messed up, so instead immediately send back a cancel
- // and let the new task continue launched as normal without a
- // dependency on its originator.
- Slog.w(TAG, "Activity is launching as a new task, so cancelling activity result.");
- r.resultTo.task.stack.sendActivityResultLocked(-,
- r.resultTo, r.resultWho, r.requestCode,
- Activity.RESULT_CANCELED, null);
- r.resultTo = null;
- }
- boolean addingToTask = false;
- boolean movedHome = false;
- TaskRecord reuseTask = null;
- ActivityStack targetStack;
//当前有没有Task可以用来执行这个Activity。由于r.launchMode的值不为ActivityInfo.LAUNCH_SINGLE_INSTANCE,因此,它通过findTaskLocked函数来查找存不存这样的Task,这里返回的结果是null,即intentActivity为null,因此,需要创建一个新的Task来启动这个Activity。- if (((launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) != &&
- (launchFlags&Intent.FLAG_ACTIVITY_MULTIPLE_TASK) == )
- || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK
- || r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
- // If bring to front is requested, and no result is requested, and
- // we can find a task that was started with this same
- // component, then instead of launching bring that one to the front.
- if (r.resultTo == null) {
- // See if there is a task to bring to the front. If this is
- // a SINGLE_INSTANCE activity, there can be one and only one
- // instance of it in the history, and it is always in its own
- // unique task, so we do a special search.
- ActivityRecord intentActivity = r.launchMode != ActivityInfo.LAUNCH_SINGLE_INSTANCE
- ? findTaskLocked(r)
- : findActivityLocked(intent, r.info);
- if (intentActivity != null) {
- if (r.task == null) {
- r.task = intentActivity.task;
- }
- targetStack = intentActivity.task.stack;
- targetStack.mLastPausedActivity = null;
- if (DEBUG_TASKS) Slog.d(TAG, "Bring to front target: " + targetStack
- + " from " + intentActivity);
- moveHomeStack(targetStack.isHomeStack());
- if (intentActivity.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.
- intentActivity.task.setIntent(intent, r.info);
- }
- // If the target task is not in the front, then we need
- // to bring it to the front... except... well, with
- // SINGLE_TASK_LAUNCH it's not entirely clear. We'd like
- // to have the same behavior as if a new instance was
- // being started, which means not bringing it to the front
- // if the caller is not itself in the front.
- final ActivityStack lastStack = getLastStack();
- ActivityRecord curTop = lastStack == null?
- null : lastStack.topRunningNonDelayedActivityLocked(notTop);
- if (curTop != null && (curTop.task != intentActivity.task ||
- curTop.task != lastStack.topTask())) {
- r.intent.addFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);
- if (sourceRecord == null || (sourceStack.topActivity() != null &&
- sourceStack.topActivity().task == sourceRecord.task)) {
- // We really do want to push this one into the
- // user's face, right now.
- movedHome = true;
- if ((launchFlags &
- (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_TASK_ON_HOME))
- == (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_TASK_ON_HOME)) {
- // Caller wants to appear on home activity.
- intentActivity.task.mOnTopOfHome = true;
- }
- targetStack.moveTaskToFrontLocked(intentActivity.task, r, options);
- options = null;
- }
- }
- // If the caller has requested that the target task be
- // reset, then do so.
- if ((launchFlags&Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) != ) {
- intentActivity = targetStack.resetTaskIfNeededLocked(intentActivity, r);
- }
- if ((startFlags&ActivityManager.START_FLAG_ONLY_IF_NEEDED) != ) {
- // 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! And for paranoia, make
- // sure we have correctly resumed the top activity.
- if (doResume) {
- resumeTopActivitiesLocked(targetStack, null, options);
- } else {
- ActivityOptions.abort(options);
- }
- if (r.task == null) Slog.v(TAG,
- "startActivityUncheckedLocked: task left null",
- new RuntimeException("here").fillInStackTrace());
- return ActivityManager.START_RETURN_INTENT_TO_CALLER;
- }
- if ((launchFlags &
- (Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_CLEAR_TASK))
- == (Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_CLEAR_TASK)) {
- // The caller has requested to completely replace any
- // existing task with its new activity. Well that should
- // not be too hard...
- reuseTask = intentActivity.task;
- reuseTask.performClearTaskLocked();
- reuseTask.setIntent(r.intent, r.info);
- } else if ((launchFlags&Intent.FLAG_ACTIVITY_CLEAR_TOP) !=
- || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK
- || r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
- // 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.
- ActivityRecord top =
- intentActivity.task.performClearTaskLocked(r, launchFlags);
- 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(r.intent, r.info);
- }
- ActivityStack.logStartActivity(EventLogTags.AM_NEW_INTENT,
- r, top.task);
- top.deliverNewIntentLocked(callingUid, r.intent);
- } else {
- // A special case: we need to
- // start the activity because it is not currently
- // running, and the caller has asked to clear the
- // current task to have this activity at the top.
- addingToTask = true;
- // Now pretend like this activity is being started
- // by the top of its task, so it is put in the
- // right place.
- sourceRecord = intentActivity;
- }
- } else if (r.realActivity.equals(intentActivity.task.realActivity)) {
- // In this case the top activity on the task is the
- // same as the one being launched, so we take that
- // as a request to bring the task to the foreground.
- // If the top activity in the task is the root
- // activity, deliver this new intent to it if it
- // desires.
- if (((launchFlags&Intent.FLAG_ACTIVITY_SINGLE_TOP) !=
- || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TOP)
- && intentActivity.realActivity.equals(r.realActivity)) {
- ActivityStack.logStartActivity(EventLogTags.AM_NEW_INTENT, r,
- intentActivity.task);
- if (intentActivity.frontOfTask) {
- intentActivity.task.setIntent(r.intent, r.info);
- }
- intentActivity.deliverNewIntentLocked(callingUid, r.intent);
- } else if (!r.intent.filterEquals(intentActivity.task.intent)) {
- // In this case we are launching the root activity
- // of the task, but with a different intent. We
- // should start a new instance on top.
- addingToTask = true;
- sourceRecord = intentActivity;
- }
- } else if ((launchFlags&Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) == ) {
- // In this case an activity is being launched in to an
- // existing task, without resetting that task. This
- // is typically the situation of launching an activity
- // from a notification or shortcut. We want to place
- // the new activity on top of the current task.
- addingToTask = true;
- sourceRecord = intentActivity;
- } else if (!intentActivity.task.rootWasReset) {
- // In this case we are launching in to an existing task
- // that has not yet been started from its front door.
- // The current task has been brought to the front.
- // Ideally, we'd probably like to place this new task
- // at the bottom of its stack, but that's a little hard
- // to do with the current organization of the code so
- // for now we'll just drop it.
- intentActivity.task.setIntent(r.intent, r.info);
- }
- if (!addingToTask && reuseTask == null) {
- // We didn't do anything... but it was needed (a.k.a., client
- // don't use that intent!) And for paranoia, make
- // sure we have correctly resumed the top activity.
- if (doResume) {
- targetStack.resumeTopActivityLocked(null, options);
- } else {
- ActivityOptions.abort(options);
- }
- if (r.task == null) Slog.v(TAG,
- "startActivityUncheckedLocked: task left null",
- new RuntimeException("here").fillInStackTrace());
- return ActivityManager.START_TASK_TO_FRONT;
- }
- }
- }
- }
- //String uri = r.intent.toURI();
- //Intent intent2 = new Intent(uri);
- //Slog.i(TAG, "Given intent: " + r.intent);
- //Slog.i(TAG, "URI is: " + uri);
- //Slog.i(TAG, "To intent: " + intent2);
- if (r.packageName != null) {
- // 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.
- ActivityStack topStack = getFocusedStack();
- ActivityRecord top = topStack.topRunningNonDelayedActivityLocked(notTop);
- if (top != null && r.resultTo == null) {
- if (top.realActivity.equals(r.realActivity) && top.userId == r.userId) {
- if (top.app != null && top.app.thread != null) {
- if ((launchFlags&Intent.FLAG_ACTIVITY_SINGLE_TOP) !=
- || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TOP
- || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK) {
- ActivityStack.logStartActivity(EventLogTags.AM_NEW_INTENT, top,
- top.task);
- // For paranoia, make sure we have correctly
- // resumed the top activity.
- topStack.mLastPausedActivity = null;
- if (doResume) {
- resumeTopActivitiesLocked();
- }
- ActivityOptions.abort(options);
- if ((startFlags&ActivityManager.START_FLAG_ONLY_IF_NEEDED) != ) {
- // 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!
- if (r.task == null) Slog.v(TAG,
- "startActivityUncheckedLocked: task left null",
- new RuntimeException("here").fillInStackTrace());
- return ActivityManager.START_RETURN_INTENT_TO_CALLER;
- }
- top.deliverNewIntentLocked(callingUid, r.intent);
- if (r.task == null) Slog.v(TAG,
- "startActivityUncheckedLocked: task left null",
- new RuntimeException("here").fillInStackTrace());
- return ActivityManager.START_DELIVERED_TO_TOP;
- }
- }
- }
- }
- } else {
- if (r.resultTo != null) {
- r.resultTo.task.stack.sendActivityResultLocked(-, r.resultTo, r.resultWho,
- r.requestCode, Activity.RESULT_CANCELED, null);
- }
- ActivityOptions.abort(options);
- if (r.task == null) Slog.v(TAG,
- "startActivityUncheckedLocked: task left null",
- new RuntimeException("here").fillInStackTrace());
- return ActivityManager.START_CLASS_NOT_FOUND;
- }
- boolean newTask = false;
- boolean keepCurTransition = false;
- // Should this be considered a new task?
- if (r.resultTo == null && !addingToTask
- && (launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) != ) {
- targetStack = adjustStackFocus(r);
- moveHomeStack(targetStack.isHomeStack());
- if (reuseTask == null) {
//当前在堆栈顶端的Activity是否就是即将要启动的Activity,有些情况下,如果即将要启动的Activity就在堆栈的顶端,那么,就不会重新启动这个Activity的别一个实例了. 现在为启动一个newtask,要在一个新的Task里面来启动这个Activity了。- r.setTask(targetStack.createTaskRecord(getNextTaskId(), r.info, intent, true),
- null, true);
- if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r + " in new task " +
- r.task);
- } else {
- r.setTask(reuseTask, reuseTask, true);
- }
- newTask = true;
- if (!movedHome) {
- if ((launchFlags &
- (Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_TASK_ON_HOME))
- == (Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_TASK_ON_HOME)) {
- // Caller wants to appear on home activity, so before starting
- // their own activity we will bring home to the front.
- r.task.mOnTopOfHome = true;
- }
- }
- } else if (sourceRecord != null) {
- TaskRecord sourceTask = sourceRecord.task;
- targetStack = sourceTask.stack;
- moveHomeStack(targetStack.isHomeStack());
- if (!addingToTask &&
- (launchFlags&Intent.FLAG_ACTIVITY_CLEAR_TOP) != ) {
- // In this case, we are adding the activity to an existing
- // task, but the caller has asked to clear that task if the
- // activity is already running.
- ActivityRecord top = sourceTask.performClearTaskLocked(r, launchFlags);
- keepCurTransition = true;
- if (top != null) {
- ActivityStack.logStartActivity(EventLogTags.AM_NEW_INTENT, r, top.task);
- top.deliverNewIntentLocked(callingUid, r.intent);
- // For paranoia, make sure we have correctly
- // resumed the top activity.
- targetStack.mLastPausedActivity = null;
- if (doResume) {
- targetStack.resumeTopActivityLocked(null);
- }
- ActivityOptions.abort(options);
- if (r.task == null) Slog.w(TAG,
- "startActivityUncheckedLocked: task left null",
- new RuntimeException("here").fillInStackTrace());
- return ActivityManager.START_DELIVERED_TO_TOP;
- }
- } else if (!addingToTask &&
- (launchFlags&Intent.FLAG_ACTIVITY_REORDER_TO_FRONT) != ) {
- // In this case, we are launching an activity in our own task
- // that may already be running somewhere in the history, and
- // we want to shuffle it to the front of the stack if so.
- final ActivityRecord top = sourceTask.findActivityInHistoryLocked(r);
- if (top != null) {
- final TaskRecord task = top.task;
- task.moveActivityToFrontLocked(top);
- ActivityStack.logStartActivity(EventLogTags.AM_NEW_INTENT, r, task);
- top.updateOptionsLocked(options);
- top.deliverNewIntentLocked(callingUid, r.intent);
- targetStack.mLastPausedActivity = null;
- if (doResume) {
- targetStack.resumeTopActivityLocked(null);
- }
- return ActivityManager.START_DELIVERED_TO_TOP;
- }
- }
- // An existing activity is starting this new activity, so we want
- // to keep the new one in the same task as the one that is starting
- // it.
- r.setTask(sourceTask, sourceRecord.thumbHolder, false);
- if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r
- + " in existing task " + r.task + " from source " + sourceRecord);
- } 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.
- targetStack = adjustStackFocus(r);
- moveHomeStack(targetStack.isHomeStack());
- ActivityRecord prev = targetStack.topActivity();
- r.setTask(prev != null ? prev.task
- : targetStack.createTaskRecord(getNextTaskId(), r.info, intent, true),
- null, true);
- if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r
- + " in new guessed " + r.task);
- }
- mService.grantUriPermissionFromIntentLocked(callingUid, r.packageName,
- intent, r.getUriPermissionsLocked());
- if (newTask) {
- EventLog.writeEvent(EventLogTags.AM_CREATE_TASK, r.userId, r.task.taskId);
- }
- ActivityStack.logStartActivity(EventLogTags.AM_CREATE_ACTIVITY, r, r.task);
- targetStack.mLastPausedActivity = null;
- targetStack.startActivityLocked(r, newTask, doResume, keepCurTransition, options);
- mService.setFocusedActivityLocked(r);
- return ActivityManager.START_SUCCESS;
- }
- ActivityStack.java (base\services\java\com\android\server\am)
- final void startActivityLocked(ActivityRecord r, boolean newTask,
- boolean doResume, boolean keepCurTransition, Bundle options) {
- TaskRecord rTask = r.task;
- final int taskId = rTask.taskId;
- if (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);
- 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() - ; taskNdx >= ; --taskNdx) {
//这里的 mTaskHistory.size()表示当前系统中历史任务的个数,这里肯定是大于0,因为Launcher已经跑起来了。当 mTaskHistory.size()>0时,并且现在要切换新任务时,要做一些任务切的界面操作- task = mTaskHistory.get(taskNdx);
- 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_ON_LOCK_SCREEN) != ,
- r.userId);
- if (VALIDATE_TOKENS) {
- validateAppTokensLocked();
- }
- ActivityOptions.abort(options);
- return;
- }
- break;
- } else if (task.numFullscreen > ) {
- 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
- // activity
- if (task == r.task && mTaskHistory.indexOf(task) != (mTaskHistory.size() - )) {
- mStackSupervisor.mUserLeaving = false;
- if (DEBUG_USER_LEAVING) Slog.v(TAG,
- "startActivity() behind front, mUserLeaving=false");
- }
- task = r.task;
- // Slot the activity into the history stack and proceed
- if (DEBUG_ADD_REMOVE) Slog.i(TAG, "Adding activity " + r + " to stack to task " + task,
- new RuntimeException("here").fillInStackTrace());
- task.addActivityToTop(r);
- r.putInHistory();
- r.frontOfTask = newTask;
- if (!isHomeStack() || numActivities() > ) {
- // 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,
- "Prepare open transition: starting " + r);
- if ((r.intent.getFlags()&Intent.FLAG_ACTIVITY_NO_ANIMATION) != ) {
- mWindowManager.prepareAppTransition(AppTransition.TRANSIT_NONE, keepCurTransition);
- mNoAnimActivities.add(r);
- } else {
- mWindowManager.prepareAppTransition(newTask
- ? AppTransition.TRANSIT_TASK_OPEN
- : AppTransition.TRANSIT_ACTIVITY_OPEN, keepCurTransition);
- mNoAnimActivities.remove(r);
- }
- r.updateOptionsLocked(options);
- mWindowManager.addAppToken(task.mActivities.indexOf(r),
- r.appToken, r.task.taskId, mStackId, r.info.screenOrientation, r.fullscreen,
- (r.info.flags & ActivityInfo.FLAG_SHOW_ON_LOCK_SCREEN) != , r.userId);
- 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) != ) {
- resetTaskIfNeededLocked(r, r);
- doShow = topRunningNonDelayedActivityLocked(null) == r;
- }
- }
- 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);
- }
- } 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_ON_LOCK_SCREEN) != , r.userId);
- ActivityOptions.abort(options);
- }
- if (VALIDATE_TOKENS) {
- validateAppTokensLocked();
- }
- //这里传进来的参数doResume为true,于是调用resumeTopActivityLocked进一步操作。
- if (doResume) {
- mStackSupervisor.resumeTopActivitiesLocked();
- }
- }
- ActivityStackSupervisor.java (base\services\java\com\android\server\am)
- boolean resumeTopActivitiesLocked() {
- return resumeTopActivitiesLocked(null, null, null);
- }
- boolean resumeTopActivitiesLocked(ActivityStack targetStack, ActivityRecord target,
- Bundle targetOptions) {
- if (targetStack == null) {
- targetStack = getFocusedStack();
- }
- boolean result = false;
- for (int stackNdx = mStacks.size() - ; stackNdx >= ; --stackNdx) {
- final ActivityStack stack = mStacks.get(stackNdx);
- if (isFrontStack(stack)) {
- if (stack == targetStack) {
//把顶层的activity设置resume状态。- result = stack.resumeTopActivityLocked(target, targetOptions);
- } else {
- stack.resumeTopActivityLocked(null);
- }
- }
- }
- return result;
- }
- ActivityStack.java (base\services\java\com\android\server\am)
- final boolean resumeTopActivityLocked(ActivityRecord prev, Bundle options) {
- if (ActivityManagerService.DEBUG_LOCKSCREEN) mService.logLockScreen("");
- // Find the first activity that is not finishing.函数先通过调用topRunningActivityLocked函数获得堆栈顶端的Activity,这里就是MainActivity了,之前已经设置好的,保存在next变量中。
- ActivityRecord next = topRunningActivityLocked(null);
- // Remember how we'll process this pause/resume situation, and ensure
- // that the state is reset however we wind up proceeding.
//接下来把mUserLeaving的保存在本地变量userLeaving中,然后重新设置为false,在前面,mUserLeaving的值为true,因此,这里的userLeaving为true。- final boolean userLeaving = mStackSupervisor.mUserLeaving;
- mStackSupervisor.mUserLeaving = false;
- if (next == null) {
- // There are no more activities! Let's just start up the
- // Launcher...
- ActivityOptions.abort(options);
- if (DEBUG_STATES) Slog.d(TAG, "resumeTopActivityLocked: No more activities go home");
- if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
- return mStackSupervisor.resumeHomeActivity(prev);
- }
- next.delayedResume = false;
- // If the top activity is the resumed one, nothing to do.
//这里的mResumedActivity为Launcher,因为Launcher是当前正被执行的Activity。
//它首先看要启动的Activity是否就是当前处理Resumed状态的Activity,如果是的话,那就什么都不用做,直接返回就可以了;否则再看一下系统当前是否休眠状态,如果是的话,再看看要启动的Activity是否就是当前处于堆栈顶端的Activity,如果是的话,也是什么都不用做。- if (mResumedActivity == next && next.state == ActivityState.RESUMED &&
- mStackSupervisor.allResumedActivitiesComplete()) {
- // Make sure we have executed any pending transitions, since there
- // should be nothing left to do at this point.
- mWindowManager.executeAppTransition();
- mNoAnimActivities.clear();
- ActivityOptions.abort(options);
- if (DEBUG_STATES) Slog.d(TAG, "resumeTopActivityLocked: Top activity resumed " + next);
- if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
- return false;
- }
- final TaskRecord nextTask = next.task;
- final TaskRecord prevTask = prev != null ? prev.task : null;
- if (prevTask != null && prevTask.mOnTopOfHome && prev.finishing && prev.frontOfTask) {
- if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
- if (prevTask == nextTask) {
- ArrayList<ActivityRecord> activities = prevTask.mActivities;
- final int numActivities = activities.size();
- for (int activityNdx = ; activityNdx < numActivities; ++activityNdx) {
- final ActivityRecord r = activities.get(activityNdx);
- // r is usually the same as next, but what if two activities were launched
- // before prev finished?
- if (!r.finishing) {
- r.frontOfTask = true;
- break;
- }
- }
- } else if (prevTask != topTask()) {
- // This task is going away but it was supposed to return to the home task.
- // Now the task above it has to return to the home task instead.
- final int taskNdx = mTaskHistory.indexOf(prevTask) + ;
- mTaskHistory.get(taskNdx).mOnTopOfHome = true;
- } else {
- if (DEBUG_STATES) Slog.d(TAG, "resumeTopActivityLocked: Launching home next");
- return mStackSupervisor.resumeHomeActivity(prev);
- }
- }
- // If we are sleeping, and there is no resumed activity, and the top
- // activity is paused, well that is the state we want.
- if (mService.isSleepingOrShuttingDown()
- && mLastPausedActivity == next
- && mStackSupervisor.allPausedActivitiesComplete()) {
- // Make sure we have executed any pending transitions, since there
- // should be nothing left to do at this point.
- mWindowManager.executeAppTransition();
- mNoAnimActivities.clear();
- ActivityOptions.abort(options);
- if (DEBUG_STATES) Slog.d(TAG, "resumeTopActivityLocked: Going to sleep and all paused");
- if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
- return false;
- }
- // Make sure that the user who owns this activity is started. If not,
- // we will just leave it as is because someone should be bringing
- // another user's activities to the top of the stack.
- if (mService.mStartedUsers.get(next.userId) == null) {
- Slog.w(TAG, "Skipping resume of top activity " + next
- + ": user " + next.userId + " is stopped");
- if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
- return false;
- }
- // The activity may be waiting for stop, but that is no longer
- // appropriate for it.
- mStackSupervisor.mStoppingActivities.remove(next);
- mStackSupervisor.mGoingToSleepActivities.remove(next);
- next.sleeping = false;
- mStackSupervisor.mWaitingVisibleActivities.remove(next);
- next.updateOptionsLocked(options);
- if (DEBUG_SWITCH) Slog.v(TAG, "Resuming " + next);
- // If we are currently pausing an activity, then don't do anything
- // until that is done.
- if (!mStackSupervisor.allPausedActivitiesComplete()) {
- if (DEBUG_SWITCH || DEBUG_PAUSE || DEBUG_STATES) Slog.v(TAG,
- "resumeTopActivityLocked: Skip resume: some activity pausing.");
- if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
- return false;
- }
- // Okay we are now going to start a switch, to 'next'. We may first
- // have to pause the current activity, but this is an important point
- // where we have decided to go to 'next' so keep track of that.
- // XXX "App Redirected" dialog is getting too many false positives
- // at this point, so turn off for now.
- if (false) {
- if (mLastStartedActivity != null && !mLastStartedActivity.finishing) {
- long now = SystemClock.uptimeMillis();
- final boolean inTime = mLastStartedActivity.startTime !=
- && (mLastStartedActivity.startTime + START_WARN_TIME) >= now;
- final int lastUid = mLastStartedActivity.info.applicationInfo.uid;
- final int nextUid = next.info.applicationInfo.uid;
- if (inTime && lastUid != nextUid
- && lastUid != next.launchedFromUid
- && mService.checkPermission(
- android.Manifest.permission.STOP_APP_SWITCHES,
- -, next.launchedFromUid)
- != PackageManager.PERMISSION_GRANTED) {
- mService.showLaunchWarningLocked(mLastStartedActivity, next);
- } else {
- next.startTime = now;
- mLastStartedActivity = next;
- }
- } else {
- next.startTime = SystemClock.uptimeMillis();
- mLastStartedActivity = next;
- }
- }
- // We need to start pausing the current activity so the top one
- // can be resumed...
//上面两个条件(mResumedActivity == next && next.state == ActivityState.RESUMED)都不满足,因此,在继续往下执行之前,首先要把当处于Resumed状态的Activity推入Paused状态,然后才可以启动新的Activity。但是在将当前这个Resumed状态的Activity推入Paused状态之前,首先要看一下当前是否有Activity正在进入Pausing状态,如果有的话,当前这个Resumed状态的Activity就要稍后才能进入Paused状态了,这样就保证了所有需要进入Paused状态的Activity串行处理
这里没有处于Pausing状态的Activity,即mPausingActivity为null,而且mResumedActivity也不为null,于是就调用startPausingLocked函数把Launcher推入Paused状态去了。
boolean pausing = mStackSupervisor.pauseBackStacks(userLeaving);- if (mResumedActivity != null) {
- pausing = true;
//同时也是在此调用内,最终调用到应用程序activity的onCreate()函数。- startPausingLocked(userLeaving, false);
- if (DEBUG_STATES) Slog.d(TAG, "resumeTopActivityLocked: Pausing " + mResumedActivity);
- }
- if (pausing) {
- if (DEBUG_SWITCH || DEBUG_STATES) Slog.v(TAG,
- "resumeTopActivityLocked: Skip resume: need to start pausing");
- // At this point we want to put the upcoming activity's process
- // at the top of the LRU list, since we know we will be needing it
- // very soon and it would be a waste to let it get killed if it
- // happens to be sitting towards the end.
- if (next.app != null && next.app.thread != null) {
- // No reason to do full oom adj update here; we'll let that
- // happen whenever it needs to later.
- mService.updateLruProcessLocked(next.app, false, true);
- }
- if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
- return true;
- }
- // If the most recent activity was noHistory but was only stopped rather
- // than stopped+finished because the device went to sleep, we need to make
- // sure to finish it as we're making a new activity topmost.
- if (mService.mSleeping && mLastNoHistoryActivity != null &&
- !mLastNoHistoryActivity.finishing) {
- if (DEBUG_STATES) Slog.d(TAG, "no-history finish of " + mLastNoHistoryActivity +
- " on new resume");
- requestFinishActivityLocked(mLastNoHistoryActivity.appToken, Activity.RESULT_CANCELED,
- null, "no-history", false);
- mLastNoHistoryActivity = null;
- }
- if (prev != null && prev != next) {
- if (!prev.waitingVisible && next != null && !next.nowVisible) {
- prev.waitingVisible = true;
- mStackSupervisor.mWaitingVisibleActivities.add(prev);
- if (DEBUG_SWITCH) Slog.v(
- TAG, "Resuming top, waiting visible to hide: " + prev);
- } else {
- // The next activity is already visible, so hide the previous
- // activity's windows right now so we can show the new one ASAP.
- // We only do this if the previous is finishing, which should mean
- // it is on top of the one being resumed so hiding it quickly
- // is good. Otherwise, we want to do the normal route of allowing
- // the resumed activity to be shown so we can decide if the
- // previous should actually be hidden depending on whether the
- // new one is found to be full-screen or not.
- if (prev.finishing) {
- mWindowManager.setAppVisibility(prev.appToken, false);
- if (DEBUG_SWITCH) Slog.v(TAG, "Not waiting for visible to hide: "
- + prev + ", waitingVisible="
- + (prev != null ? prev.waitingVisible : null)
- + ", nowVisible=" + next.nowVisible);
- } else {
- if (DEBUG_SWITCH) Slog.v(TAG, "Previous already visible but still waiting to hide: "
- + prev + ", waitingVisible="
- + (prev != null ? prev.waitingVisible : null)
- + ", nowVisible=" + next.nowVisible);
- }
- }
- }
- // Launching this app's activity, make sure the app is no longer
- // considered stopped.
- try {
- AppGlobals.getPackageManager().setPackageStoppedState(
- next.packageName, false, next.userId); /* TODO: Verify if correct userid */
- } catch (RemoteException e1) {
- } catch (IllegalArgumentException e) {
- Slog.w(TAG, "Failed trying to unstop package "
- + next.packageName + ": " + e);
- }
- // We are starting up the next activity, so tell the window manager
- // that the previous one will be hidden soon. This way it can know
- // to ignore it when computing the desired screen orientation.
- boolean anim = true;
- if (prev != null) {
- if (prev.finishing) {
- if (DEBUG_TRANSITION) Slog.v(TAG,
- "Prepare close transition: prev=" + prev);
- if (mNoAnimActivities.contains(prev)) {
- anim = false;
- mWindowManager.prepareAppTransition(AppTransition.TRANSIT_NONE, false);
- } else {
- mWindowManager.prepareAppTransition(prev.task == next.task
- ? AppTransition.TRANSIT_ACTIVITY_CLOSE
- : AppTransition.TRANSIT_TASK_CLOSE, false);
- }
- mWindowManager.setAppWillBeHidden(prev.appToken);
- mWindowManager.setAppVisibility(prev.appToken, false);
- } else {
- if (DEBUG_TRANSITION) Slog.v(TAG, "Prepare open transition: prev=" + prev);
- if (mNoAnimActivities.contains(next)) {
- anim = false;
- mWindowManager.prepareAppTransition(AppTransition.TRANSIT_NONE, false);
- } else {
- mWindowManager.prepareAppTransition(prev.task == next.task
- ? AppTransition.TRANSIT_ACTIVITY_OPEN
- : AppTransition.TRANSIT_TASK_OPEN, false);
- }
- }
- if (false) {
- mWindowManager.setAppWillBeHidden(prev.appToken);
- mWindowManager.setAppVisibility(prev.appToken, false);
- }
- } else {
- if (DEBUG_TRANSITION) Slog.v(TAG, "Prepare open transition: no previous");
- if (mNoAnimActivities.contains(next)) {
- anim = false;
- mWindowManager.prepareAppTransition(AppTransition.TRANSIT_NONE, false);
- } else {
- mWindowManager.prepareAppTransition(AppTransition.TRANSIT_ACTIVITY_OPEN, false);
- }
- }
- if (anim) {
- next.applyOptionsLocked();
- } else {
- next.clearOptionsLocked();
- }
- ActivityStack lastStack = mStackSupervisor.getLastStack();
- if (next.app != null && next.app.thread != null) {
- if (DEBUG_SWITCH) Slog.v(TAG, "Resume running: " + next);
- // This activity is now becoming visible.
- mWindowManager.setAppVisibility(next.appToken, true);
- // schedule launch ticks to collect information about slow apps.
- next.startLaunchTickingLocked();
- ActivityRecord lastResumedActivity =
- lastStack == null ? null :lastStack.mResumedActivity;
- ActivityState lastState = next.state;
- mService.updateCpuStats();
- if (DEBUG_STATES) Slog.v(TAG, "Moving to RESUMED: " + next + " (in existing)");
- next.state = ActivityState.RESUMED;
- mResumedActivity = next;
- next.task.touchActiveTime();
- mService.addRecentTaskLocked(next.task);
- mService.updateLruProcessLocked(next.app, true, true);
- updateLRUListLocked(next);
- // Have the window manager re-evaluate the orientation of
- // the screen based on the new activity order.
- boolean notUpdated = true;
- if (mStackSupervisor.isFrontStack(this)) {
- Configuration config = mWindowManager.updateOrientationFromAppTokens(
- mService.mConfiguration,
- next.mayFreezeScreenLocked(next.app) ? next.appToken : null);
- if (config != null) {
- next.frozenBeforeDestroy = true;
- }
- notUpdated = !mService.updateConfigurationLocked(config, next, false, false);
- }
- if (notUpdated) {
- // The configuration update wasn't able to keep the existing
- // instance of the activity, and instead started a new one.
- // We should be all done, but let's just make sure our activity
- // is still at the top and schedule another run if something
- // weird happened.
- ActivityRecord nextNext = topRunningActivityLocked(null);
- if (DEBUG_SWITCH || DEBUG_STATES) Slog.i(TAG,
- "Activity config changed during resume: " + next
- + ", new next: " + nextNext);
- if (nextNext != next) {
- // Do over!
- mStackSupervisor.scheduleResumeTopActivities();
- }
- if (mStackSupervisor.reportResumedActivityLocked(next)) {
- mNoAnimActivities.clear();
- if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
- return true;
- }
- if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
- return false;
- }
- try {
- // Deliver all pending results.
- ArrayList<ResultInfo> a = next.results;
- if (a != null) {
- final int N = a.size();
- if (!next.finishing && N > ) {
- if (DEBUG_RESULTS) Slog.v(
- TAG, "Delivering results to " + next
- + ": " + a);
- next.app.thread.scheduleSendResult(next.appToken, a);
- }
- }
- if (next.newIntents != null) {
- next.app.thread.scheduleNewIntent(next.newIntents, next.appToken);
- }
- EventLog.writeEvent(EventLogTags.AM_RESUME_ACTIVITY,
- next.userId, System.identityHashCode(next),
- next.task.taskId, next.shortComponentName);
- next.sleeping = false;
- mService.showAskCompatModeDialogLocked(next);
- next.app.pendingUiClean = true;
- next.app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_TOP);
- next.app.thread.scheduleResumeActivity(next.appToken, next.app.repProcState,
- mService.isNextTransitionForward());
- mStackSupervisor.checkReadyForSleepLocked();
- if (DEBUG_STATES) Slog.d(TAG, "resumeTopActivityLocked: Resumed " + next);
- } catch (Exception e) {
- // Whoops, need to restart this activity!
- if (DEBUG_STATES) Slog.v(TAG, "Resume failed; resetting state to "
- + lastState + ": " + next);
- next.state = lastState;
- if (lastStack != null) {
- lastStack.mResumedActivity = lastResumedActivity;
- }
- Slog.i(TAG, "Restarting because process died: " + next);
- if (!next.hasBeenLaunched) {
- next.hasBeenLaunched = true;
- } else if (SHOW_APP_STARTING_PREVIEW && lastStack != null &&
- mStackSupervisor.isFrontStack(lastStack)) {
- mWindowManager.setAppStartingWindow(
- next.appToken, next.packageName, next.theme,
- mService.compatibilityInfoForPackageLocked(next.info.applicationInfo),
- next.nonLocalizedLabel, next.labelRes, next.icon, next.logo,
- next.windowFlags, null, true);
- }
- mStackSupervisor.startSpecificActivityLocked(next, true, false);
- if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
- return true;
- }
- // From this point on, if something goes wrong there is no way
- // to recover the activity.
- try {
- next.visible = true;
- completeResumeLocked(next);
- } catch (Exception e) {
- // If any exception gets thrown, toss away this
- // activity and try the next one.
- Slog.w(TAG, "Exception thrown during resume of " + next, e);
- requestFinishActivityLocked(next.appToken, Activity.RESULT_CANCELED, null,
- "resume-exception", true);
- if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
- return true;
- }
- next.stopped = false;
- } else {
- // Whoops, need to restart this activity!
- if (!next.hasBeenLaunched) {
- next.hasBeenLaunched = true;
- } else {
- if (SHOW_APP_STARTING_PREVIEW) {
- mWindowManager.setAppStartingWindow(
- next.appToken, next.packageName, next.theme,
- mService.compatibilityInfoForPackageLocked(
- next.info.applicationInfo),
- next.nonLocalizedLabel,
- next.labelRes, next.icon, next.logo, next.windowFlags,
- null, true);
- }
- if (DEBUG_SWITCH) Slog.v(TAG, "Restarting: " + next);
- }
- if (DEBUG_STATES) Slog.d(TAG, "resumeTopActivityLocked: Restarting " + next);
- mStackSupervisor.startSpecificActivityLocked(next, true, true);
- }
- if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
- return true;
- }
android activity 启动过程分析(source code 4.4)的更多相关文章
- Activity启动过程分析
Android的四大组件中除了BroadCastReceiver以外,其他三种组件都必须在AndroidManifest中注册,对于BroadCastReceiver来说,它既可以在AndroidMa ...
- Android Activity启动流程源码全解析(1)
前言 Activity是Android四大组件的老大,我们对它的生命周期方法调用顺序都烂熟于心了,可是这些生命周期方法到底是怎么调用的呢?在启动它的时候会用到startActivty这个方法,但是这个 ...
- Android Activity启动流程源码全解析(2)
接上之前的分析 ++Android Activity启动流程源码全解析(1)++ 1.正在运行的Activity调用startPausingLocked 一个一个分析,先来看看startPausing ...
- Android Activity启动黑/白屏原因与解决方式
Android Activity启动黑/白屏原因与解决方式 我们新建一个HelloWorld项目,运行在手机上时,Activity打开之前会有一个动画,而这个动画里是全白或者全黑的(取决于你的主题是亮 ...
- 【Android】应用程序Activity启动过程分析
在Android系统中,有两种操作会引发Activity的启动,一种用户点击应用程序图标时,Launcher会为我们启动应用程序的主Activity:应用程序的默认Activity启动起来后,它又可以 ...
- android Activity启动过程(四)startActivityUncheckedLocked
final int startActivityUncheckedLocked(ActivityRecord r, ActivityRecord sourceRecord, IVoiceInteract ...
- Android Activity启动耗时统计方案
作者:林基宗 Activity的启动速度是很多开发者关心的问题,当页面跳转耗时过长时,App就会给人一种非常笨重的感觉.在遇到某个页面启动过慢的时候,开发的第一直觉一般是onCreate执行速度太慢了 ...
- android activity启动的4种方式记录及打开其他应用的activity的坑
Android启动的四种方式分别为standard,singleTop,singleTask,singleInstence. standard是最常见的activity启动方式,也是默认的启动的方式. ...
- Android Activity 启动模式详解
最近有群里的朋友问我 Activity的四种启动模式分别是什么意思? 当初因为项目比较忙,草草的解释了下, Api文档中说的也只是一般,在这里就小记一下吧,以便有更多的朋友对Activity启动模式了 ...
随机推荐
- php安装swoole2.1.2
准备环境: cos7.2 & php 7.1.7 1.www.swoole.com找到对应版本2.使用git clone或则 wget 命令(下载后需解压)进行下载3.在swoole目录 ...
- linux(centos 7)安装及使用yum
yum介绍: Yum(全称为 Yellow dog Updater, Modified)是一个在Fedora和RedHat以及CentOS中的Shell前端软件包管理器.基于RPM包管理,能够从指定的 ...
- 吴裕雄--天生自然 pythonTensorFlow图形数据处理:windows操作系统删除tensorflow
输入:pip uninstall tensorflow Proceed(y/n):y
- 吴裕雄--天生自然python TensorFlow图片数据处理:No module named 'tensorflow.examples.tutorials'解决办法
import numpy as np import tensorflow as tf from tensorflow.examples.tutorials.mnist import input_dat ...
- TPO5-3 The Cambrian Explosion
At one time, the animals present in these fossil beds were assigned to various modern animal groups, ...
- Mac中Apache常用命令
Apache常用命令记录,还是记一下吧,总是忘记. Apache常用命令: # sudo apachectl start // 启动Apache服务 # sudo apachectl stop // ...
- SpringBoot集成ssm-druid-通用mapper
简单介绍 springboot 首先什么是springboot? springboot是spring的另外一款框架,设计目的是用来简化新的spring应用的搭建和开发时所需要的特定的配置,从而使开发过 ...
- sql 新增随机数
update RemoteDetection set humidity=round((rand()*3+29),0),TEMPERATURE=round((rand()*3+16),0),atmosp ...
- xmemcached过期时间
最近项目中使用到了Memcached,而客户端选择了XMemcached ,在设置过期时间时,因对Memcached 不熟悉,将expire 设置为1000000000,本意表示尽量长的时间不要过期, ...
- Synchronized的jvm实现
参考文档: https://www.cnblogs.com/dennyzhangdd/p/6734638.html