|
| 1 | +### AMS |
| 2 | + |
| 3 | +#### AMS是什么? |
| 4 | + |
| 5 | +它是Android提供的一个用于**管理Activity运行状态的系统进程**。我们的分析将按照这样的顺序进行: |
| 6 | + |
| 7 | +1. AMS的功能 |
| 8 | +2. ActivityStack |
| 9 | +3. ActivityTask |
| 10 | + |
| 11 | +#### AMS的功能概述 |
| 12 | + |
| 13 | +和大多数系统服务一样,它是寄生在`SystemServer`里面的。当系统服务启动的时候,创建一个线程来处理客户的请求,如果到现在还不能理解的话,你可以想象成类似于socket那种监听模型,就是有一个serverSocket线程来监听进入的socket,从而开始处理请求。我们可以来看一下AMS的启动过程: |
| 14 | + |
| 15 | +文件位置:`$CODEBASE/frameworks/base/services/java/com/android/server/SystemServer.java` |
| 16 | + |
| 17 | +```java |
| 18 | +public void run() { |
| 19 | + ... |
| 20 | + ActivityTaskManagerService atm = mSystemServiceManager.startService( |
| 21 | + ActivityTaskManagerService.Lifecycle.class).getService(); |
| 22 | + mActivityManagerService = ActivityManagerService.Lifecycle.startService( |
| 23 | + mSystemServiceManager, atm); // 注意这里启动了AMS |
| 24 | + mActivityManagerService.setSystemServiceManager(mSystemServiceManager); // 向ServiceManager注册 |
| 25 | + mActivityManagerService.setInstaller(installer); |
| 26 | + mWindowManagerGlobalLock = atm.getGlobalLock(); |
| 27 | + ... |
| 28 | +} |
| 29 | +``` |
| 30 | + |
| 31 | +注意到这里调用了AMS内部类`LifeCycle`的`startService`方法,它本质上会调用`SystemServiceManager`的`startService`方法。这个`LifeCycle`类是`SystemService`的一个子类, 然后在`SystemServiceManager`这个类里面的的`startService`方法里面,调用了native方法来构造了一个新的`AMS$LifeCycle`的实例(走了它的构造函数),在`LifeCycle`的构造函数里面调用了`AMS`的构造函数。 |
| 32 | + |
| 33 | +文件位置:`$CODEBASE/frameworks/base/services/core/java/com/android/server/SystemServiceManager.java` |
| 34 | + |
| 35 | +```java |
| 36 | +public <T extends SystemService> T startService(Class<T> serviceClass) { |
| 37 | + ... |
| 38 | + final T service; |
| 39 | + try { |
| 40 | + Constructor<T> constructor = serviceClass.getConstructor(Context.class); |
| 41 | + service = constructor.newInstance(mContext); // 这个newInstance方法是native方法 |
| 42 | + }catch(Exception ex) { |
| 43 | + ... |
| 44 | + } |
| 45 | + startService(service); // 走接下来的重载方法 |
| 46 | +} |
| 47 | +``` |
| 48 | + |
| 49 | +```java |
| 50 | +public void startService(@NonNull final SystemService service) { |
| 51 | + // Register it. |
| 52 | + mServices.add(service); // 注意这里,把AMS$LifeCycle加入了一个ArrayList里面进行管理。 |
| 53 | + // Start it. |
| 54 | + long time = SystemClock.elapsedRealtime(); |
| 55 | + try { |
| 56 | + service.onStart(); // 回调AMS$LifeCycle的onStart方法 |
| 57 | + } catch (RuntimeException ex) { |
| 58 | + throw new RuntimeException("Failed to start service " + service.getClass().getName() |
| 59 | + + ": onStart threw an exception", ex); |
| 60 | + } |
| 61 | + warnIfTooLong(SystemClock.elapsedRealtime() - time, service, "onStart"); |
| 62 | + } |
| 63 | +``` |
| 64 | + |
| 65 | +可以看到,它又跑回去调用`AMS$LifeCycle`的`onStart`方法了。在AMS$LifeCycle的`onStart`方法里面,走了AMS的`start`方法,这个方法里面主要就是创建和启动AMS线程,而且,这个线程是必须启动成功的,可以试着想想看,如果AMS拉不起来,你的设备就算其他服务启动了,也没啥意义。这个AMS也是一个Binder Server,因此,要查看它的功能,其实可以去翻看一下自动编译生成的`IActiivityManager.java`这个文件。这个文件有很多行,提供的功能也很多,我们这里大概看看几个重要的功能。 |
| 66 | + |
| 67 | +- 组件状态管理 |
| 68 | + |
| 69 | + 这里的组件指的是四大组件,状态管理包括关闭,开启一系列的相关操作。如启动Activity等。 |
| 70 | + |
| 71 | +- 组件状态查询 |
| 72 | + |
| 73 | + 这里就是指获取组件的信息,如getCallingActivity等。 |
| 74 | + |
| 75 | +- Task相关 |
| 76 | + |
| 77 | +- 其他杂项功能 |
| 78 | + |
| 79 | +#### ActivityStack |
| 80 | + |
| 81 | +文件位置:`$CODEBASE/frameworks/base/services/core/java/com/android/server/wm/ActivityStack.java` |
| 82 | + |
| 83 | +这个东西叫做活动栈,这个哥们是管理**系统所有活动的Activity状态**的数据结构。首先,大家都可能了解栈这个数据结构,首先问一个问题:如何用多个ArrayList来模拟栈的功能?留个心眼,我们继续往前走。 |
| 84 | + |
| 85 | +1. ActivityState |
| 86 | + |
| 87 | + 它描述了一个Activity可能经历的所有状态: |
| 88 | + |
| 89 | + ```java |
| 90 | + enum ActivityState { |
| 91 | + INITIALIZING, |
| 92 | + RESUMED, |
| 93 | + PAUSING, |
| 94 | + PAUSED, |
| 95 | + STOPPING, |
| 96 | + STOPPED, |
| 97 | + FINISHING, |
| 98 | + DESTROYING, |
| 99 | + DESTROYED, |
| 100 | + RESTARTING_PROCESS |
| 101 | + } |
| 102 | + ``` |
| 103 | + |
| 104 | + 一个Activity会经历上述的一些状态,这些状态对应的是Activity的生命周期方法,大家可以回去翻看API文档的那7个生命周期回调方法。 |
| 105 | + |
| 106 | +2. ArrayList |
| 107 | + |
| 108 | + ActivityStack管理了一系列的ArrayList, 这些ArrayList保存的东西都是ActivityRecord。ActivityRecord所对应的是,一个Activity就对应了一个AcitivtyRecord。 |
| 109 | + |
| 110 | + ```java |
| 111 | + ... |
| 112 | + /** |
| 113 | + * The back history of all previous (and possibly still |
| 114 | + * running) activities. It contains #TaskRecord objects. |
| 115 | + */ |
| 116 | + private final ArrayList<TaskRecord> mTaskHistory = new ArrayList<>(); |
| 117 | + |
| 118 | + /** |
| 119 | + * List of running activities, sorted by recent usage. |
| 120 | + * The first entry in the list is the least recently used. |
| 121 | + * It contains HistoryRecord objects. |
| 122 | + */ |
| 123 | + private final ArrayList<ActivityRecord> mLRUActivities = new ArrayList<>(); |
| 124 | + |
| 125 | + /** |
| 126 | + * When we are in the process of pausing an activity, before starting the |
| 127 | + * next one, this variable holds the activity that is currently being paused. |
| 128 | + */ |
| 129 | + ActivityRecord mPausingActivity = null; |
| 130 | + |
| 131 | + /** |
| 132 | + * This is the last activity that we put into the paused state. This is |
| 133 | + * used to determine if we need to do an activity transition while sleeping, |
| 134 | + * when we normally hold the top activity paused. |
| 135 | + */ |
| 136 | + ActivityRecord mLastPausedActivity = null; |
| 137 | + |
| 138 | + /** |
| 139 | + * Activities that specify No History must be removed once the user navigates away from them. |
| 140 | + * If the device goes to sleep with such an activity in the paused state then we save it here |
| 141 | + * and finish it later if another activity replaces it on wakeup. |
| 142 | + */ |
| 143 | + ActivityRecord mLastNoHistoryActivity = null; |
| 144 | + |
| 145 | + /** |
| 146 | + * Current activity that is resumed, or null if there is none. |
| 147 | + */ |
| 148 | + ActivityRecord mResumedActivity = null; |
| 149 | + ... |
| 150 | + ``` |
| 151 | + |
| 152 | + 这里我们可以做个实验: |
| 153 | + |
| 154 | + 在`ActivityStack`的`startActivityLocked()`方法中添加Log打印: |
| 155 | + |
| 156 | + ```JAVA |
| 157 | + void startActivityLocked(ActivityRecord r, ActivityRecord focusedTopActivity, |
| 158 | + boolean newTask, boolean keepCurTransition, ActivityOptions options) { |
| 159 | + if (r != null && focusedTopActivity != null) { |
| 160 | + |
| 161 | + String msg = "we enter startActivityLocked() method!\nActivityRecord = " + r.toString() + ": ActivityRecord(focusedTopActivity) = " + focusedTopActivity |
| 162 | + .toString(); |
| 163 | + Log.d("TESTTAG", msg); |
| 164 | + } |
| 165 | + ... |
| 166 | + } |
| 167 | + ``` |
| 168 | + |
| 169 | + 编译代码然后烧录镜像(或者冷启动一下你的模拟机) |
| 170 | + |
| 171 | + ```shell |
| 172 | + $> source build/envsetup.sh |
| 173 | + $> lunch xx # xx 为你的combo |
| 174 | + $> make -j48 && emulator -no-snapshot-load |
| 175 | + ``` |
| 176 | + |
| 177 | + |
| 178 | + |
| 179 | + 然后我们编写一个Activity,里面有个按钮,自己启动自己: |
| 180 | + |
| 181 | + ```java |
| 182 | + import android.app.Activity; |
| 183 | + import android.content.Intent; |
| 184 | + import android.os.Bundle; |
| 185 | + import android.widget.Button; |
| 186 | + |
| 187 | + public class MainActivity extends Activity { |
| 188 | + |
| 189 | + private Button btn; |
| 190 | + |
| 191 | + |
| 192 | + @Override |
| 193 | + protected void onCreate(Bundle savedInstanceState) { |
| 194 | + super.onCreate(savedInstanceState); |
| 195 | + |
| 196 | + setContentView(R.layout.activity_main); |
| 197 | + btn = findViewById(R.id.button); |
| 198 | + btn.setOnClickListener(v -> startActivity(new Intent(getApplicationContext(), MainActivity.class))); |
| 199 | + } |
| 200 | + } |
| 201 | + ``` |
| 202 | + |
| 203 | + 我们所做的操作是,点击这个按钮两次,那么按道理说,此时应该有三个MainActivity的实例(因为启动模式是standard)。我们来看Logcat给出的输出: |
| 204 | + |
| 205 | + ```java |
| 206 | + 2020-12-29 19:00:26.281 1641-2595/system_process D/TESTTAG: we enter startActivityLocked() method! |
| 207 | + ActivityRecord = ActivityRecord{eb0b8f1 u0 com.example.myapplication1/.MainActivity t28}: ActivityRecord(focusedTopActivity) = ActivityRecord{9e899cf u0 com.android.launcher3/.Launcher t27} // 这里的t27的意思是task ID 是27 |
| 208 | + // 从这条记录我们可以发现,我们从Launcher启动我们的Activity的时候,创建了一个新的ActivityRecord记录。 |
| 209 | + // 注意观察这里的ActivityRecord的hash值的变化。 |
| 210 | + // 注意观察TaskID的变化。 |
| 211 | + |
| 212 | + 2020-12-29 19:00:36.342 1641-2137/system_process D/TESTTAG: we enter startActivityLocked() method! |
| 213 | + ActivityRecord = ActivityRecord{4118cff u0 com.example.myapplication1/.MainActivity t28}: ActivityRecord(focusedTopActivity) = ActivityRecord{eb0b8f1 u0 com.example.myapplication1/.MainActivity t28} |
| 214 | + // 因为启动模式是standard,因此继续启动新的MainActivity实例。 |
| 215 | + |
| 216 | + 2020-12-29 19:01:03.114 1641-1979/system_process D/TESTTAG: we enter startActivityLocked() method! |
| 217 | + ActivityRecord = ActivityRecord{6236e55 u0 com.example.myapplication1/.MainActivity t28}: ActivityRecord(focusedTopActivity) = ActivityRecord{4118cff u0 com.example.myapplication1/.MainActivity t28} |
| 218 | + |
| 219 | + |
| 220 | + ``` |
| 221 | + |
| 222 | + 我们可以发现,系统如实的记录了我们的启动顺序: |
| 223 | + |
| 224 | + `LauncherActivity(9e899cf)` -> `MainActivity1(eb0b8f1)` |
| 225 | + |
| 226 | + `MainActivity1(eb0b8f1)` -> `MainActivity2(4118cff)` |
| 227 | + |
| 228 | + `MainActivity2(4118cff)`-> `MainActivity3(6236e55)` |
| 229 | + |
| 230 | +所以,我们可以得出这么一个结论: |
| 231 | + |
| 232 | +**AMS是通过ActivityStack来管理,记录系统中的Activity和其他组件的状态,同时提供查询功能的一个系统服务** |
| 233 | + |
| 234 | + |
| 235 | + |
| 236 | +我是一个调皮的分割线 |
| 237 | + |
| 238 | +--- |
| 239 | + |
| 240 | +那么,我们走到这里,下一个要回答的问题是: |
| 241 | + |
| 242 | +**一个Activity是怎么被启动的?** |
| 243 | + |
| 244 | +我们从最直观的调用入口开始: |
| 245 | + |
| 246 | +`startActivity(Intent intent)` |
| 247 | + |
| 248 | +这个东西大家都不会陌生,它用于启动一个目标Activity---具体启动哪个,由AMS通过对系统所有的程序进行`intent`的匹配得到,不局限于当前package的范围。因此,`startActivity`很可能启动的不是本应用的组件。 |
| 249 | + |
| 250 | +我们可以通过大致跟踪一下startActivity的调用流程: |
| 251 | + |
| 252 | +1. 在Activity里面调用`startActivity`方法, 最终会调用到 `execStartActivity@Instrumentation` |
| 253 | + |
| 254 | +2. 深入到`Instrumentation.java`来看,它通过RPC调用了`ActivityTaskManagerService`的`startActivity`方法。 |
| 255 | + |
| 256 | +3. 注意,现在我们移步进入了`ActivityTaskManagerService`,它调用了这样的一个方法: |
| 257 | + |
| 258 | + `startActivityAsUser()`,来看看这两个方法的签名区别: |
| 259 | + |
| 260 | + ```java |
| 261 | + public final int startActivity(IApplicationThread caller, String callingPackage,Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,int startFlags, ProfilerInfo profilerInfo, Bundle bOptions) |
| 262 | + |
| 263 | + public int startActivityAsUser(IApplicationThread caller, String callingPackage,Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, int userId) |
| 264 | + ``` |
| 265 | + |
| 266 | + 可以看到,他们两个方法唯一区别多了一个参数 `userId`。这个通过`UserHandle.getCallingUserId()`方法拿到。顺口提一句,这里也是通过Binder拿的用户UID。这个UID在后面用来判定权限相关的工作。 |
| 267 | + |
| 268 | +4. 来到这个方法: |
| 269 | + |
| 270 | + ```java |
| 271 | + int startActivityAsUser(IApplicationThread caller, String callingPackage, |
| 272 | + Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode, |
| 273 | + int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, int userId, |
| 274 | + boolean validateIncomingUser) { |
| 275 | + enforceNotIsolatedCaller("startActivityAsUser"); |
| 276 | + |
| 277 | + userId = getActivityStartController().checkTargetUser(userId, validateIncomingUser, |
| 278 | + Binder.getCallingPid(), Binder.getCallingUid(), "startActivityAsUser"); |
| 279 | + |
| 280 | + // TODO: Switch to user app stacks here. |
| 281 | + return getActivityStartController().obtainStarter(intent, "startActivityAsUser") // 链式调用,这个地方获取一个叫ActivityStarter的一个对象,来启动Acitivty。注意看这里的参数。 |
| 282 | + .setCaller(caller) |
| 283 | + .setCallingPackage(callingPackage) |
| 284 | + .setResolvedType(resolvedType) |
| 285 | + .setResultTo(resultTo) |
| 286 | + .setResultWho(resultWho) |
| 287 | + .setRequestCode(requestCode) |
| 288 | + .setStartFlags(startFlags) |
| 289 | + .setProfilerInfo(profilerInfo) |
| 290 | + .setActivityOptions(bOptions) |
| 291 | + .setMayWait(userId) |
| 292 | + .execute(); |
| 293 | + |
| 294 | + } |
| 295 | + ``` |
| 296 | + |
| 297 | + |
| 298 | + |
0 commit comments