Apk逆向

反编译:

1
java -jar apktool_2.6.0.jar d out_old.apk -o out_old

在打包回去:

1
java -jar apktool_2.6.0.jar b out_old -o out.apk

对齐:

1
SDK\Sdk\build-tools\29.0.2\zipalign -f 4 out.apk out_zipalign.apk

签名:

1
SDK\Sdk\build-tools\29.0.2\apksigner sign -ks key.jks -ks-key-alias key -ks-pass pass:123456 -key-pass pass:123456789 -out app_singed.apk out_zipalign.apk

查看dex文件代码:

1
2
3
java -jar apktool_2.7.0.jar d lll.apk

sh d2j-dex2jar.sh /home/yuAndroid/fanbianyi/llal/classes.dex

kotlin的in和out

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Animal {}
class Dog extends Animal {}
class Cat extends Animal {}
class Result<T>{
private T data;
public Result() {
}
public Result(T data) {
this.data = data;
}

public T getData() {
return data;
}

public void setData(T data) {
this.data = data;
}
}

1. 不可型变

1
2
Result<Dog> dogResult = new Result<>();
Result<Animal> animalResult = dogResult; // 编译报错

虽然DogAnimal的孩子,但是Java泛型是不可变型变,所以Result不可以赋值给Result.如果可以的话会发生什么情况呢?

1
2
3
4
5
6
7
8
Result<Dog> dogResult = new Result<>();
Result<Animal> animalResult = dogResult;//编译报错
//现在假设上面这个不会报错
//现在塞一个数据
animalResult.setData(new Animal());
//那请问这里getData获取的是 animal 还是 dog 这变成了不确定的. 如果上面的编译不报错,那我们还可以setData任何一个 animal的子类, 那你在get的时候就根本 不知道这个取出来的对象到底是哪一个.
Dog dog = animalResult.getData();

所以为了安全考虑,java设置了限制符? extends X   ? super X

**2. 型变性通配符—- ? extends **

1
2
3
4
5
Result<Dog> dogResult = new Result<>(new Dog());
Result<? extends Animal> animalResult = dogResult;// 编译通过
// animalResult = new Result<Object>(new Object);// 编译报错
// animalResult.setData(new Animal());//编译报错
Animal animal = animalResult.getData();

? extends X指定了 泛型的上界,也就是 X 或者是 X 的子类. 所以我们在取数据的时候是可以进行的,因为数据一定是 X 类型的, 但是不允许你插入数据.比如你看 ArrayList.addAll方法:

1
2
3
4
5
class ArrayList{
// 指定了泛型上界是 E, 然后addAll只做取的操作,所以这是使用 ? extends 是非常好的
public boolean addAll(Collection<? extends E> c) {
}
}

3.逆变型通配符 —- ? super

1
2
3
4
5
6
Result<Dog> dogResult = new Result<>(new Dog());
Result<Object> objResult = new Result<>(new Object());
Result<? super Animal> animalResult = objResult;
//animalResult = dogResult;// 编译报错
animalResult.setData(new Dog());
Animal animal = animalResult.getData();

? super X规定了泛型下界X,必须是X或者是X的父类才行.

总结:

**? extends X ** :

  1. 限定了上界,泛型类型必须是 X 或者 X 的子类
  2. 此通配符只可以读,不可以写
  3. 对应的是Kotlinout

? super X:

  1. 限定了下界,泛型的类型是 X 或者是 的父类
  2. 此通配符可以写 但是读没法确定类型.
  3. 对应的是kotlinin

特别好的博客:地址

为什么ViewModel对象不会因为屏幕旋转而重建

Android会在Activity执行销毁的生命周期之前,会将所有你想保存的数据放到NonConfigurationInstances对象里面, 然后会将ActivityNonConfigurationInstance对象放进ActivityClientRecord里面, 我们知道启动一个Activity会为他创建一个ActivityClientRecord对象,这个对象只会有一个,你现在是旋转屏幕,这个不涉及ActivityClientRecord对象的重新创建,所以你保存在里面的旧数据就会一直在.

1
2
3
4
5
6
7
8
9
class ActivityThread{
void performDestroyActivity(ActivityClientRecord r, boolean finishing,
int configChanges, boolean getNonConfigInstance, String reason){
//将Activity创建的NonConfigurationInstance赋值给ActivityClentRecord
r.lastNonConfigurationInstances = r.activity.retainNonConfigurationInstances();

}

}

现在看下Activity.retainNonConfigurationInstances()方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Activity{

NonConfigurationInstances retainNonConfigurationInstances() {

// 需要保存自己的数据,就需要重写 onRetainNonConfigurationInstance 方法
Object activity = onRetainNonConfigurationInstance();


HashMap<String, Object> children = onRetainNonConfigurationChildInstances();


NonConfigurationInstances nci = new NonConfigurationInstances();
nci.activity = activity;
nci.children = children;
nci.fragments = fragments;

return nci;
}
}

然后ComponentActivity重写了onRetainNonConfigurationInstance方法:

1
2
3
4
5
6
7
8
9
10
11
12
class ComponentActivity{
public final Object onRetainNonConfigurationInstance() {
Object custom = onRetainCustomNonConfigurationInstance();

ViewModelStore viewModelStore = mViewModelStore;

NonConfigurationInstances nci = new NonConfigurationInstances();
nci.custom = custom;
nci.viewModelStore = viewModelStore;
return nci;
}
}

可以看到在ComponentActivity重写了onRetainNonConfigurationInstance方法,将ActivityViewModeStore放进了NonConfigurationInstance里面.

上面是关于viewModelStore存的操作,下面看下是怎么取的.


取的阶段发生在Activityattach阶段:

