AMS & WMS,应该是app端打交道最多的2个framwork层的service。

ActivityManagerService 是android提供给用于管理Activity运行状态的系统进程。

本系列共分3个部分,概述,ActivityStatck & Activiy Task.

AMS 主要用于管理Activity之间的交互问题。

核心问题有以下几个:

1.activity 生命周期管理

2.intent是怎么传递数据的。(可能跨进程,以及双向传递)

3.launchmode是怎么使用的。(Task的概念)

一. AMS概述

首先AMS 是一个同我们开发的service非常相似的一个service,只不过它的作用是管理activity。

所以AMS是一个进程,并且当开机以后,它就常驻在系统里面,归ServiceManager调度。

而AMS启动后,它开始有一个线程监听处理客户的需求。

一下为android5.0 的代码:

\frameworks\base\services\java\com\android\server\SystemServera.java

  1. private void startBootstrapServices() {
  2. // Wait for installd to finish starting up so that it has a chance to
  3. // create critical directories such as /data/user with the appropriate
  4. // permissions. We need this to complete before we initialize other services.
  5. mInstaller = mSystemServiceManager.startService(Installer.class);
  6.  
  7. // Activity manager runs the show.
  8. mActivityManagerService = mSystemServiceManager.startService(
  9. ActivityManagerService.Lifecycle.class).getService();
  10. mActivityManagerService.setSystemServiceManager(mSystemServiceManager);
  11.  
  12. // Power manager needs to be started early because other services need it.
  13. // Native daemons may be watching for it to be registered so it must be ready
  14. // to handle incoming binder calls immediately (including being able to verify
  15. // the permissions for those calls).
  16. mPowerManagerService = mSystemServiceManager.startService(PowerManagerService.class);
  17.  
  18. // Now that the power manager has been started, let the activity manager
  19. // initialize power management features.
  20. mActivityManagerService.initPowerManagement();
  21.  
  22. // Display manager is needed to provide display metrics before package manager
  23. // starts up.
  24. mDisplayManagerService = mSystemServiceManager.startService(DisplayManagerService.class);
  25.  
  26. // We need the default display before we can initialize the package manager.
  27. mSystemServiceManager.startBootPhase(SystemService.PHASE_WAIT_FOR_DEFAULT_DISPLAY);
  28.  
  29. // Only run "core" apps if we're encrypting the device.
  30. String cryptState = SystemProperties.get("vold.decrypt");
  31. if (ENCRYPTING_STATE.equals(cryptState)) {
  32. Slog.w(TAG, "Detected encryption in progress - only parsing core apps");
  33. mOnlyCore = true;
  34. } else if (ENCRYPTED_STATE.equals(cryptState)) {
  35. Slog.w(TAG, "Device encrypted - only parsing core apps");
  36. mOnlyCore = true;
  37. }
  38.  
  39. // Start the package manager.
  40. Slog.i(TAG, "Package Manager");
  41. mPackageManagerService = PackageManagerService.main(mSystemContext, mInstaller,
  42. mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore);
  43. mFirstBoot = mPackageManagerService.isFirstBoot();
  44. mPackageManager = mSystemContext.getPackageManager();
  45.  
  46. Slog.i(TAG, "User Service");
  47. ServiceManager.addService(Context.USER_SERVICE, UserManagerService.getInstance());
  48.  
  49. // Initialize attribute cache used to cache resources from packages.
  50. AttributeCache.init(mSystemContext);
  51.  
  52. // Set up the Application instance for the system process and get started.
  53. mActivityManagerService.setSystemProcess();
  54. }

startBootstrapServices

可以看到AMS在这里启动。而这个函数startBootstrapServices是在run方法中运行。

  1. public ActivityManagerService(Context systemContext) {
  2. mContext = systemContext;
  3. mFactoryTest = FactoryTest.getMode();
  4. mSystemThread = ActivityThread.currentActivityThread();
  5.  
  6. Slog.i(TAG, "Memory class: " + ActivityManager.staticGetMemoryClass());
  7.  
  8. mHandlerThread = new ServiceThread(TAG,
  9. android.os.Process.THREAD_PRIORITY_FOREGROUND, false /*allowIo*/);
  10. mHandlerThread.start();
  11. mHandler = new MainHandler(mHandlerThread.getLooper());
  12.  
  13. mFgBroadcastQueue = new BroadcastQueue(this, mHandler,
  14. "foreground", BROADCAST_FG_TIMEOUT, false);
  15. mBgBroadcastQueue = new BroadcastQueue(this, mHandler,
  16. "background", BROADCAST_BG_TIMEOUT, true);
  17. mBroadcastQueues[0] = mFgBroadcastQueue;
  18. mBroadcastQueues[1] = mBgBroadcastQueue;
  19.  
  20. mServices = new ActiveServices(this);
  21. mProviderMap = new ProviderMap(this);
  22.  
  23. // TODO: Move creation of battery stats service outside of activity manager service.
  24. File dataDir = Environment.getDataDirectory();
  25. File systemDir = new File(dataDir, "system");
  26. systemDir.mkdirs();
  27. mBatteryStatsService = new BatteryStatsService(systemDir, mHandler);
  28. mBatteryStatsService.getActiveStatistics().readLocked();
  29. mBatteryStatsService.getActiveStatistics().writeAsyncLocked();
  30. mOnBattery = DEBUG_POWER ? true
  31. : mBatteryStatsService.getActiveStatistics().getIsOnBattery();
  32. mBatteryStatsService.getActiveStatistics().setCallback(this);
  33.  
  34. mProcessStats = new ProcessStatsService(this, new File(systemDir, "procstats"));
  35.  
  36. mAppOpsService = new AppOpsService(new File(systemDir, "appops.xml"), mHandler);
  37.  
  38. mGrantFile = new AtomicFile(new File(systemDir, "urigrants.xml"));
  39.  
  40. // User 0 is the first and only user that runs at boot.
  41. mStartedUsers.put(0, new UserStartedState(new UserHandle(0), true));
  42. mUserLru.add(Integer.valueOf(0));
  43. updateStartedUserArrayLocked();
  44.  
  45. GL_ES_VERSION = SystemProperties.getInt("ro.opengles.version",
  46. ConfigurationInfo.GL_ES_VERSION_UNDEFINED);
  47.  
  48. mConfiguration.setToDefaults();
  49. mConfiguration.setLocale(Locale.getDefault());
  50.  
  51. mConfigurationSeq = mConfiguration.seq = 1;
  52. mProcessCpuTracker.init();
  53.  
  54. mCompatModePackages = new CompatModePackages(this, systemDir, mHandler);
  55. mIntentFirewall = new IntentFirewall(new IntentFirewallInterface(), mHandler);
  56. mStackSupervisor = new ActivityStackSupervisor(this);
  57. mTaskPersister = new TaskPersister(systemDir, mStackSupervisor);
  58.  
  59. mProcessCpuThread = new Thread("CpuTracker") {
  60. @Override
  61. public void run() {
  62. while (true) {
  63. try {
  64. try {
  65. synchronized(this) {
  66. final long now = SystemClock.uptimeMillis();
  67. long nextCpuDelay = (mLastCpuTime.get()+MONITOR_CPU_MAX_TIME)-now;
  68. long nextWriteDelay = (mLastWriteTime+BATTERY_STATS_TIME)-now;
  69. //Slog.i(TAG, "Cpu delay=" + nextCpuDelay
  70. // + ", write delay=" + nextWriteDelay);
  71. if (nextWriteDelay < nextCpuDelay) {
  72. nextCpuDelay = nextWriteDelay;
  73. }
  74. if (nextCpuDelay > 0) {
  75. mProcessCpuMutexFree.set(true);
  76. this.wait(nextCpuDelay);
  77. }
  78. }
  79. } catch (InterruptedException e) {
  80. }
  81. updateCpuStatsNow();
  82. } catch (Exception e) {
  83. Slog.e(TAG, "Unexpected exception collecting process stats", e);
  84. }
  85. }
  86. }
  87. };
  88.  
  89. mLockToAppRequest = new LockToAppRequestDialog(mContext, this);
  90.  
  91. Watchdog.getInstance().addMonitor(this);
  92. Watchdog.getInstance().addThread(mHandler);
  93. }

ActivityManagerService

  1. mStackSupervisor = new ActivityStackSupervisor(this);
  2. mTaskPersister = new TaskPersister(systemDir, mStackSupervisor);

这两句是ActivityStack & ActivityTask设置的地方。

public void setSystemProcess()就比较简单了,它不仅注册了自己一个server, 还注册了其他的server。

  1. ServiceManager.addService(Context.ACTIVITY_SERVICE, this, true);
  2. ServiceManager.addService(ProcessStats.SERVICE_NAME, mProcessStats);
  3. ServiceManager.addService("meminfo", new MemBinder(this));
  4. ServiceManager.addService("gfxinfo", new GraphicsBinder(this));
  5. ServiceManager.addService("dbinfo", new DbBinder(this));
  6. if (MONITOR_CPU_USAGE) {
  7. ServiceManager.addService("cpuinfo", new CpuBinder(this));
  8. }
  9. ServiceManager.addService("permission", new PermissionController(this));

二.Activit状态管理---ActivityStack

1.ActivityState

定义了如下状态:

状态变化图。

三:startActivity

startActivity@ActivityManagerService.java

startActivityAsUser@ActivityManagerService.java

startActivityMayWait@ActivityStack.java

startActivityLocked@ActivityStack.java

sartActivityUncheckedLocked@ActivityStack.java

这5个函数先后关系,就是上面的顺序。

  1. public final int startActivity(IApplicationThread caller, String callingPackage,
  2. Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
  3. int startFlags, ProfilerInfo profilerInfo, Bundle options) {
  4. return startActivityAsUser(caller, callingPackage, intent, resolvedType, resultTo,
  5. resultWho, requestCode, startFlags, profilerInfo, options,
  6. UserHandle.getCallingUserId());
  7. }

多了一个

  1. UserHandle.getCallingUserId()

调用者的Userid值,通过bind机制获得的。

  1. @Override
  2. public final int startActivityAsUser(IApplicationThread caller, String callingPackage,
  3. Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
  4. int startFlags, ProfilerInfo profilerInfo, Bundle options, int userId) {
  5. enforceNotIsolatedCaller("startActivity");
  6. userId = handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), userId,
  7. false, ALLOW_FULL_ONLY, "startActivity", null);
  8. // TODO: Switch to user app stacks here.
  9. return mStackSupervisor.startActivityMayWait(caller, -1, callingPackage, intent,
  10. resolvedType, null, null, resultTo, resultWho, requestCode, startFlags,
  11. profilerInfo, null, null, options, false, userId, null, null);
  12. }

enforceNotIsolatedCaller 的目的是确认当前用户是否属于被隔离的对象。

接下来是 startActivityMayWait