1
2
3
4
5
6
7
8
9
10
class ActivityThread{
private Activity performLaunchActivity(ActivityClientRecord r,Intent customInstent){
//可以看到在attach阶段,将activityClientRecord.lastNonConfigurationInstance值传了进去
activity.attach(appContext, this, getInstrumentation(), r.token,
r.ident, app, r.intent, r.activityInfo, title, r.parent,
r.embeddedID, r.lastNonConfigurationInstances, config,
r.referrer, r.voiceInteractor, window, r.configCallback,
r.assistToken, r.shareableActivityToken))
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
class Activity{
final void attach(Context context, ActivityThread aThread,
Instrumentation instr, IBinder token, int ident,
Application application, Intent intent, ActivityInfo info,
CharSequence title, Activity parent, String id,
NonConfigurationInstances lastNonConfigurationInstances,
Configuration config, String referrer, IVoiceInteractor voiceInteractor,
Window window, ActivityConfigCallback activityConfigCallback, IBinder assistToken,
IBinder shareableActivityToken){
//对nonConfigurationInstance进行了赋值
mLastNonConfigurationInstances = lastNonConfigurationInstances;
}
}

Android:窗口管理

5.jpg

Android的窗口管理如图所示!

这里我也不说Window了,我直接就说PhoneWindow了,因为不管是Activity还是Dialog他们的Window都是PhoneWindow.

每个PhoneWindow都对应一个WindowManagerImpl,通过Window.setWindowManager()方法进行WindowManagerImpl的创建. 拿Acvitity的创建来说,在ActivityThreadperformLaunchActivity时候,调用了Activity.attach方法,来看下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Activity{
final void attach(Context context, ActivityThread aThread,
Instrumentation instr, IBinder token, int ident,
Application application, Intent intent, ActivityInfo info,
CharSequence title, Activity parent, String id,
NonConfigurationInstances lastNonConfigurationInstances,
Configuration config, String referrer, IVoiceInteractor voiceInteractor,
Window window, ActivityConfigCallback activityConfigCallback, IBinder assistToken,
IBinder shareableActivityToken) {
// 第一行
mWindow = new PhoneWindow(this, window, activityConfigCallback);
// 第二行
mWindow.setWindowManager(
(WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
mToken, mComponent.flattenToString(),
(info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
if (mParent != null) {
mWindow.setContainer(mParent.getWindow());
}
mWindowManager = mWindow.getWindowManager();

}
}

在 第一行 创建了 PhoneWindow对象,然后在 第二行 给当前 PhoneWindow 设置了 WindowManagerImpl 对象, 看下创建过程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class Activity{
void attach(){
mWindow.setWindowManager(
(WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
mToken, mComponent.flattenToString(),
(info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
}
}
class PhoneWindow{
public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
boolean hardwareAccelerated) {
mAppToken = appToken;
mAppName = appName;
mHardwareAccelerated = hardwareAccelerated;
if (wm == null) {
wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
}
//创建了属于自己的 windowManagerImpl,并且将自己传入进 WindowManagerImpl
mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
}
}

class WindowManagerImpl{
// 标记1
public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
return new WindowManagerImpl(mContext, parentWindow, mWindowContextToken);
}
}

在调用setWindowManager的时候一定要传入一个WindowManager对象,在我看来,比如Activity的启动,这个…..然后比如Dialog,这个WindowManager就是当前触发Dialog显示的那个Activity的WindowManagerImpl.

这里需要着重看下 标记1 的地方, parentwindow是什么,他为什么如此重要,就像他的名字一样 父窗口, 每个WindowManagerImpl里面都保留了当前的PhoneWindow对象,目的是在addView的时候,就可以知道新添加的Window需要指定到哪个 windowToken 上.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class WindowManagerGlobal{
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow, int userId) {

final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
// 给新的window设置 windowToken 值, 值的由来就是用过 parentWindow获取到的
parentWindow.adjustLayoutParamsForSubWindow(wparams);
root = new ViewRootImpl(view.getContext(), display);
view.setLayoutParams(wparams);
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
root.setView(view, wparams, panelParentView, userId);
}
}
}

现在我们来看下一个Window是怎么被添加进去和管理的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
class ViewRootImpl{
void setView(){
// 当调用到来这里就切换到了系统进程,进入到了 windowManagerService
int res = mWindowSession.addToDisplayAsUser(mWindow,mWindowAttributes,getHostVisibility(),mDisplay.getDisplayId(),userId,..)
}
}

class Session{
//这时候就来到了系统进程
public int addToDisplayAsUser(IWindow window, WindowManager.LayoutParams attrs,
int viewVisibility, int displayId, int userId, InsetsState requestedVisibility,
InputChannel outInputChannel, InsetsState outInsetsState,
InsetsSourceControl[] outActiveControls) {
// 这个mService 就是 WindowManagerService
return mService.addWindow(this, window, attrs, viewVisibility, displayId, userId,
requestedVisibility, outInputChannel, outInsetsState, outActiveControls);
}
}

class WindowManagerService{
public int addWindow(Session session, IWindow client, LayoutParams attrs, int viewVisibility,
int displayId, int requestUserId, InsetsState requestedVisibility,
InputChannel outInputChannel, InsetsState outInsetsState,
InsetsSourceControl[] outActiveControls) {

// 可以看到这里获取到了 DisplayContent对象
final DisplayContent displayContent = getDisplayContentOrCreate(displayId, attrs.token);

final WindowState win = new WindowState(this, session, client, token, parentWindow,
appOp[0], attrs, viewVisibility, session.mUid, userId,
session.mCanAddInternalSystemWindow);
//看到没,通过windToken管理着一组windowState
win.mToken.addWindow(win);
}

private DisplayContent getDisplayContentOrCreate(int displayId, IBinder token) {
if (token != null) {
//可以看到去获取对应的 WindowToken
//这时候这里的mRoot对象是 RootWindowContainer.java
// 标记1
final WindowToken wToken = mRoot.getWindowToken(token);
if (wToken != null) {
return wToken.getDisplayContent();
}
}

return mRoot.getDisplayContentOrCreate(displayId);
}
}

class RootWindowContainer{
WindowToken getWindowToken(IBinder binder) {
//标记2
for (int i = mChildren.size() - 1; i >= 0; --i) {
final DisplayContent dc = mChildren.get(i);
final WindowToken wtoken = dc.getWindowToken(binder);
if (wtoken != null) {
return wtoken;
}
}
return null;
}
}

现在看下标记1 标记2这时候的内存分布情况:

图1
图2
图3

看了这三张图你就能深刻理解到最上面图的意思了. 首先 RootWindowContainer管理着所有的DisplayContent,一个DisplayContent代表一个屏幕.然后一个屏幕管理着所有的WindowToken,一个WindowToken管理这一组窗口,比如,两个Activity,他们的WindowToken一定是不一样的,如果是在Activity里面启动了一个Dialog,那么这两个窗口的WindowToken就是一样的.

token报错分析

为什么要再读Activity的启动呢,因为在启动弹窗的时候,出现了一个这个报错:

1
2
3
4
Process: com.librty.crashsolution, PID: 7037 android.view.WindowManager$BadTokenException:   Unable to add window -- token null is not valid; is your activity running? 
at android.view.ViewRootImpl.setView(ViewRootImpl.java:1189)
at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:400)
at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:138)

里面个讲到一个window.token值。由于这个值的不正确导致的。
首先需要找到抛出异常的位置:在ViewRootImpl的setView阶段:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class ViewRootImpl{
public void setView(View view,WindowManager.LayoutParams attrs,View panelParentView,int userId){
...
int res = mWindowSession.addToDisplayAsUser(mWindow, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(), userId,
mInsetsController.getRequestedVisibilities(), inputChannel, mTempInsets,
mTempControls);

if (res < WindowManagerGlobal.ADD_OKAY) {
switch (res) {
case WindowManagerGlobal.ADD_BAD_APP_TOKEN:
case WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN:
throw new WindowManager.BadTokenException(
"Unable to add window -- token " + attrs.token
+ " is not valid; is your activity running?");
case WindowManagerGlobal.ADD_NOT_APP_TOKEN:
throw new WindowManager.BadTokenException(
"Unable to add window -- token " + attrs.token
+ " is not for an application");

}
}
...
}
}

可以看到res的值是通过mWindowSession.addToDisplayerAsUser方法获取到的。
获取过程如下:
第一步:搞明白mWindowSession对象的获取过程

1
2
3
4
5
6
7
8
9
10
class ViewRootImpl{
final IWindowSession mWindowSession;
public ViewRootImpl(Context context, Display display) {
this(context, display, WindowManagerGlobal.getWindowSession(),
false);
}
public ViewRootImpl(Context cotext,Display display,IWindowSession session,boolean useSfChoreographer){
mWindowSession = sesssion;
}
}

可以看到mWindowSession是通过WindowManagerGlobal.getWindowSession()方法获取的。可以看到这里是通过Binder通信去获取WindowManagerService服务。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public static IWindowSession getWindowSession() {
synchronized (WindowManagerGlobal.class) {
if (sWindowSession == null) {
try {
@UnsupportedAppUsage InputMethodManager.ensureDefaultInstanceForDefaultDisplayIfNecessary();
IWindowManager windowManager = getWindowManagerService();
sWindowSession = windowManager.openSession(
new IWindowSessionCallback.Stub() {
@Override
public void onAnimatorScaleChanged(float scale) {
ValueAnimator.setDurationScale(scale);
}
});
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
return sWindowSession;
}
}

class WindowManagerService extends IWindowManager.Stub
implements Watchdog.Monitor, WindowManagerPolicy.WindowManagerFuncs{
public IWindowSession openSession(IWindowSessionCallback callback) {
return new Session(this, callback);
}
}

可以看到iWindowSession其实就是Session对象。

第二步调用addToDisplayAsUser方法

1
2
3
4
5
6
7
8
9
10
class Session{
final WindowManagerService mService;
public int addToDisplayAsUser(IWindow window, WindowManager.LayoutParams attrs,
int viewVisibility, int displayId, int userId, InsetsVisibilities requestedVisibilities,
InputChannel outInputChannel, InsetsState outInsetsState,
InsetsSourceControl[] outActiveControls) {
return mService.addWindow(this, window, attrs, viewVisibility, displayId, userId,
requestedVisibilities, outInputChannel, outInsetsState, outActiveControls);
}
}

可以看到又调用了WindowManagerService的addWindow方法:

1
2
3
4
5
6
7
8
class WindowManagerService{
public int addWindow(Session session, IWindow client, LayoutParams attrs, int viewVisibility,
int displayId, int requestUserId, InsetsVisibilities requestedVisibilities,
InputChannel outInputChannel, InsetsState outInsetsState,
InsetsSourceControl[] outActiveControls) {
}
}

这里就引出了很重要的一个服务WindowManagerService,它管理了当前系统中的所有window窗口,他主管了设备产生的touch事件,因为windowmanagerservice知道当前哪个window最适合处理事件。

来看下WindowmanagerService里面所有的窗口是如何管理的:

5.jpg

现在继续看addWindow整个过程干了什么:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
class WindowManagerService{
public static final int FIRST_SUB_WINDOW = 1000;
public static final int FIRST_SYSTEM_WINDOW = 2000;
public int addWindow(Session session, IWindow client, LayoutParams attrs, int viewVisibility,
int displayId, int requestUserId, InsetsVisibilities requestedVisibilities,
InputChannel outInputChannel, InsetsState outInsetsState,
InsetsSourceControl[] outActiveControls) {
Arrays.fill(outActiveControls, null);
int[] appOp = new int[1];
final boolean isRoundedCornerOverlay = (attrs.privateFlags
& PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY) != 0;
// 第一步 : 进行权限检查
int res = mPolicy.checkAddPermission(attrs.type, isRoundedCornerOverlay, attrs.packageName,
appOp);
if (res != ADD_OKAY) {
return res;
}
// 第二步:获取 Dispay
final DisplayContent displayContent = getDisplayContentOrCreate(displayId, attrs.token);
if (displayContent == null) {
ProtoLog.w(WM_ERROR, "Attempted to add window to a display that does "
+ "not exist: %d. Aborting.", displayId);
return WindowManagerGlobal.ADD_INVALID_DISPLAY;
}
// 获取窗口类型
final int type = attrs.type;
//第三步: 1000 2000
if (type >= FIRST_SUB_WINDOW && type <= LAST_SUB_WINDOW) {
parentWindow = windowForClientLocked(null, attrs.token, false);
if (parentWindow == null) {
ProtoLog.w(WM_ERROR, "Attempted to add window with token that is not a window: "
+ "%s. Aborting.", attrs.token);
return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;
}
if (parentWindow.mAttrs.type >= FIRST_SUB_WINDOW
&& parentWindow.mAttrs.type <= LAST_SUB_WINDOW) {
ProtoLog.w(WM_ERROR, "Attempted to add window with token that is a sub-window: "
+ "%s. Aborting.", attrs.token);
return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;
}

// 避免窗口的重复添加
if (mWindowMap.containsKey(client.asBinder())) {
ProtoLog.w(WM_ERROR, "Window %s is already added", client);
return WindowManagerGlobal.ADD_DUPLICATE_ADD;
}
if (type >= FIRST_SUB_WINDOW && type <= LAST_SUB_WINDOW) {
parentWindow = windowForClientLocked(null, attrs.token, false);
if (parentWindow == null) {
ProtoLog.w(WM_ERROR, "Attempted to add window with token that is not a window: "
+ "%s. Aborting.", attrs.token);
return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;
}
if (parentWindow.mAttrs.type >= FIRST_SUB_WINDOW
&& parentWindow.mAttrs.type <= LAST_SUB_WINDOW) {
ProtoLog.w(WM_ERROR, "Attempted to add window with token that is a sub-window: "
+ "%s. Aborting.", attrs.token);
return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;
}
}

}
}
}

第一步: 进行权限检查。看你这个类型的window能够进行创建。
第二步: 获取Display。Display就是屏幕的意思,手机的可显示区域会通过displayId进行标记,每个displayId对应一个display,然后display会被保存在DisplayContent里面。到了这里就要讲到上面的关于token产生的报错问题了。

dd.jpg

在图上可以看到通过token值从DisplayContent里面获取WindowToken值。windowToken是一组窗口的编号,比如我们在activity里面弹出了一个dialog,那么这个时候dialog的windowtoken和activity的token的值就是一样的,因为dialog是activity的子窗口,他们属于一组窗口,所以windowtoken的值是一样的。

1669356626268.jpg这个token是activity的。
1669356765007.jpg这个token是activity上弹的dialog弹窗。

从我们的实验结果能发现,属于一组窗口的时候他们的windowToken的值是一样的。什么叫一组窗口,就是存在父子关系的,比如activity和他的dialog。两个activity间就不是一组窗口,因此他们的windowtoken也不一样。

lQDPJxbn5v0At5XNAtvNBRuwT8cA5lJ9efEDfQPYo8A2AA_1307_731.jpg
在WindowManagerservice.addWindow()里面会为窗口每次去创建WindowState对象,每个windowSate都保存了windowToken值,所以间接的,通过windowToken管理了一组windowState,是怎么实现的呢?
WindowToken继承自WindowContainer,然后在WindowContainer里面有一个WindowList数组,这个数组就存下了相同token下的所有窗口。

captrue.png
lQDPJxbqOR7xeyDNAbrNAxqwqPAI_ypMD0EDgNFGKkDOAA_794_442.jpg

captrue.png

captrue.png
可以看到一组相同的windowToken会通过windowList保存所有的windowState。
5.jpg
经过这么一通分析,上面这张图已经搞懂了WindowToken和WidowState的关系了。但是我现在还有一个疑问这个WindowToken的值是从哪来的?继续分析…..

lQDPJxbqOv9bUtnNAnDNA16ws9drjwKi7p8DgNRZXMBwAA_862_624.jpg

captrue.png
WindowToken是通过IBinder值从DisplayContent获取到的。然后IBinder值是通过保存在WindowManager.LayoutParams里的token值来获取的。那现在就一步步往上追,这个WindowManager.LayoutParams.token的赋值时机:

captrue.png

① 调用者Session:

captrue.png

② 调用者ViewRootImpm.setView

captrue.png

③ 调用者WindowManagerGlobal.addView:

captrue.png

④ 调用者WindowManagerImpl.addView:

captrue.png

⑤ 调用者ActivityThread.handleResumeActivity:

captrue.png

现在分析每步干了什么:
⑤的位置: 首先获取到一个空的LayoutParams,因为在我们每次创建Window对象的时候,都会立马创建一个空的WindManager.LayoutParams对象。然后在⑤位置给我们的window设置了type值WindowManager.LayoutParams.TYPE_BASE_APPLICATION这个常量值是1也就代表这是一个应用窗口。

④的位置: 没干任何事情。直接就掉用了WindowManagerGlobal.addView方法。

③的位置: 对token进行赋值

captrue.png
这里的parentWindow并不为空,因为在Activity的attach阶段,创建了我们的windowManagetImpl对象

103.png

captrue.png

现在看回adjustLayoutParamsForSubWindow:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
class Window{

void adjustLayoutParamsForSubWindow(WindowManager.LayoutParams wp) {
CharSequence curTitle = wp.getTitle();
// 不符合这里1000 - 1999
if (wp.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&
wp.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
if (wp.token == null) {
View decor = peekDecorView();
if (decor != null) {
wp.token = decor.getWindowToken();
}
}
if (curTitle == null || curTitle.length() == 0) {
final StringBuilder title = new StringBuilder(32);
if (wp.type == WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA) {
title.append("Media");
} else if (wp.type == WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY) {
title.append("MediaOvr");
} else if (wp.type == WindowManager.LayoutParams.TYPE_APPLICATION_PANEL) {
title.append("Panel");
} else if (wp.type == WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL) {
title.append("SubPanel");
} else if (wp.type == WindowManager.LayoutParams.TYPE_APPLICATION_ABOVE_SUB_PANEL) {
title.append("AboveSubPanel");
} else if (wp.type == WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG) {
title.append("AtchDlg");
} else {
title.append(wp.type);
}
if (mAppName != null) {
title.append(":").append(mAppName);
}
wp.setTitle(title);
}
} //这也不符合2000 - 2999
else if (wp.type >= WindowManager.LayoutParams.FIRST_SYSTEM_WINDOW &&
wp.type <= WindowManager.LayoutParams.LAST_SYSTEM_WINDOW) {
if (curTitle == null || curTitle.length() == 0) {
final StringBuilder title = new StringBuilder(32);
title.append("Sys").append(wp.type);
if (mAppName != null) {
title.append(":").append(mAppName);
}
wp.setTitle(title);
}
} //进到这里因为应用级别的type 1 - 999
else {
if (wp.token == null) {
//对于我们的activity这个mContainer是null
wp.token = mContainer == null ? mAppToken : mContainer.mAppToken;
}
if ((curTitle == null || curTitle.length() == 0)
&& mAppName != null) {
wp.setTitle(mAppName);
}
}
if (wp.packageName == null) {
wp.packageName = mContext.getPackageName();
}
if (mHardwareAccelerated ||
(mWindowAttributes.flags & FLAG_HARDWARE_ACCELERATED) != 0) {
wp.flags |= FLAG_HARDWARE_ACCELERATED;
}
}
}

到这里就结束了,LayoutParams该设置的值都设置结束了。

标记1的地方可以看到WindowManagerService有一个成员变量RootWindowContainer。我们都知道WindowManagerService可以管理所有窗口,这就是答案所在。在RootWindowContainer里面有一个数组专门用来保存创建的窗口。

1
2
3
class RootWindowContainer{
protected final WindowList<E> mChildren = new WindowList<E>();
}

标记2的地方,通过token值从RootWindowContainer里面获取对应存在的DisplayContent。在我们创建一个activity的时候,我可以给你讲,这个token的值是null。token的赋值操作可以通过获取Widow的WindowManager.LayoutParams直接进行赋值的:

1
2
3
4
5
6
val p = WindowManager.LayoutParams()
p.flags = WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
p.type = WindowManager.LayoutParams.TYPE_APPLICATION_PANEL
p.token = applicationContext.getActivityToken()
var mWindowManager = getSystemService(WINDOW_SERVICE) as WindowManager
mWindowManager.addView(view, p)

标记3:通过token获取不到值的时候,就通过displayId来获取,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class RootWindowContainer{
DisplayContent getDisplayContentOrCreate(int displayId) {
// 从WindowList里面取,看能不能取到
DisplayContent displayContent = getDisplayContent(displayId);
if (displayContent != null) {
return displayContent;
}
if (mDisplayManager == null) {
return null;
}
//先获取display
final Display display = mDisplayManager.getDisplay(displayId);
if (display == null) {
return null;
}
//创建一个和display对应的displaycontent对象。
displayContent = new DisplayContent(display, this);
//保存到windowlist里面
addChild(displayContent, POSITION_BOTTOM);
return displayContent;
}
}

其实看到这里我又不懂了,这个displayId是什么,Display又是什么?我没搞懂 (2022年11月24日,今天我搞懂了。)

在android里面display就是一个可以展示的屏幕,displayid就是标记是哪个屏幕。surfaceflinger就是通过displayid将合成的图片分发给指定的屏幕。在我们手机只有一个屏幕的时候这个dispalyid就是0.如果有两个屏幕分别为0 和 1(就是这么个意思,也可能不是0 1。,反正就是两个不同的数字)。有一个屏幕就有一个display对象,有两个屏幕就有两个display对象。

5.jpg

第三步:
当type大于1000,小于1999,说明这个窗口是一个子窗口。如果是子窗口就会在第三步去找到他的父窗口也就是代码里面的parentWindow,activity是应用窗口所以在第三步不会去找他的父窗口。Dialog是一个子窗,这个时候会去找他的父窗口,现在来研究下是怎么找的。

1
2
3
class WindowManagerService{
parentWindow = windowForClientLocked(null, attrs.token, false);
}

可以看到在第二步的时候去判断了窗口类型。

看到这里总结一下:在创建ViewRootImpl对象的时候,会去获取WindowManagerService服务去创建Session对象。然后调用session对象的addToDisplayAsUser方法,他的返回值结果用来表示窗口添加结果情况情况。


现在来总结下全部过程,在ActivityThread的performLaunchActivity阶段会去创建Activity对象,并调用Activity的attach方法。在attach里面会去创建PhoneWindow对象。然后给当前创建的PhoneWindow创建对应的WindowManagerImpl对象,通过方法window.setWindowManager()。创建WindowManagerImpl对象。PhoneWindow持有WindowManagerImpl的引用。WindowManagerImpl也持有window的引用。在这个阶段还涉及了一个很重的参数token:IBinder。每个Activity都有一个对应的token:IBinder。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Activtiy{
public void attach(){
mWindow = PhoneWindow();
mWindow.setWindowManager((WindowManager)context.getSystemService(Context.WINDOW_SERVICE),mToken,mComponent.flattenToString(),(info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED)!=0);
}
}
class Window{
public void setWindowManager(WindowManager wm,IBinder appToken,String appName,boolean hardwareAccelerated){
mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
}
}
class WindowManagerImpl{
public WindowManagerImpl crateLocalWindowManager(Window parentWindow){
return new WindowManagerImpl(mContext,parentWindow,mWindowContextToken);
}
}

然后在ActivityThread的handleResumeActivity阶段,设置Activity对应的window属性type为应用窗口属性1,然后调用WindowManagerImpl的addView方法。在addView方法又再次调用了WindowManagerGlobal.addView方法。这里需要注意,WindowManagerGlobal对象是一个单例对象,一个进程只会存在一份,然后windowmanagerImpl对象是在每次创建窗口都会新建一个。
接着来到了windowmanagetGlobal.addView方法。在这个时候会进行很重要的一次操作,就是给window设置token:IBinder值。token值用来标记了哪些窗口是一组的,比如activity和他的dialog,他们的token值就是一样的,两个activity的token值就是不一样的。
设置完window的token值,接着创建了ViewRootImpl对象。然后调用了ViewRootImpl的setView方法。这里面干了很重的操作,不仅调用了requestlayout触发了整个view树的measure layout draw操作,还干了件大事就是完成了window窗口的添加。调用方法windowsession.addToDisplayAsUser方法。这里就涉及到了跨进程通信。windowSession是通过跨进程的方式从windowManagerService里面获取到的session对象。接着看addToDisplayToUser方法。这个时候就来到了system_server进程。
这个时候就来到了一个新的领域,android窗口添加过程了。在session调用addDisplayToUser的时候会调用到WindowManagerService.addWindow方法。然后通过displayid或者token值获取对应的displayContent。如果用户手机有多个屏幕的话就会有多个Displaycontent对象。如果只有一个屏幕。那只会有一个DisplayContent对象。通过token从DisplayContent里面获取windowtoken值。然后为窗口创建windowstate对象。通过windowtoken维护着一组windowstate。然后displaycontent维护着一组windowtoken。至此结束。


RootWindowContainer

RootWindowContainer是应用程序窗口管理器的顶层容器,用于维护所有显示内容的层级关系.

在Android系统里面有个 窗口容器 的概念,一个窗口容器里面可以包含很多个子窗口.

由DisplayArea作为根节点,每个DisplayArea下面可以包含多个DisplayContent,每个DisplayContent下面可以包含多个ActivityStack,每个ActivityStack下面可以包含多个AppWindowToken,每个AppWindowToken下面可以包含多个Window,如Dialog、Toast等。通过WindowContainer,可以方便地对整个窗口树进行管理和控制。

比如这里我们看类TaskDisplayArea他表示了一个窗口区域,这个窗口区域下可以有很多子窗口容器:

lQLPJw2x8y0DEcLNAu3NBLWwogv5_CBerEYEMBkFVkA0AA_1205_749.png
比如上面的:
5 : 我们的桌面窗口容器
6 : 我们运行的app的窗口容器(com.librity.glidestudy)

从源码的角度解析:Android启动模式和FLAG和taskAffinity的关系

在 Android 中存在 Task 的概念,启动的 Activity 都会被放入对应的 Task 中.那什么时候会去创建新的 Task 呢?

可能你会想到使用 Intent.FLAG_ACTIVITY_NEW_TASK,标志位来做,但是实际情况是通过他一个标志位并不行.

上代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class ActivityStarter{
int startActivityInner(ActivityRecord r, ActivityRecord sourceRecord,
IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
int startFlags, boolean doResume, ActivityOptions options, Task inTask,
boolean restrictedBgActivity, NeededUriGrants intentGrants){
// 这个方法很关键,通过标志位判断情况
Task reusedTask = getReusableTask();
final Task targetTask = reusedTask != null ? reusedTask : computeTargetTask();
final boolean newTask = targetTask == null;
// 判断是否需要新建新的task
if (newTask) {
final Task taskToAffiliate = (mLaunchTaskBehind && mSourceRecord != null)
? mSourceRecord.getTask() : null;
setNewTask(taskToAffiliate);
} else if (mAddingToTask) {
//加入到已经存在的task
addOrReparentStartingActivity(targetTask, "adding to task");
}
}
}

这里要搞懂 reusedTask 和 targetTask 的关系.

  • reusedTask表示可复用的task,指的是系统已经存在的task,可以用来放目标activity.
  • targetTask表示目标activity最终会被放入的task.

reusedTask不会为空的情况是,是当你启动目标Activity的时候,对启动有要求的情况,比如启动模式为 singleInstance ,这个时候就要求去创建新的task,所以系统会先去找可复用的task.

这里来看getReusableTask() 很关键

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
private Task getReusableTask() {
// 关键点1:
boolean putIntoExistingTask = ((mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0 &&
(mLaunchFlags & FLAG_ACTIVITY_MULTIPLE_TASK) == 0)
|| isLaunchModeOneOf(LAUNCH_SINGLE_INSTANCE, LAUNCH_SINGLE_TASK);
putIntoExistingTask &= mInTask == null && mStartActivity.resultTo == null;
ActivityRecord intentActivity = null;
if (putIntoExistingTask) {
// 关键点2:
if (LAUNCH_SINGLE_INSTANCE == mLaunchMode) {
intentActivity = mRootWindowContainer.findActivity(mIntent, mStartActivity.info,
mStartActivity.isActivityTypeHome());
}else {
// 关键点3:
intentActivity =
mRootWindowContainer.findTask(mStartActivity, mPreferredTaskDisplayArea);
}
}
return intentActivity != null ? intentActivity.getTask() : null;
}

关键点1:会判断有没有给Flag设置FLAG_ACTIVITY_NEW_TASK 或者启动模式 是不是SINGLE_INSTANVE || SINGLE_TASK,如果符合上述条件就会将 putIntoExistingTask设置为true.

关键点2: 这里会进行两层判断:

  1. 启动模式是 LAUNCH_INSTANCE
  2. 启动模式不是 LAUNCH_INSTANCE

上面两者查找可复用task的方式是不用的.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
class RootWindowContainer{
ActivityRecord findTask(ActivityRecord r, TaskDisplayArea preferredTaskDisplayArea) {
return findTask(r.getActivityType(), r.taskAffinity, r.intent, r.info,
preferredTaskDisplayArea);
}
ActivityRecord findTask(int activityType, String taskAffinity, Intent intent, ActivityInfo info,
TaskDisplayArea preferredTaskDisplayArea) {
ProtoLog.d(WM_DEBUG_TASKS, "Looking for task of type=%s, taskAffinity=%s, intent=%s"
+ ", info=%s, preferredTDA=%s", activityType, taskAffinity, intent, info,
preferredTaskDisplayArea);
mTmpFindTaskResult.init(activityType, taskAffinity, intent, info);

// Looking up task on preferred display area first
ActivityRecord candidateActivity = null;
if (preferredTaskDisplayArea != null) {
mTmpFindTaskResult.process(preferredTaskDisplayArea);
if (mTmpFindTaskResult.mIdealRecord != null) {
return mTmpFindTaskResult.mIdealRecord;
} else if (mTmpFindTaskResult.mCandidateRecord != null) {
candidateActivity = mTmpFindTaskResult.mCandidateRecord;
}
}

final ActivityRecord idealMatchActivity = getItemFromTaskDisplayAreas(taskDisplayArea -> {
if (taskDisplayArea == preferredTaskDisplayArea) {
return null;
}

mTmpFindTaskResult.process(taskDisplayArea);
if (mTmpFindTaskResult.mIdealRecord != null) {
return mTmpFindTaskResult.mIdealRecord;
}
return null;
});
if (idealMatchActivity != null) {
return idealMatchActivity;
}

if (WM_DEBUG_TASKS.isEnabled() && candidateActivity == null) {
ProtoLog.d(WM_DEBUG_TASKS, "No task found");
}
return candidateActivity;
}
}

通过上面代码我们可以总结发现:
是否需要查找可复用的条件是你有需要创建task的动作的时候.
比如 启动模式是 SINGLE_INSTANCE 或者是 SINGLE_TASK. 或者 FLAG设置了 FLAG_ACTIVITY_NEW_TASK.查找可复用的 Task的方式根据启动方式的不同有区别:

  • 当启动模式是: SINGLE_INSTANCE.
  • 当启动模式是: STANDARD SINGLETOP SINGLETASK.

情况1: 是 SINGLE_INSTANCE:

1
2
3
4
if (LAUNCH_SINGLE_INSTANCE == mLaunchMode) {
intentActivity = mRootWindowContainer.findActivity(mIntent, mStartActivity.info,
mStartActivity.isActivityTypeHome());
}

过程就是遍历所有的 TASK 找到和要启动的目标一样的 ActivityRecord

情况2: 是 另外三种:

1
intentActivity =  mRootWindowContainer.findTask(mStartActivity, mPreferredTaskDisplayArea);

这个是去寻找最合适的栈,意思就是如果能找和他一样的实例,那就是最好的栈,如果找不到,就找那个最适合让他入栈的task,什么叫最好的栈,比如 A 跳转 B ,用户按返回键是返回A,而不是跳到别的地方去了,保证用户操作的流畅性.

现在分析下情况2的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
ActivityRecord findTask(int activityType, String taskAffinity, Intent intent, ActivityInfo info,
TaskDisplayArea preferredTaskDisplayArea) {
ProtoLog.d(WM_DEBUG_TASKS, "Looking for task of type=%s, taskAffinity=%s, intent=%s"
+ ", info=%s, preferredTDA=%s", activityType, taskAffinity, intent, info,
preferredTaskDisplayArea);
mTmpFindTaskResult.init(activityType, taskAffinity, intent, info);

// Looking up task on preferred display area first
ActivityRecord candidateActivity = null;
if (preferredTaskDisplayArea != null) {
mTmpFindTaskResult.process(preferredTaskDisplayArea);
if (mTmpFindTaskResult.mIdealRecord != null) {
return mTmpFindTaskResult.mIdealRecord; // 这里返回的话,说明在已经存在的 task里面找到了相同的实例,也就是他是最合适的栈
} else if (mTmpFindTaskResult.mCandidateRecord != null) {
// mCandidateRecord意思就是返回适合的栈
candidateActivity = mTmpFindTaskResult.mCandidateRecord;
}
}

final ActivityRecord idealMatchActivity = getItemFromTaskDisplayAreas(taskDisplayArea -> {
if (taskDisplayArea == preferredTaskDisplayArea) {
return null;
}

mTmpFindTaskResult.process(taskDisplayArea);
if (mTmpFindTaskResult.mIdealRecord != null) {
return mTmpFindTaskResult.mIdealRecord;
}
return null;
});
if (idealMatchActivity != null) {
return idealMatchActivity;
}

if (WM_DEBUG_TASKS.isEnabled() && candidateActivity == null) {
ProtoLog.d(WM_DEBUG_TASKS, "No task found");
}
return candidateActivity;
}

整个查找过程就是调用 FindTaskResult.process方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
class FindTaskResult{
void process(WindowContainer parent) {
cls = mIntent.getComponent();
if (mInfo.targetActivity != null) {
cls = new ComponentName(mInfo.packageName, mInfo.targetActivity);
}
userId = UserHandle.getUserId(mInfo.applicationInfo.uid);
isDocument = mIntent != null & mIntent.isDocument();
// If documentData is non-null then it must match the existing task data.
documentData = isDocument ? mIntent.getData() : null;

ProtoLog.d(WM_DEBUG_TASKS, "Looking for task of %s in %s", mInfo,
parent);
parent.forAllLeafTasks(this);
}
}
class WindowContainer{
boolean forAllLeafTasks(Function<Task, Boolean> callback) {
for (int i = mChildren.size() - 1; i >= 0; --i) {
if (mChildren.get(i).forAllLeafTasks(callback)) {
return true;
}
}
return false;
}
}
class Task{
boolean forAllLeafTasks(Function<Task, Boolean> callback) {
boolean isLeafTask = true;
for (int i = mChildren.size() - 1; i >= 0; --i) {
// 关键代码 标记1
final Task child = mChildren.get(i).asTask();
if (child != null) {
isLeafTask = false;
if (child.forAllLeafTasks(callback)) {
return true;
}
}
}
if (isLeafTask) {
return callback.apply(this);
}
return false;
}
}

看这里的代码我们需要有个概念: Android的窗口机制.
所有的窗口父类都是WindowContainer,窗口的分类有

  • RootWindowContainer
  • DisplayContent
  • TaskDisplayArea
  • Task
  • ActivityRecord
  • WindowToken
  • WindowState

他们的关系是:
RootWindowContainer管理所有的DisplayContent,DisplayContent里面放着所有的TaskDisplayArea, TaskDisplayArea放着所有的Task, Task里面放着ActivtityRecord.

现在看回上面代码:

1
2
3
4
5
6
7
8
9
10
class WindowContainer{
boolean forAllLeafTasks(Function<Task, Boolean> callback) {
for (int i = mChildren.size() - 1; i >= 0; --i) {
if (mChildren.get(i).forAllLeafTasks(callback)) {
return true;
}
}
return false;
}
}

过程就是遍历所有的孩子,直至找到叶子节点,也就是ActivityRecord,为什么这里我要说叶子节点是ActivityRecord呢,因为所有窗口类,只有 Task 重写了 findAllLeafTasks 方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Task{
boolean forAllLeafTasks(Function<Task, Boolean> callback) {
boolean isLeafTask = true;
for (int i = mChildren.size() - 1; i >= 0; --i) {
// 关键代码 标记1
final Task child = mChildren.get(i).asTask();
if (child != null) {
isLeafTask = false;
if (child.forAllLeafTasks(callback)) {
return true;
}
}
}
if (isLeafTask) {
return callback.apply(this);
}
return false;
}
}

然后看 标记1 的地方,调用了 asTask() 方法, asTask() 方法只有 Task类进行重载,返回了自己,其他的类比如ActivityRecord没有重载asTask,默认返回null.所以,当asTask返回的不是null,就说明还没到叶子节点,如果是叶子节点就返回null,然后执行callback.apply方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
boolean forAllLeafTasks(Function<Task, Boolean> callback) {
boolean isLeafTask = true;
for (int i = mChildren.size() - 1; i >= 0; --i) {
// 只有task类的 asTask返回的不是null
final Task child = mChildren.get(i).asTask();
if (child != null) {
isLeafTask = false;
if (child.forAllLeafTasks(callback)) {
return true;
}
}
}
//是叶子了
if (isLeafTask) {
//apply方法传入的参数就是 task当前 task对象
return callback.apply(this);
}
return false;
}

现在回到 RootWindowContainer看看 callback.apply方法是如何实现的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
class RootWindowContainer{
public Boolean apply(Task task) {
if (!ConfigurationContainer.isCompatibleActivityType(mActivityType,
task.getActivityType())) {
ProtoLog.d(WM_DEBUG_TASKS, "Skipping task: (mismatch activity/task) %s", task);
return false;
}

if (task.voiceSession != null) {
// We never match voice sessions; those always run independently.
ProtoLog.d(WM_DEBUG_TASKS, "Skipping %s: voice session", task);
return false;
}
if (task.mUserId != userId) {
// Looking for a different task.
ProtoLog.d(WM_DEBUG_TASKS, "Skipping %s: different user", task);
return false;
}

// Overlays should not be considered as the task's logical top activity.
final ActivityRecord r = task.getTopNonFinishingActivity(false /* includeOverlays */);
if (r == null || r.finishing || r.mUserId != userId
|| r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
ProtoLog.d(WM_DEBUG_TASKS, "Skipping %s: mismatch root %s", task, r);
return false;
}
if (!ConfigurationContainer.isCompatibleActivityType(r.getActivityType(),
mActivityType)) {
ProtoLog.d(WM_DEBUG_TASKS, "Skipping %s: mismatch activity type", task);
return false;
}

final Intent taskIntent = task.intent;
final Intent affinityIntent = task.affinityIntent;
final boolean taskIsDocument;
final Uri taskDocumentData;
if (taskIntent != null && taskIntent.isDocument()) {
taskIsDocument = true;
taskDocumentData = taskIntent.getData();
} else if (affinityIntent != null && affinityIntent.isDocument()) {
taskIsDocument = true;
taskDocumentData = affinityIntent.getData();
} else {
taskIsDocument = false;
taskDocumentData = null;
}

ProtoLog.d(WM_DEBUG_TASKS, "Comparing existing cls=%s /aff=%s to new cls=%s /aff=%s",
r.getTask().rootAffinity, mIntent.getComponent().flattenToShortString(),
mInfo.taskAffinity, (task.realActivity != null
? task.realActivity.flattenToShortString() : ""));
// TODO Refactor to remove duplications. Check if logic can be simplified.
if (task.realActivity != null && task.realActivity.compareTo(cls) == 0
&& Objects.equals(documentData, taskDocumentData)) {
mIdealRecord = r;
return true;
} else if (affinityIntent != null && affinityIntent.getComponent() != null
&& affinityIntent.getComponent().compareTo(cls) == 0 &&
Objects.equals(documentData, taskDocumentData)) {
mIdealRecord = r;
return true;
} else if (!isDocument && !taskIsDocument
&& mIdealRecord == null && mCandidateRecord == null
&& task.rootAffinity != null) {
if (task.rootAffinity.equals(mTaskAffinity)) {
mCandidateRecord = r;
}
} else {
ProtoLog.d(WM_DEBUG_TASKS, "Not a match: %s", task);
}

return false;
}
}

我们来看下他是怎么判断什么是 最合适的 ActivityRecord, 什么是 比较合适的 ActivityRecord.

还有一篇比较好的博客推荐

在我们手机上你按Home键查看任务列表的时候,他是根据 Task 来展示展示的,比如你的MainActivity在task1 TwoActiivty在task2,那么你就会看到下面这个情况:

ActivityTaskManager启动Activity再读

Activity创建startActivity请求,然后这个请求就被交给了ActivityTaskManagerService

这里还需要有一个概念,在Android系统每个用户有一个独立的uid.这个值从0还是.然后每个安装的包有一个独立的Uid.这个值在安装后就会固定下来,不会变.每个app启动会生成一个动态的进程id也就是pid.然后 Android系统每个用户下安装的app数量是有上限的100000个.比如用户id是0,那么安装包的uid就是从 0 - 99999. 如果用户uid=1 ,那么安装的app的uid是从 100000 - 999999.

我们来看看在Activity调用startActivityActivityTaskManagerService都传递了哪些值过去: (这里我直接带你看InstrumentationexecuStartActivity方法)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Instrumentation{
public ActivityResult execStartActivity(Context who , IBinder contextThread , IBinder token , Activity target , Intent intent , int requestCode , Bundel options){
int result = ActivityTaskManager.getService().startActivity(contextThread,
who.getOpPackageName(), who.getAttributionTag(), intent,
intent.resolveTypeIfNeeded(who.getContentResolver()), token,
target != null ? target.mEmbeddedID : null, requestCode, 0, null, options);
}
}
class ActivityTaskManagerService{
public final int startActivity(IApplicationThread caller, String callingPackage,
String callingFeatureId, Intent intent, String resolvedType, IBinder resultTo,
String resultWho, int requestCode, int startFlags, ProfilerInfo profilerInfo,
Bundle bOptions) {
return
}
}

参数解释:

Instrumentation.execStartActivity:

  1. IBinder contextThread : 是ActivityThread 的 ApplicationThread对象, ApplicationThread连接了App进程和系统服务间进行的通信.也就是跨进程通信.
  2. IBinder token : 是当前调用startActivity的Activity的IBinder值. 每个Activity都一个叫mToken的成员变量,他保存的就是IBinder
  3. Activity target : 是当前调用startActivity的Activity对象
  4. Intent intent : 这个就是我们在startActivity时候新建的Intent对象

ActivityTaskManagerSercice.startActivity:

  1. IApplicationThread caller : 调用startActivity者的ApplicationThread对象,用来实现两个进程间的通信.
  2. Srting callingPackage : 调用者所属的包
  3. Intent intent : 调用者创建的Intent
  4. IBindet resultTo : 调用startActivity的Activity的IBinder对象.通过这个resuleTo可以找到调用者是谁.

总结: 上面最终的参数就是intent 和 token , 通过intent可以知道目标Activity是谁 , 通过token 可以知道调用者是谁.

接着来到ActivityTaskManagerServicestartActivtyAsUser

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class ActivityTaskManagerService{
private int startActivityAsUser(IApplicationThread caller, String callingPackage,
@Nullable String callingFeatureId, Intent intent, String resolvedType,
IBinder resultTo, String resultWho, int requestCode, int startFlags,
ProfilerInfo profilerInfo, Bundle bOptions, int userId, boolean validateIncomingUser) {
//这里判断当前要启动的包是不是当前用户下的app 见我的博客<< Android账户uid和App的uid的含义 >>
assertPackageMatchesCallingUid(callingPackage);

// ActivityStartController
userId = getActivityStartController().checkTargetUser(userId, validateIncomingUser,
Binder.getCallingPid(), Binder.getCallingUid(), "startActivityAsUser");

return getActivityStartController().obtainStarter(intent, "startActivityAsUser")
.setCaller(caller)
.setCallingPackage(callingPackage)
.setCallingFeatureId(callingFeatureId)
.setResolvedType(resolvedType)
.setResultTo(resultTo)
.setResultWho(resultWho)
.setRequestCode(requestCode)
.setStartFlags(startFlags)
.setProfilerInfo(profilerInfo)
.setActivityOptions(bOptions)
.setUserId(userId)
.execute();

}
}

然后会先判断应用是否属于当前用户组的应用,如果不是就不给启动,然后如果通过验证.就会去构建一个ActivityStarter,然后会将启动的各种值写入到ActivityStarterRequest的成员变量里面.(这里用到了构造者模式) 然后执行execute进行启动.

1
2
3
4
5
6
7
8
9
10
11
12
class ActivityStarter{
int execute(){
// 第一步: 通过token获取对应的ActivityRecord
final ActivityRecord caller = ActivityRecord.forTokenLocked(mRequest.resultTo);
// 这里会去记录Activity启动的初始时间
launchingState = mSupervisor.getActivityMetricsLogger().notifyActivityLaunching(mRequest.intent, caller, callingUid);
// 第二步: RootWindowContainer获取顶层可见的window , 这个很关键,针对他可以干些事情 关于RootWindowContainer知识去看我的博客<<RootWindowContainer>>
final Task rootTask = mRootWindowContainer.getTopDisplayFocusedRootTask();
// 第三步: 去启动Activity
res = executeRequest(mRequest);
}
}

首先第一步通过token值去获取他对应的ActivityRecord对象,这里我们需要知道每一个Activity都有对应他的ActivityRecord对像.
来看下是如何通过Token就能获取到ActivityRecord的:

1
2
3
4
5
6
7
8
//mReqeust.resultTo 是一个IBinder对象,是调用startActivity的那个Activity的IBinder对象
ActivityRecord.forTokenLocked(mRequest.resultTo);

class ActivityRecord{
static ActivityRecord forTokenLocked(IBinder token){
return Token.toTokenActivityRecordLocked((Token)token);
}
}

这里可以看到他将Binder强转成了Token对象了.为什么可以强转,那就要看下Token这个类:

1
2
3
4
5
6
class Token extends IApplicationToken.Stub{
static ActivityRecord tokenToActivityRecordLocked(Token token){
ActivityRecord r = token.weakActivity.get();
return r;
}
}

因为Token就是IBinder的子类,所以可以进行强转.

关于Activity的 mToken:IBinder 值是什么时候传递的这个后面分析

在Android系统中,Activity的生命周期都是由ActivityTaskManagerService来管理的,每个Activity在AMS中都有一个唯一的Token来表示自己.

ActivityTaskManagerService里面创建了RootWindowContainer对象,是怎么创建的呢?

1
2
3
4
5
6
7
8
9
class ActivityTaskManagerService{
RootWindowContainer mRootWindowContainer;
public void setWindowManager(WindowManagerService wm) {
synchronized (mGlobalLock) {
mWindowManager = wm;
mRootWindowContainer = wm.mRoot;
}
}
}

RootWindowContainer 是 Android系统中的顶层窗口容器,他负责管理所有窗口的层级关系.然后每个层级的窗口显示顺序是由 WindowManagerService控制的. 详见博客 < < RootWindowContainer > >

接着看第三步: executeRequest代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
class ActivityStarter{
int executeRequest(Request request){
//request.resultTo这个前面分析了,这个值就是当前触发启动Activity的mToken值.
final IBinder resultTo = request.resultTo;
final IApplicationThread caller = request.caller;
//WindowprocessController管理着手机里面所有的进程信息
WindowProcessController callerApp = null;
//caller就是我们当前app进程的ApplicationThread,然后通过这个值,去我们系统的所有进程里面,找到对应ApplicationThread这个进程的WindowProcessController
callerApp = mService.getProcessController(caller);

ActivityRecord sourceRecord = null;
ActivityRecord resultRecord = null;
if (resultTo != null) {
//通过IBinder去获取对应的Activity他的ActivityRecord对象
//ActivityRecord是一个很重要的对象,他保存了所有关于Activity的信息
sourceRecord = mRootWindowContainer.isInAnyTask(resultTo);
}
//mInterceptor用来拦截Activity的启动的,你可以进行自定义
ActivityStartInterceptor mInterceptor;
// 如果被拦截了,你的Activty就跳转不过去了
if (mInterceptor.intercept(intent, rInfo, aInfo, resolvedType, inTask, callingPid,
callingUid, checkedOptions)) {
//这会重新给你的intent进行赋值,赋值为当前触发startAcvitity的intnet . 比如你从MainActivity 跳转到到B Activity,,,,,那么这个intent就会被重新赋值为MainActivity从而是实现拦截效果
intent = mInterceptor.mIntent;
}
// 这一步骤很关键,针对我们要启动的Activity创建对应他的ActivityRecoed对象.
ActivityRecord r = new ActivityRecord.Builder(mService)
.setCaller(callerApp)
.setLaunchedFromPid(callingPid)
.setLaunchedFromUid(callingUid)
.setLaunchedFromPackage(callingPackage)
.setLaunchedFromFeature(callingFeatureId)
.setIntent(intent)
.setResolvedType(resolvedType)
.setActivityInfo(aInfo)
.setConfiguration(mService.getGlobalConfiguration())
.setResultTo(resultRecord)
.setResultWho(resultWho)
.setRequestCode(requestCode)
.setComponentSpecified(request.componentSpecified)
.setRootVoiceInteraction(voiceSession != null)
.setActivityOptions(checkedOptions)
.setSourceRecord(sourceRecord)
.build();

startActivityUnchecked()
}

private int startActivityUnchecked(final ActivityRecord r, ActivityRecord sourceRecord,
IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
int startFlags, boolean doResume, ActivityOptions options, Task inTask,
boolean restrictedBgActivity, NeededUriGrants intentGrants) {
//在启动activity的时候会去 暂停window布局
activityTaskManagerService.deferWindowLayout();
//启动activity
result = startActivityInner(r, sourceRecord, voiceSession, voiceInteractor,
startFlags, doResume, options, inTask, restrictedBgActivity, intentGrants);
//让window可以开始布局
mService.continueWindowLayout();
}

int startActivityInner(final ActivityRecord r, ActivityRecord sourceRecord,
IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
int startFlags, boolean doResume, ActivityOptions options, Task inTask,
boolean restrictedBgActivity, NeededUriGrants intentGrants){

//目的是获取可重用的任务栈 这个方法很重要,他涉及到了 activity的启动模式
//详细内容见博客<< ActivityStarter.getReusableTask()方法详解 >>
//这里你只需要知道,这里去获取当前要启动的activity需要放的任务栈是谁
final Task reusedTask = getReusableTask();
//如果没有找到可重用的task,那么就执行computeTargetTask()去找task
final Task targetTask = reusedTask != null ? reusedTask : computeTargetTask();
//如果找不到指定的task就说明需要新建一个task了 这时候newTask就等于true
final boolean newTask = targetTask == null;
if (newTask) {
// 如果需要新建task 那么就会去新建一个task
final Task taskToAffiliate = (mLaunchTaskBehind && mSourceRecord != null)
? mSourceRecord.getTask() : null;
setNewTask(taskToAffiliate);
} else if (mAddingToTask) {
// 将要启动的Activity加入到存在的task里面, 这里就是Activity被加入到Task的位置.
addOrReparentStartingActivity(targetTask, "adding to task");
}
// 权限检查
mService.mUgmInternal.grantUriPermissionUncheckedFromIntent(intentGrants,
mStartActivity.getUriPermissionsLocked());
//判断用户是否开启了省电模式
mRootWindowContainer.startPowerModeLaunchIfNeeded(
false /* forceSend */, mStartActivity);
}
private Task mTargetRootTask;

//开启启动acvitity的任务
mTargetRootTask.startActivityLocked(mStartActivity,
topRootTask != null ? topRootTask.getTopNonFinishingActivity() : null, newTask,
mKeepCurTransition, mOptions, sourceRecord);
}

在android12上,Handler会发送一个EXECUTE_TRANSACTIONMessage消息。消息内容ClientTransaction。我们看下这个类:

1
2
3
4
case EXECUTE_TRANSACTION:
final ClientTransaction transaction = (ClientTransaction) msg.obj;
mTransactionExecutor.execute(transaction);
break;
1
2
3
4
5
class ClientTransaction{

private ActivityLifecycleItem mLifecycleStateRequest;

}

ClientTransaction内部有个ActivityLifecycleItem的成员变量,这个类是抽象类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public abstract class ActivityLifecycleItem extends ActivityTransactionItem {
@IntDef(prefix = { "UNDEFINED", "PRE_", "ON_" }, value = {
UNDEFINED,
PRE_ON_CREATE,
ON_CREATE,
ON_START,
ON_RESUME,
ON_PAUSE,
ON_STOP,
ON_DESTROY,
ON_RESTART
})
@Retention(RetentionPolicy.SOURCE)
public @interface LifecycleState{}
public static final int UNDEFINED = -1;
public static final int PRE_ON_CREATE = 0;
public static final int ON_CREATE = 1;
public static final int ON_START = 2;
public static final int ON_RESUME = 3;
public static final int ON_PAUSE = 4;
public static final int ON_STOP = 5;
public static final int ON_DESTROY = 6;
public static final int ON_RESTART = 7;
}

看下他的实现类有哪些 : 可以发现他对应的就是我们的Activity的生命周期.

截图 2023-04-12 15-03-22.png

所以这里倒着往回找,找下每个生命周期是怎么触发的,然后ClientTransaction对象是什么时候创建的.

巴拉巴拉…..

过程太过于复杂,这里我直接用语言描述:

在 Activity 触发 startActivity 方法后, 会进入到 system_server进程,然后调用 ActivityTaskManagerService 的 startActivity 方法, 会将构建的 intent ,当前 Activity 的 token 传入到 ActivityTaskManagerService 内, 然后 ActivityTaskManagerService 会构建 ActivityStarter对象,然后创建 Request 将请求的信息放入到 Request里面, 然后执行了 Request.execute() 方法. 接着会判断当前要启动的app是不是属于当前用户组的,在 android 里面每个用户组拥有的资源是有上限制的10000个. 接着会为我们要启动的 Activity 构建 ActivityRecord 对象, 然后会判断当前 Activity 的启动模式, 如果是 singleInstance 的话,必定会创建新Task,但如果设置了Intent.FLOAG_ACTIVITY_NEW_TASK,但是taskAffinity没有设置不同的值的话,还是不会创建新的Task的. 接着在 TASk 里面会触发 ClientLifeCycleManager.scheduleTransaction() 然后会调用 ApplicationThread.scheduleTransaction()方法,实现从系统进程到用户进程的调用.然后就回到了用户进程,然后完成整个Activity的调用.

回顾整个启动流程,我似乎发现 Activity的启动没有那么复杂,主要就做了这么几件事情,Activity调用startActivity方法,然后将事情交给了ActivtiyTaskManagerService, ActivityTaskMangerService的作用就是用来判断要启动的Acvitiy是不是属于当前用户组,是否有权限,然后判断进程是否要创建,task是否要创建,activtity要放入那个task里面,管理着activity的生命周期.用户进程只需要关心页面怎么展示就行.

Android账户uid和App的uid的含义

总结:
这篇文章你会学到: PackageManagerSercive UserManagerService UserHandle是一个工具类

Android系统中,每个用户都有自己的一套权限和资源,因此需要将系统中的各种资源进行隔离和分配.Android系统给每个用户分配的资源范围是 100000. 每个用户预留了 100000个UID号.比如 第一个用户的UID=0 那么他的资源就是 0 - 999999 . 如果第二个账户UID=1. 那么他的资源范围就是 100000 - 199999. 为什么要这么干因为每个app都有唯一一个UID号.
在App启动的时候,第一个判断就是判断当期要启动的app是不是属于这个用户的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
class ActivityTaskManagerService{
private int startActivityAsUser(IApplicationThread caller, String callingPackage,
@Nullable String callingFeatureId, Intent intent, String resolvedType,
IBinder resultTo, String resultWho, int requestCode, int startFlags,
ProfilerInfo profilerInfo, Bundle bOptions, int userId, boolean validateIncomingUser) {
// 判断 这个包是不是这个用户的
assertPackageMatchesCallingUid(callingPackage);
enforceNotIsolatedCaller("startActivityAsUser");

userId = getActivityStartController().checkTargetUser(userId, validateIncomingUser,
Binder.getCallingPid(), Binder.getCallingUid(), "startActivityAsUser");

// TODO: Switch to user app stacks here.
return getActivityStartController().obtainStarter(intent, "startActivityAsUser")
.setCaller(caller)
.setCallingPackage(callingPackage)
.setCallingFeatureId(callingFeatureId)
.setResolvedType(resolvedType)
.setResultTo(resultTo)
.setResultWho(resultWho)
.setRequestCode(requestCode)
.setStartFlags(startFlags)
.setProfilerInfo(profilerInfo)
.setActivityOptions(bOptions)
.setUserId(userId)
.execute();

}
}
1
2
3
4
5
6
7
8
9
10
11
void assertPackageMatchesCallingUid(@Nullable String packageName) {
// 获取当期app的uid 比如是10106 每个app安装的时候就会被固定分配一个值
final int callingUid = Binder.getCallingUid();
if (isSameApp(callingUid, packageName)) {
return;
}
final String msg = "Permission Denial: package=" + packageName
+ " does not belong to uid=" + callingUid;
Slog.w(TAG, msg);
throw new SecurityException(msg);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
private boolean isSameApp(int callingUid, @Nullable String packageName) {
try {
//SYSTEM_UID 超级管理员是1000
if (callingUid != 0 && callingUid != SYSTEM_UID) {
if (packageName == null) {
return false;
}
//通过包名获取用户的uid
final int uid = AppGlobals.getPackageManager().getPackageUid(packageName,
PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
UserHandle.getUserId(callingUid));
// 最终步骤
return UserHandle.isSameApp(callingUid, uid);
}
} catch (RemoteException e) {
// Should not happen
}
return true;
}

这里就涉及到了另外一个系统级别的服务PackageManagerService

1
2
3
4
5
6
7
8
9
10
11
12
class PackageManagerSercice{
// 找到对应的用户id 一般就0 1 2 这么小的值
public int getPackageUid(String packageName, int flags, int userId) {
//通过系统级别服务判断这个用户存在不
if (!mUserManager.exists(userId)) return -1;
final int callingUid = Binder.getCallingUid();
flags = updateFlagsForPackage(flags, userId);
enforceCrossUserPermission(callingUid, userId, false /*requireFullPermission*/,
false /*checkShell*/, "getPackageUid");
return getPackageUidInternal(packageName, flags, userId, callingUid);
}
}

这里又涉及到了另外一个服务UserManagerSercice,用户判断用户是否存在.

这里就不开枝散叶了,直接看回最终步骤:

1
2
3
4
5
6
7
8
9
UserHandle.isSameApp(callingUid, uid);

public static boolean isSameApp(int uid1, int uid2) {
return getAppId(uid1) == getAppId(uid2);
}

public static @AppIdInt int getAppId(int uid) {
return uid % PER_USER_RANGE;//100000
}

获取anr文件的方法

执行命令:

1
adb bugreport

然后在as里面打开手机文件管理器,在根目录就会看见bugreports文件.