startActivityMayWait

  1. ActivityStackSupervisor.startActivityMayWait
  1. final int startActivityMayWait(IApplicationThread caller, int callingUid,
  2. String callingPackage, Intent intent, String resolvedType,
  3. IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
  4. IBinder resultTo, String resultWho, int requestCode, int startFlags,
  5. ProfilerInfo profilerInfo, WaitResult outResult, Configuration config,
  6. Bundle options, boolean ignoreTargetSecurity, int userId,
  7. IActivityContainer iContainer, TaskRecord inTask) {
  8. // Refuse possible leaked file descriptors
  9. if (intent != null && intent.hasFileDescriptors()) {
  10. throw new IllegalArgumentException("File descriptors passed in Intent");
  11. }
  12. boolean componentSpecified = intent.getComponent() != null;
  13.  
  14. // Don't modify the client's object!
  15. intent = new Intent(intent);
  16.  
  17. // Collect information about the target of the Intent.
  18. ActivityInfo aInfo =
  19. resolveActivity(intent, resolvedType, startFlags, profilerInfo, userId);
  20.  
  21. ActivityContainer container = (ActivityContainer)iContainer;
  22. synchronized (mService) {
  23. if (container != null && container.mParentActivity != null &&
  24. container.mParentActivity.state != RESUMED) {
  25. // Cannot start a child activity if the parent is not resumed.
  26. return ActivityManager.START_CANCELED;
  27. }
  28. final int realCallingPid = Binder.getCallingPid();
  29. final int realCallingUid = Binder.getCallingUid();
  30. int callingPid;
  31. if (callingUid >= 0) {
  32. callingPid = -1;
  33. } else if (caller == null) {
  34. callingPid = realCallingPid;
  35. callingUid = realCallingUid;
  36. } else {
  37. callingPid = callingUid = -1;
  38. }
  39.  
  40. final ActivityStack stack;
  41. if (container == null || container.mStack.isOnHomeDisplay()) {
  42. stack = mFocusedStack;
  43. } else {
  44. stack = container.mStack;
  45. }
  46. stack.mConfigWillChange = config != null && mService.mConfiguration.diff(config) != 0;
  47. if (DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
  48. "Starting activity when config will change = " + stack.mConfigWillChange);
  49.  
  50. final long origId = Binder.clearCallingIdentity();
  51.  
  52. if (aInfo != null &&
  53. (aInfo.applicationInfo.privateFlags
  54. &ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE) != 0) {
  55. // This may be a heavy-weight process! Check to see if we already
  56. // have another, different heavy-weight process running.
  57. if (aInfo.processName.equals(aInfo.applicationInfo.packageName)) {
  58. if (mService.mHeavyWeightProcess != null &&
  59. (mService.mHeavyWeightProcess.info.uid != aInfo.applicationInfo.uid ||
  60. !mService.mHeavyWeightProcess.processName.equals(aInfo.processName))) {
  61. int appCallingUid = callingUid;
  62. if (caller != null) {
  63. ProcessRecord callerApp = mService.getRecordForAppLocked(caller);
  64. if (callerApp != null) {
  65. appCallingUid = callerApp.info.uid;
  66. } else {
  67. Slog.w(TAG, "Unable to find app for caller " + caller
  68. + " (pid=" + callingPid + ") when starting: "
  69. + intent.toString());
  70. ActivityOptions.abort(options);
  71. return ActivityManager.START_PERMISSION_DENIED;
  72. }
  73. }
  74.  
  75. IIntentSender target = mService.getIntentSenderLocked(
  76. ActivityManager.INTENT_SENDER_ACTIVITY, "android",
  77. appCallingUid, userId, null, null, 0, new Intent[] { intent },
  78. new String[] { resolvedType }, PendingIntent.FLAG_CANCEL_CURRENT
  79. | PendingIntent.FLAG_ONE_SHOT, null);
  80.  
  81. Intent newIntent = new Intent();
  82. if (requestCode >= 0) {
  83. // Caller is requesting a result.
  84. newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_HAS_RESULT, true);
  85. }
  86. newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_INTENT,
  87. new IntentSender(target));
  88. if (mService.mHeavyWeightProcess.activities.size() > 0) {
  89. ActivityRecord hist = mService.mHeavyWeightProcess.activities.get(0);
  90. newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_CUR_APP,
  91. hist.packageName);
  92. newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_CUR_TASK,
  93. hist.task.taskId);
  94. }
  95. newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_NEW_APP,
  96. aInfo.packageName);
  97. newIntent.setFlags(intent.getFlags());
  98. newIntent.setClassName("android",
  99. HeavyWeightSwitcherActivity.class.getName());
  100. intent = newIntent;
  101. resolvedType = null;
  102. caller = null;
  103. callingUid = Binder.getCallingUid();
  104. callingPid = Binder.getCallingPid();
  105. componentSpecified = true;
  106. try {
  107. ResolveInfo rInfo =
  108. AppGlobals.getPackageManager().resolveIntent(
  109. intent, null,
  110. PackageManager.MATCH_DEFAULT_ONLY
  111. | ActivityManagerService.STOCK_PM_FLAGS, userId);
  112. aInfo = rInfo != null ? rInfo.activityInfo : null;
  113. aInfo = mService.getActivityInfoForUser(aInfo, userId);
  114. } catch (RemoteException e) {
  115. aInfo = null;
  116. }
  117. }
  118. }
  119. }
  120.  
  121. int res = startActivityLocked(caller, intent, resolvedType, aInfo,
  122. voiceSession, voiceInteractor, resultTo, resultWho,
  123. requestCode, callingPid, callingUid, callingPackage,
  124. realCallingPid, realCallingUid, startFlags, options, ignoreTargetSecurity,
  125. componentSpecified, null, container, inTask);
  126.  
  127. Binder.restoreCallingIdentity(origId);
  128.  
  129. if (stack.mConfigWillChange) {
  130. // If the caller also wants to switch to a new configuration,
  131. // do so now. This allows a clean switch, as we are waiting
  132. // for the current activity to pause (so we will not destroy
  133. // it), and have not yet started the next activity.
  134. mService.enforceCallingPermission(android.Manifest.permission.CHANGE_CONFIGURATION,
  135. "updateConfiguration()");
  136. stack.mConfigWillChange = false;
  137. if (DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
  138. "Updating to new configuration after starting activity.");
  139. mService.updateConfigurationLocked(config, null, false, false);
  140. }
  141.  
  142. if (outResult != null) {
  143. outResult.result = res;
  144. if (res == ActivityManager.START_SUCCESS) {
  145. mWaitingActivityLaunched.add(outResult);
  146. do {
  147. try {
  148. mService.wait();
  149. } catch (InterruptedException e) {
  150. }
  151. } while (!outResult.timeout && outResult.who == null);
  152. } else if (res == ActivityManager.START_TASK_TO_FRONT) {
  153. ActivityRecord r = stack.topRunningActivityLocked(null);
  154. if (r.nowVisible && r.state == RESUMED) {
  155. outResult.timeout = false;
  156. outResult.who = new ComponentName(r.info.packageName, r.info.name);
  157. outResult.totalTime = 0;
  158. outResult.thisTime = 0;
  159. } else {
  160. outResult.thisTime = SystemClock.uptimeMillis();
  161. mWaitingActivityVisible.add(outResult);
  162. do {
  163. try {
  164. mService.wait();
  165. } catch (InterruptedException e) {
  166. }
  167. } while (!outResult.timeout && outResult.who == null);
  168. }
  169. }
  170. }
  171.  
  172. return res;
  173. }
  174. }

首先判断是不是显示intent,

调用resolveActivity来进行查找。这里有一段注释:

  1. // Store the found target back into the intent, because now that
  2. // we have it we never want to do this again. For example, if the
  3. // user navigates back to this point in the history, we should
  4. // always restart the exact same activity.

为了确保在history里面的intent统一,只查找一次,获取ActivityInfo。

判断当前目标进程是不是重量级进程。

最后调用startActivityLocked来进一部启动。

如果onResult不为空,还需要将函数的结果写入这个变量中。在onResult的处理上,有可能会wait,所以这个函数叫startActivityMayWait。

StartActivityLocked

顾名思义,它是线程安全的。

  1. final int startActivityLocked(IApplicationThread caller,
  2. Intent intent, String resolvedType, ActivityInfo aInfo,
  3. IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
  4. IBinder resultTo, String resultWho, int requestCode,
  5. int callingPid, int callingUid, String callingPackage,
  6. int realCallingPid, int realCallingUid, int startFlags, Bundle options,
  7. boolean ignoreTargetSecurity, boolean componentSpecified, ActivityRecord[] outActivity,
  8. ActivityContainer container, TaskRecord inTask) {
  9. int err = ActivityManager.START_SUCCESS;
  10.  
  11. ProcessRecord callerApp = null;
  12. if (caller != null) {
  13. callerApp = mService.getRecordForAppLocked(caller);
  14. if (callerApp != null) {
  15. callingPid = callerApp.pid;
  16. callingUid = callerApp.info.uid;
  17. } else {
  18. Slog.w(TAG, "Unable to find app for caller " + caller
  19. + " (pid=" + callingPid + ") when starting: "
  20. + intent.toString());
  21. err = ActivityManager.START_PERMISSION_DENIED;
  22. }
  23. }
  24.  
  25. final int userId = aInfo != null ? UserHandle.getUserId(aInfo.applicationInfo.uid) : 0;
  26.  
  27. if (err == ActivityManager.START_SUCCESS) {
  28. Slog.i(TAG, "START u" + userId + " {" + intent.toShortString(true, true, true, false)
  29. + "} from uid " + callingUid
  30. + " on display " + (container == null ? (mFocusedStack == null ?
  31. Display.DEFAULT_DISPLAY : mFocusedStack.mDisplayId) :
  32. (container.mActivityDisplay == null ? Display.DEFAULT_DISPLAY :
  33. container.mActivityDisplay.mDisplayId)));
  34. }
  35.  
  36. ActivityRecord sourceRecord = null;
  37. ActivityRecord resultRecord = null;
  38. if (resultTo != null) {
  39. sourceRecord = isInAnyStackLocked(resultTo);
  40. if (DEBUG_RESULTS) Slog.v(TAG_RESULTS,
  41. "Will send result to " + resultTo + " " + sourceRecord);
  42. if (sourceRecord != null) {
  43. if (requestCode >= 0 && !sourceRecord.finishing) {
  44. resultRecord = sourceRecord;
  45. }
  46. }
  47. }
  48.  
  49. final int launchFlags = intent.getFlags();
  50.  
  51. if ((launchFlags & Intent.FLAG_ACTIVITY_FORWARD_RESULT) != 0 && sourceRecord != null) {
  52. // Transfer the result target from the source activity to the new
  53. // one being started, including any failures.
  54. if (requestCode >= 0) {
  55. ActivityOptions.abort(options);
  56. return ActivityManager.START_FORWARD_AND_REQUEST_CONFLICT;
  57. }
  58. resultRecord = sourceRecord.resultTo;
  59. if (resultRecord != null && !resultRecord.isInStackLocked()) {
  60. resultRecord = null;
  61. }
  62. resultWho = sourceRecord.resultWho;
  63. requestCode = sourceRecord.requestCode;
  64. sourceRecord.resultTo = null;
  65. if (resultRecord != null) {
  66. resultRecord.removeResultsLocked(sourceRecord, resultWho, requestCode);
  67. }
  68. if (sourceRecord.launchedFromUid == callingUid) {
  69. // The new activity is being launched from the same uid as the previous
  70. // activity in the flow, and asking to forward its result back to the
  71. // previous. In this case the activity is serving as a trampoline between
  72. // the two, so we also want to update its launchedFromPackage to be the
  73. // same as the previous activity. Note that this is safe, since we know
  74. // these two packages come from the same uid; the caller could just as
  75. // well have supplied that same package name itself. This specifially
  76. // deals with the case of an intent picker/chooser being launched in the app
  77. // flow to redirect to an activity picked by the user, where we want the final
  78. // activity to consider it to have been launched by the previous app activity.
  79. callingPackage = sourceRecord.launchedFromPackage;
  80. }
  81. }
  82.  
  83. if (err == ActivityManager.START_SUCCESS && intent.getComponent() == null) {
  84. // We couldn't find a class that can handle the given Intent.
  85. // That's the end of that!
  86. err = ActivityManager.START_INTENT_NOT_RESOLVED;
  87. }
  88.  
  89. if (err == ActivityManager.START_SUCCESS && aInfo == null) {
  90. // We couldn't find the specific class specified in the Intent.
  91. // Also the end of the line.
  92. err = ActivityManager.START_CLASS_NOT_FOUND;
  93. }
  94.  
  95. if (err == ActivityManager.START_SUCCESS
  96. && !isCurrentProfileLocked(userId)
  97. && (aInfo.flags & FLAG_SHOW_FOR_ALL_USERS) == 0) {
  98. // Trying to launch a background activity that doesn't show for all users.
  99. err = ActivityManager.START_NOT_CURRENT_USER_ACTIVITY;
  100. }
  101.  
  102. if (err == ActivityManager.START_SUCCESS && sourceRecord != null
  103. && sourceRecord.task.voiceSession != null) {
  104. // If this activity is being launched as part of a voice session, we need
  105. // to ensure that it is safe to do so. If the upcoming activity will also
  106. // be part of the voice session, we can only launch it if it has explicitly
  107. // said it supports the VOICE category, or it is a part of the calling app.
  108. if ((launchFlags & Intent.FLAG_ACTIVITY_NEW_TASK) == 0
  109. && sourceRecord.info.applicationInfo.uid != aInfo.applicationInfo.uid) {
  110. try {
  111. intent.addCategory(Intent.CATEGORY_VOICE);
  112. if (!AppGlobals.getPackageManager().activitySupportsIntent(
  113. intent.getComponent(), intent, resolvedType)) {
  114. Slog.w(TAG,
  115. "Activity being started in current voice task does not support voice: "
  116. + intent);
  117. err = ActivityManager.START_NOT_VOICE_COMPATIBLE;
  118. }
  119. } catch (RemoteException e) {
  120. Slog.w(TAG, "Failure checking voice capabilities", e);
  121. err = ActivityManager.START_NOT_VOICE_COMPATIBLE;
  122. }
  123. }
  124. }
  125.  
  126. if (err == ActivityManager.START_SUCCESS && voiceSession != null) {
  127. // If the caller is starting a new voice session, just make sure the target
  128. // is actually allowing it to run this way.
  129. try {
  130. if (!AppGlobals.getPackageManager().activitySupportsIntent(intent.getComponent(),
  131. intent, resolvedType)) {
  132. Slog.w(TAG,
  133. "Activity being started in new voice task does not support: "
  134. + intent);
  135. err = ActivityManager.START_NOT_VOICE_COMPATIBLE;
  136. }
  137. } catch (RemoteException e) {
  138. Slog.w(TAG, "Failure checking voice capabilities", e);
  139. err = ActivityManager.START_NOT_VOICE_COMPATIBLE;
  140. }
  141. }
  142.  
  143. final ActivityStack resultStack = resultRecord == null ? null : resultRecord.task.stack;
  144.  
  145. if (err != ActivityManager.START_SUCCESS) {
  146. if (resultRecord != null) {
  147. resultStack.sendActivityResultLocked(-1,
  148. resultRecord, resultWho, requestCode,
  149. Activity.RESULT_CANCELED, null);
  150. }
  151. ActivityOptions.abort(options);
  152. return err;
  153. }
  154.  
  155. boolean abort = false;
  156.  
  157. final int startAnyPerm = mService.checkPermission(
  158. START_ANY_ACTIVITY, callingPid, callingUid);
  159.  
  160. if (startAnyPerm != PERMISSION_GRANTED) {
  161. final int componentRestriction = getComponentRestrictionForCallingPackage(
  162. aInfo, callingPackage, callingPid, callingUid, ignoreTargetSecurity);
  163. final int actionRestriction = getActionRestrictionForCallingPackage(
  164. intent.getAction(), callingPackage, callingPid, callingUid);
  165.  
  166. if (componentRestriction == ACTIVITY_RESTRICTION_PERMISSION
  167. || actionRestriction == ACTIVITY_RESTRICTION_PERMISSION) {
  168. if (resultRecord != null) {
  169. resultStack.sendActivityResultLocked(-1,
  170. resultRecord, resultWho, requestCode,
  171. Activity.RESULT_CANCELED, null);
  172. }
  173. String msg;
  174. if (actionRestriction == ACTIVITY_RESTRICTION_PERMISSION) {
  175. msg = "Permission Denial: starting " + intent.toString()
  176. + " from " + callerApp + " (pid=" + callingPid
  177. + ", uid=" + callingUid + ")" + " with revoked permission "
  178. + ACTION_TO_RUNTIME_PERMISSION.get(intent.getAction());
  179. } else if (!aInfo.exported) {
  180. msg = "Permission Denial: starting " + intent.toString()
  181. + " from " + callerApp + " (pid=" + callingPid
  182. + ", uid=" + callingUid + ")"
  183. + " not exported from uid " + aInfo.applicationInfo.uid;
  184. } else {
  185. msg = "Permission Denial: starting " + intent.toString()
  186. + " from " + callerApp + " (pid=" + callingPid
  187. + ", uid=" + callingUid + ")"
  188. + " requires " + aInfo.permission;
  189. }
  190. Slog.w(TAG, msg);
  191. throw new SecurityException(msg);
  192. }
  193.  
  194. if (actionRestriction == ACTIVITY_RESTRICTION_APPOP) {
  195. String message = "Appop Denial: starting " + intent.toString()
  196. + " from " + callerApp + " (pid=" + callingPid
  197. + ", uid=" + callingUid + ")"
  198. + " requires " + AppOpsManager.permissionToOp(
  199. ACTION_TO_RUNTIME_PERMISSION.get(intent.getAction()));
  200. Slog.w(TAG, message);
  201. abort = true;
  202. } else if (componentRestriction == ACTIVITY_RESTRICTION_APPOP) {
  203. String message = "Appop Denial: starting " + intent.toString()
  204. + " from " + callerApp + " (pid=" + callingPid
  205. + ", uid=" + callingUid + ")"
  206. + " requires appop " + AppOpsManager.permissionToOp(aInfo.permission);
  207. Slog.w(TAG, message);
  208. abort = true;
  209. }
  210. }
  211.  
  212. abort |= !mService.mIntentFirewall.checkStartActivity(intent, callingUid,
  213. callingPid, resolvedType, aInfo.applicationInfo);
  214.  
  215. if (mService.mController != null) {
  216. try {
  217. // The Intent we give to the watcher has the extra data
  218. // stripped off, since it can contain private information.
  219. Intent watchIntent = intent.cloneFilter();
  220. abort |= !mService.mController.activityStarting(watchIntent,
  221. aInfo.applicationInfo.packageName);
  222. } catch (RemoteException e) {
  223. mService.mController = null;
  224. }
  225. }
  226.  
  227. if (abort) {
  228. if (resultRecord != null) {
  229. resultStack.sendActivityResultLocked(-1, resultRecord, resultWho, requestCode,
  230. Activity.RESULT_CANCELED, null);
  231. }
  232. // We pretend to the caller that it was really started, but
  233. // they will just get a cancel result.
  234. ActivityOptions.abort(options);
  235. return ActivityManager.START_SUCCESS;
  236. }
  237.  
  238. ActivityRecord r = new ActivityRecord(mService, callerApp, callingUid, callingPackage,
  239. intent, resolvedType, aInfo, mService.mConfiguration, resultRecord, resultWho,
  240. requestCode, componentSpecified, voiceSession != null, this, container, options);
  241. if (outActivity != null) {
  242. outActivity[0] = r;
  243. }
  244.  
  245. if (r.appTimeTracker == null && sourceRecord != null) {
  246. // If the caller didn't specify an explicit time tracker, we want to continue
  247. // tracking under any it has.
  248. r.appTimeTracker = sourceRecord.appTimeTracker;
  249. }
  250.  
  251. final ActivityStack stack = mFocusedStack;
  252. if (voiceSession == null && (stack.mResumedActivity == null
  253. || stack.mResumedActivity.info.applicationInfo.uid != callingUid)) {
  254. if (!mService.checkAppSwitchAllowedLocked(callingPid, callingUid,
  255. realCallingPid, realCallingUid, "Activity start")) {
  256. PendingActivityLaunch pal =
  257. new PendingActivityLaunch(r, sourceRecord, startFlags, stack);
  258. mPendingActivityLaunches.add(pal);
  259. ActivityOptions.abort(options);
  260. return ActivityManager.START_SWITCHES_CANCELED;
  261. }
  262. }
  263.  
  264. if (mService.mDidAppSwitch) {
  265. // This is the second allowed switch since we stopped switches,
  266. // so now just generally allow switches. Use case: user presses
  267. // home (switches disabled, switch to home, mDidAppSwitch now true);
  268. // user taps a home icon (coming from home so allowed, we hit here
  269. // and now allow anyone to switch again).
  270. mService.mAppSwitchesAllowedTime = 0;
  271. } else {
  272. mService.mDidAppSwitch = true;
  273. }
  274.  
  275. doPendingActivityLaunchesLocked(false);
  276.  
  277. err = startActivityUncheckedLocked(r, sourceRecord, voiceSession, voiceInteractor,
  278. startFlags, true, options, inTask);
  279.  
  280. if (err < 0) {
  281. // If someone asked to have the keyguard dismissed on the next
  282. // activity start, but we are not actually doing an activity
  283. // switch... just dismiss the keyguard now, because we
  284. // probably want to see whatever is behind it.
  285. notifyActivityDrawnForKeyguard();
  286. }
  287. return err;
  288. }

startActivityLocked

@Step1:首先判断调用者本身的进场是否存在?否则就返回

  1. err = ActivityManager.START_PERMISSION_DENIED;

这种情况是存在的:调用者被系统杀死或者异常退出等。

@Step2:

  1. Intent.FLAG_ACTIVITY_FORWARD_RESULT

考虑这个传递标志,如果Activity1 启动了Actvitiy2,然后Activity2 启动了Activity3,当Activity3 调用SetResult以后,AMS会把Activity3 的result

传递给Activity1.

这是怎么做的的呢?

在启动Activity3的时候,我们把caller改为Activity1.

  1. if ((launchFlags & Intent.FLAG_ACTIVITY_FORWARD_RESULT) != 0 && sourceRecord != null) {
  2. // Transfer the result target from the source activity to the new
  3. // one being started, including any failures.
  4. if (requestCode >= 0) {
  5. ActivityOptions.abort(options);
  6. return ActivityManager.START_FORWARD_AND_REQUEST_CONFLICT;
  7. }
  8. resultRecord = sourceRecord.resultTo;
  9. if (resultRecord != null && !resultRecord.isInStackLocked()) {
  10. resultRecord = null;
  11. }
  12. resultWho = sourceRecord.resultWho;
  13. requestCode = sourceRecord.requestCode;
  14. sourceRecord.resultTo = null;
  15. if (resultRecord != null) {
  16. resultRecord.removeResultsLocked(sourceRecord, resultWho, requestCode);
  17. }
  18. if (sourceRecord.launchedFromUid == callingUid) {
  19. // The new activity is being launched from the same uid as the previous
  20. // activity in the flow, and asking to forward its result back to the
  21. // previous. In this case the activity is serving as a trampoline between
  22. // the two, so we also want to update its launchedFromPackage to be the
  23. // same as the previous activity. Note that this is safe, since we know
  24. // these two packages come from the same uid; the caller could just as
  25. // well have supplied that same package name itself. This specifially
  26. // deals with the case of an intent picker/chooser being launched in the app
  27. // flow to redirect to an activity picked by the user, where we want the final
  28. // activity to consider it to have been launched by the previous app activity.
  29. callingPackage = sourceRecord.launchedFromPackage;
  30. }
  31. }

因为Activity2 已经把接收result的对象设置为Activity1,所以Activity2 不能startactivityforresult去启动activity3.

@Step3:

没有合适的Component或者ActivityInfo为空,代码直接报错返回。

@Step4:

检查权限。

@Step5:

生成ActivityRecord r,用来记录各项判断的结果。

做完这些了以后,调用startActivityUncheckedLocked

startActivityUncheckedLocked会调用ActivityStack里面的startActivityLocked

startActivityLocked

先判断Intent.FLAG_ACTIVITY_NEW_TASK, 并确定为newTask。

然后把newTask = true。传入startActivityLocked。

  1. if (!r.mLaunchTaskBehind && (taskForIdLocked(taskId) == null || newTask)) {
  2. // Last activity in task had been removed or ActivityManagerService is reusing task.
  3. // Insert or replace.
  4. // Might not even be in.
  5. insertTaskAtTop(rTask, r);
  6. mWindowManager.moveTaskToTop(taskId);
  7. }

查找task,并发task move to Top。

如果不是newTask,mTaskHistory里面找到它。

找到以后就干了几件事:

  1. task.addActivityToTop(r);
  2. r.putInHistory();
  3. mWindowManager.addAppToken(task.mActivities.indexOf(r), r.appToken,
  4. r.task.taskId, mStackId, r.info.screenOrientation, r.fullscreen,
  5. (r.info.flags & ActivityInfo.FLAG_SHOW_FOR_ALL_USERS) != 0,
  6. r.userId, r.info.configChanges, task.voiceSession != null,
  7. r.mLaunchTaskBehind);

把Activity r 放到栈的最上面。

ActivityTask

Task是google专门为Android创造的一个概念。

关于launchmode & taskAffinity 可以看我的一篇博客。是以前对于Task & Intent这块的一个PPT。Android四大组件之Intent

这里我们分析下,google是怎么来实现这个概念的。

关于task的概念在上面的函数startActivityUncheckedLocked里面有讲到:

  1.  
  1. int launchFlags = intent.getFlags();
    if ((launchFlags & Intent.FLAG_ACTIVITY_NEW_DOCUMENT) != 0 &&
    (launchSingleInstance || launchSingleTask)) {
    // We have a conflict between the Intent and the Activity manifest, manifest wins.
    Slog.i(TAG, "Ignoring FLAG_ACTIVITY_NEW_DOCUMENT, launchMode is " +
    "\"singleInstance\" or \"singleTask\"");
    launchFlags &=
    ~(Intent.FLAG_ACTIVITY_NEW_DOCUMENT | Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
    } else {
    switch (r.info.documentLaunchMode) {
    case ActivityInfo.DOCUMENT_LAUNCH_NONE:
    break;
    case ActivityInfo.DOCUMENT_LAUNCH_INTO_EXISTING:
    launchFlags |= Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
    break;
    case ActivityInfo.DOCUMENT_LAUNCH_ALWAYS:
    launchFlags |= Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
    break;
    case ActivityInfo.DOCUMENT_LAUNCH_NEVER:
    launchFlags &= ~Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
    break;
    }
    }

这段开始就讲到launchmode的。

首先就是,判断是不是launchSingleTask 或者launchSingleInstance 是的化,就忽略FLAG_ACTIVITY_NEW_DOCUMENT这个属性。

不是这2个launchmode,就去判断documentLaunchMode的属性。

  1. final boolean launchTaskBehind = r.mLaunchTaskBehind
  2. && !launchSingleTask && !launchSingleInstance
  3. && (launchFlags & Intent.FLAG_ACTIVITY_NEW_DOCUMENT) != 0;
  4.  
  5. if (r.resultTo != null && (launchFlags & Intent.FLAG_ACTIVITY_NEW_TASK) != 0
  6. && r.resultTo.task.stack != null) {
  7. // For whatever reason this activity is being launched into a new
  8. // task... yet the caller has requested a result back. Well, that
  9. // is pretty messed up, so instead immediately send back a cancel
  10. // and let the new task continue launched as normal without a
  11. // dependency on its originator.
  12. Slog.w(TAG, "Activity is launching as a new task, so cancelling activity result.");
  13. r.resultTo.task.stack.sendActivityResultLocked(-1,
  14. r.resultTo, r.resultWho, r.requestCode,
  15. Activity.RESULT_CANCELED, null);
  16. r.resultTo = null;
  17. }

判断activity是不是要重新开一个task,如果是的话,就把result去掉,应为在一个new task里面,必定是创建新的activity,所以也就么有传递result的必要。

  1. if ((launchFlags & Intent.FLAG_ACTIVITY_NEW_DOCUMENT) != 0 && r.resultTo == null) {
  2. launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
  3. }
  4.  
  5. // If we are actually going to launch in to a new task, there are some cases where
  6. // we further want to do multiple task.
  7. if ((launchFlags & Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
  8. if (launchTaskBehind
  9. || r.info.documentLaunchMode == ActivityInfo.DOCUMENT_LAUNCH_ALWAYS) {
  10. launchFlags |= Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
  11. }
  12. }

这个FLAG_ACTIVITY_MULTIPLE_TASK,是和Intent.FLAG_ACTIVITY_NEW_TASK一起使用,系统总是启动一个新task来启动activity

  1. ActivityRecord notTop =
  2. (launchFlags & Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP) != 0 ? r : null;
  1. if (sourceRecord == null && inTask != null && inTask.stack != null) {
  2. final Intent baseIntent = inTask.getBaseIntent();
  3. final ActivityRecord root = inTask.getRootActivity();
  4. if (baseIntent == null) {
  5. ActivityOptions.abort(options);
  6. throw new IllegalArgumentException("Launching into task without base intent: "
  7. + inTask);
  8. }
  9.  
  10. // If this task is empty, then we are adding the first activity -- it
  11. // determines the root, and must be launching as a NEW_TASK.
  12. if (launchSingleInstance || launchSingleTask) {
  13. if (!baseIntent.getComponent().equals(r.intent.getComponent())) {
  14. ActivityOptions.abort(options);
  15. throw new IllegalArgumentException("Trying to launch singleInstance/Task "
  16. + r + " into different task " + inTask);
  17. }
  18. if (root != null) {
  19. ActivityOptions.abort(options);
  20. throw new IllegalArgumentException("Caller with inTask " + inTask
  21. + " has root " + root + " but target is singleInstance/Task");
  22. }
  23. }
  24.  
  25. // If task is empty, then adopt the interesting intent launch flags in to the
  26. // activity being started.
  27. if (root == null) {
  28. final int flagsOfInterest = Intent.FLAG_ACTIVITY_NEW_TASK
  29. | Intent.FLAG_ACTIVITY_MULTIPLE_TASK | Intent.FLAG_ACTIVITY_NEW_DOCUMENT
  30. | Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS;
  31. launchFlags = (launchFlags&~flagsOfInterest)
  32. | (baseIntent.getFlags()&flagsOfInterest);
  33. intent.setFlags(launchFlags);
  34. inTask.setIntent(r);
  35. addingToTask = true;
  36.  
  37. // If the task is not empty and the caller is asking to start it as the root
  38. // of a new task, then we don't actually want to start this on the task. We
  39. // will bring the task to the front, and possibly give it a new intent.
  40. } else if ((launchFlags & Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
  41. addingToTask = false;
  42.  
  43. } else {
  44. addingToTask = true;
  45. }
  46.  
  47. reuseTask = inTask;
  48. } else {
  49. inTask = null;
  50. }
  51.  
  52. if (inTask == null) {
  53. if (sourceRecord == null) {
  54. // This activity is not being started from another... in this
  55. // case we -always- start a new task.
  56. if ((launchFlags & Intent.FLAG_ACTIVITY_NEW_TASK) == 0 && inTask == null) {
  57. Slog.w(TAG, "startActivity called from non-Activity context; forcing " +
  58. "Intent.FLAG_ACTIVITY_NEW_TASK for: " + intent);
  59. launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
  60. }
  61. } else if (sourceRecord.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
  62. // The original activity who is starting us is running as a single
  63. // instance... this new activity it is starting must go on its
  64. // own task.
  65. launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
  66. } else if (launchSingleInstance || launchSingleTask) {
  67. // The activity being started is a single instance... it always
  68. // gets launched into its own task.
  69. launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
  70. }
  71. }

我们来观察下singletask的套路:

  1. // If the caller is not coming from another activity, but has given us an
  2. // explicit task into which they would like us to launch the new activity,
  3. // then let's see about doing that.
  4. if (sourceRecord == null && inTask != null && inTask.stack != null) {
  5. final Intent baseIntent = inTask.getBaseIntent();
  6. final ActivityRecord root = inTask.getRootActivity();
  7. if (baseIntent == null) {
  8. ActivityOptions.abort(options);
  9. throw new IllegalArgumentException("Launching into task without base intent: "
  10. + inTask);
  11. }
  12.  
  13. // If this task is empty, then we are adding the first activity -- it
  14. // determines the root, and must be launching as a NEW_TASK.
  15. if (launchSingleInstance || launchSingleTask) {
  16. if (!baseIntent.getComponent().equals(r.intent.getComponent())) {
  17. ActivityOptions.abort(options);
  18. throw new IllegalArgumentException("Trying to launch singleInstance/Task "
  19. + r + " into different task " + inTask);
  20. }
  21. if (root != null) {
  22. ActivityOptions.abort(options);
  23. throw new IllegalArgumentException("Caller with inTask " + inTask
  24. + " has root " + root + " but target is singleInstance/Task");
  25. }
  26. }
  27.  
  28. // If task is empty, then adopt the interesting intent launch flags in to the
  29. // activity being started.
  30. if (root == null) {
  31. final int flagsOfInterest = Intent.FLAG_ACTIVITY_NEW_TASK
  32. | Intent.FLAG_ACTIVITY_MULTIPLE_TASK | Intent.FLAG_ACTIVITY_NEW_DOCUMENT
  33. | Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS;
  34. launchFlags = (launchFlags&~flagsOfInterest)
  35. | (baseIntent.getFlags()&flagsOfInterest);
  36. intent.setFlags(launchFlags);
  37. inTask.setIntent(r);
  38. addingToTask = true;
  39.  
  40. // If the task is not empty and the caller is asking to start it as the root
  41. // of a new task, then we don't actually want to start this on the task. We
  42. // will bring the task to the front, and possibly give it a new intent.
  43. } else if ((launchFlags & Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
  44. addingToTask = false;
  45.  
  46. } else {
  47. addingToTask = true;
  48. }
  49.  
  50. reuseTask = inTask;
  51. } else {
  52. inTask = null;
  53. }

如果task原来就是空的,哪就是添加root activity,所以要设置NEW_TASK这个标志位。

如果task已经存在,并且activity是root,我们不需要重新start newtask,只需要把这个task从新放到front就可以。

下面判断singletask & singleinstance标志位。如果sourceRecord是正在finishing的activity,我们不能在把它当做启动的源。

如果是singleinstance被拉到前台,由于它在task里面,“有且只有”它一个activity。找到它,并且movetofront。

然后判断topactivity,以及singletop launchmode。如果都满足,就把这个task启动起来,

android activity 管理器AMS----概述的更多相关文章

  1. Android布局管理器-使用LinearLayout实现简单的登录窗口布局

    场景 Android布局管理器-从实例入手学习相对布局管理器的使用: https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/1038389 ...

  2. Android布局管理器-使用TableLayout表格布局管理器实现简单的用户登录页面

    场景 Android布局管理器-使用FrameLayout帧布局管理器显示层叠的正方形以及前景照片: https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article ...

  3. Android布局管理器-使用FrameLayout帧布局管理器显示层叠的正方形以及前景照片

    场景 Android布局管理器-使用LinearLayout实现简单的登录窗口布局: https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details ...

  4. android开发4:Android布局管理器1(线性布局,相对布局RelativeLayout-案例)

    控件类概述 View 可视化控件的基类 属性名称 对应方法 描述 android:background setBackgroundResource(int) 设置背景 android:clickabl ...

  5. [置顶] Android布局管理器 - 详细解析布局实现

    布局管理器都是以ViewGroup为基类派生出来的; 使用布局管理器可以适配不同手机屏幕的分辨率,尺寸大小; 布局管理器之间的继承关系 : 在上面的UML图中可以看出, 绝对布局 帧布局 网格布局 相 ...

  6. Android 布局管理器

    为了更好地管理Android应用程序的用户界面组件,Android它提供了一个布局管理.通过使用布局管理,Android具有良好的平台无关的图形用户界面应用程序. 平时,推荐布局管理器来管理分布式组件 ...

  7. Android布局管理器(线性布局)

    线性布局有LinearLayout类来代表,Android的线性布局和Swing的Box有点相似(他们都会将容器里面的组件一个接一个的排列起来),LinearLayout中,使用android:ori ...

  8. Android布局管理器(表格布局)

    表格布局有TableLayout所代表,TableLayout继承了LinearLayout,因此他的本质依然是LinearLayout. 表格布局采用行.列的形式来进行管理,在使用的时候不需要声明多 ...

  9. Android 音频管理器AudioManager

    音频管理器AudioManager,通过它可以管理android系统的音量或直接让系统静音,依旧是通过调用getSystemService()方法获取音频管理器AudioManager对象,获取到该对 ...

随机推荐

  1. Android manifest之系统自带的permission

    Android manifest之系统自带的permission 本文描述Android系统自带的permission.点击查看:“关于permission的原始定义和说明”.点击查看:“Androi ...

  2. 【转载】利用shell脚本获取一个文件的绝对路径readlink

    转载自:http://os.chinaunix.net/a2007/1118/976/000000976787.shtml #! /bin/bash echo "Path to $(base ...

  3. [git]解决:git config --global push.default matching

    解决:git config --global push.default matching 这个警告的意思是:需要设置默认push的分支,所以设置好全局push的默认分支就好了.命令如下: 在有git目 ...

  4. Python内置模块(2)

    这一部分主要介绍sys.os.hashlib和re模块.其中的re模块介绍得非常详细,是本部分的重点! 均为python3.5.1环境. 一.sys模块 sys模块涉及的主要是与python解释器相关 ...

  5. iOS实现图像指定区域模糊

    在大多图像处理中,我们会应用到高斯模糊处理图像,通常用它来减少图像噪声以及降低细节层次.在此文中介绍了高斯模糊的实现和可选区域的模糊[美图秀秀-背景虚化] 高斯模糊的原理中,它是根据高斯曲线调节像素色 ...

  6. GPUImage滤镜之锐化

    应用锐化工具可以快速聚焦模糊边缘,提高图像中某一部位的清晰度或者焦距程度,使图像特定区域的色彩更加鲜明. 在应用锐化工具时,若勾选器选项栏中的“对所有图层取样”复选框,则可对所有可见图层中的图像进行锐 ...

  7. Sprint 3计划

    一.计划目标: 1.完成基本的首页面的信息查询功能 2.学生家教用户注册和登录,将信息存储到数据库 3.完成家教的资格评定设定和个人教学内容备份信息 二.燃尽图 三.项目具体工作细则 待明天工作会议分 ...

  8. WinForm输入网址打开源码

    无聊练习一下WinForm,输入网址,点击按钮就在浏览器打开网址. 源代码下载: http://hovertree.com/h/bjaf/cao15h74.htm

  9. gitbook使用

    第一步:安装node.js 官方网址:https://nodejs.org/en/ 运行以下命令,确认是否安装成功 node -v 第二步:安装gitbook npm install -g gitbo ...

  10. VS "15" 预览 5 中 VB 15 新增的功能

    VS "15" 预览 5 给 VB 带来了更新.这次的更新内容有3个: * 值元组 ValueTuple这个功能能把一组计算结果成组返回.为了使用这个功能,我们要安装 System ...