Android Framework:关于AMS的那点事

Posted 初一十五啊

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android Framework:关于AMS的那点事相关的知识,希望对你有一定的参考价值。

前言

所谓的ASM,其实就是如何生成一个Class文件或者修改一个Class文件的工具,包括对Class里的成员变量或者方法进行增加或修改。相比于Javassist,ASM最大的好处就是性能方面优于Javassist,但随之带来的就是需要开发者精通 class 文件格式和 JVM 指令集。

关注公众号:初一十五a
解锁 《Android十三大板块文档》,让学习更贴近未来实战。已形成PDF版

内容如下

1.2022最新Android11位大厂面试专题,128道附答案
2.音视频大合集,从初中高到面试应有尽有
3.Android车载应用大合集,从零开始一起学
4.性能优化大合集,告别优化烦恼
5.Framework大合集,从里到外分析的明明白白
6.Flutter大合集,进阶Flutter高级工程师
7.compose大合集,拥抱新技术
8.Jetpack大合集,全家桶一次吃个够
9.架构大合集,轻松应对工作需求
10.Android基础篇大合集,根基稳固高楼平地起
11.Flutter番外篇:Flutter面试+Flutter项目实战+Flutter电子书
12.高级Android组件化强化实战
13.十二模块之补充部分:其他Android十一大知识体系

敲代码不易,关注一下吧。开始进入正题,ღ( ´・ᴗ・` ) 🤔

一丶用ASM生成Class文件

引入ASM工具库

//核心库

implementation 'org.ow2.asm:asm:9.3'
//辅助库
implementation 'org.ow2.asm:asm-commons:9.3'
首先,先简单生成一个Class文件。运行以下代码,就可以直接生成一个Class文件。

1.1.生成Class 字节码

public static byte[] genClass() 
    ClassWriter classWriter = new ClassWriter(0);
    classWriter.visit(V1_8, ACC_PUBLIC | ACC_SUPER, "asm/User",null, "java/lang/Object", null); 

        MethodVisitor methodVisitor = classWriter.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
        methodVisitor.visitCode();
        Label label0 = new Label();
        methodVisitor.visitLabel(label0);
        methodVisitor.visitLineNumber(3, label0);
        methodVisitor.visitVarInsn(ALOAD, 0);
        methodVisitor.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
        methodVisitor.visitInsn(RETURN);
        Label label1 = new Label();
        methodVisitor.visitLabel(label1);
        methodVisitor.visitLocalVariable("this", "Lasm/User;", null, label0,label1, 0);
        methodVisitor.visitMaxs(1, 1);
        methodVisitor.visitEnd();
    
    classWriter.visitEnd();
    return classWriter.toByteArray();

1.2.将生成的Class字节码输出

public static void main(String[] args) throws Exception 
    //1.生成Class字节码
    byte[] genClassByte = genClass();
    try 
        //输出Class字节码文件
        FileOutputStream fos = new FileOutputStream("src/asm/User.class");
        fos.write(genClassByte);
        fos.close();
     catch (Exception e) 
        e.printStackTrace();
    

效果如下:

...省略
package asm;

public class user 
    public user() 
    

可以看到,相比与Javassist,复杂的不是一点两点。我们可以把上面的代码区分为两部分。

分别是ASM工具的使用和Java字节码。如下:

ASM工具的使用,例如ClassWriter,MethodVisitor等

Java字节码,例如: “” 代表的是初始化构造方法,“()V” 代表的是这个方法无参并且无返回参数。

接下来我们介绍ClassWriter,MethodVisitor类的用法,以及一些JVM字节码知识,高能预警!!

1.3.ClassWriter

很明显,这是一个对Class进行写入操作的类,例如对Class文件添加变量,方法。我们先看看他的构造方法。
构造方法

ClassWriter classWriter = new ClassWriter(0);
public ClassWriter(final int flags)

关于flags的传值如下:

  • flag == 0, 你必须自己计算栈帧和局部变量以及操作数堆栈的大小 ,也就是你要自己调用visitmax和visitFrame方法。
  • flag == ClassWriter. COMPUTE_MAXS,局部变量和操作数堆栈部分的大小会为你计算,还需要调用visitFrame方法设置栈帧。
  • flag == ClassWriter.COMPUTE_FRAMES,所有的内容都是自动计算的。 你不必调用visitFrame和visitmax。换句话说,COMPUTE_FRAMES包含

注意:使用ClassWriter. COMPUTE_MAXS他会使得ClassWriter的速度慢10%,ClassWriter.COMPUTE_FRAMES会慢20%。

visit方法

作用:用来定义类的属性

方法使用如下:

例如:生成一个User类。

    classWriter.visit(
            V1_8,
            ACC_PUBLIC | ACC_SUPER,
            "asm/User",
            null,
            "java/lang/Object",
            null);

源代码如下:

public final void visit(
     final int version,
     final int access,
     final String name,
     final String signature,
     final String superName,
     final String[] interfaces)

参数解释
version:Java版本号,例如 V1_8 代表Java 8
access:Class访问权限,一般默认都是 ACC_PUBLIC | ACC_SUPER,部分字段解释如下

  1. name: Class文件名,例如:asm/User,包名加类名
  2. signature:类的签名,除非你是泛型类或者实现泛型接口,一般默认null。
  3. superName:继承的类,很明显所有类默认继承Object。例如:java/lang/Object ,如果是继承
    自己写的类Animal,那就是 asm/Animal
  4. interfaces:实现的接口,例如实现自己写的接口IPrint,那就是new String[]"asm/IPrint"

visitMethod方法

作用:用来定义类的方法

方法使用如下:

例如:生成User的构造方法 public User()

classWriter.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);

源代码如下:

public final MethodVisitor visitMethod(
    final int access,
    final String name,
    final String descriptor,
    final String signature,
    final String[] exceptions)

参数解释
access:方法的访问权限,也就是public,private等
name: 方法名
在Class中,有两个特殊方法名。和,其他的方法都是正常对应Java方法名。

代表的是类的实例构造方法初始化,也就是new 一个类时,肯定会调用的方法。

代表的类的初始化方法,不同于,它不是显示调用的。因为Java虚拟机会自动调用,并且保证在子类的前调用父类的。也就是说,Java虚拟机中,第一个被执行方法的肯定是java.lang.Object。

注意:和执行的时机不一样,的时机早于,是在类被加载阶段的初始化过程中调用方法,而方法的调用是在new一个类的实例的时候。

descriptor:方法的描述符
所谓方法的描述符,就是字节码对代码中方法的形参和返回值的一个描述。其实就是一个一一对应的模板。如下:

(IF)V =(表示方法的形参类型描述符)方法的返回值

关于形参的类型描述符如下:

方法描述符如下:

signature:方法签名,除非方法的参数、返回类型和异常使用了泛型,否则一般为 null。

exceptions:方法上的异常。这里我们没有抛出任何异常,所以为null。如果throws Exception,exceptions的值为new String[]"java/lang/Exception"

visitField方法

作用:用来定义一个变量

方法使用如下:

例如:生成一个 private int a = 10;

classWriter.visitField(ACC_PRIVATE, "a", "I", null, null);

源代码如下:

public final FieldVisitor visitField(
    final int access,
    final String name,
    final String descriptor,
    final String signature,
    final Object value)

参数解释

  • access:变量的访问权限,,也就是public,private等
  • name:变量名
  • descriptor:变量的描述符,可以参考上面的Java类型对应的描述符
  • signature:变量的签名,如果没有使用泛型则为null
  • value:变量的初始值。这个字段仅作用于被final修饰的字段,或者接口中声明的变量。其他默认为null,变量的赋值是通过MethodVisitor 的 visitFieldInsn方法。

visitEnd方法

作用:用来通知Class已经使用完。

toByteArray方法

作用:返回一个字节数组

1.4.MethodVisitor

这是一个用来生成方法的类。

visitCode方法

作用:表示开始生成字节码

通常第一个调用,固定格式

visitLabel方法

作用:设置Label,Label的作用相当于表示方法在字节码中的位置每一个方法都需要一个Label,用来保证方法调用顺序。

visitLineNumber方法

作用:定义源代码中的行号与对应的指令

源代码如下:

public void visitLineNumber(final int line, final Label start)
  • line:源代码中对应的行号
  • start:行号对应的字节码指令

visitVarInsn方法

作用:用来对变量进行加载和存储的指令操作

源代码如下:

public void visitVarInsn(final int opcode, final int varIndex)

参数解释:

opcode:对应的变量字节码指令

例如:获取一个int数值类型的指令对应 iload 0

有获取就肯定会有存储

  • varIndex:变量对应在局部变量表的下标
    例如下列代码:
int a = 1;
int b = 2;
int d = a + b;

上面代码对应的字节码指令就是

L5
   LINENUMBER 13 L5
   ICONST_1
   ISTORE 1
L6
   LINENUMBER 14 L6
   ICONST_2
   ISTORE 2
L7
  LINENUMBER 15 L7
  ILOAD 1
  ILOAD 2
  IADD
  ISTORE 3

解释下对应的字节码含义:

  1. 将1变量加载到操作数栈,对应的指令就是ICONST_1
  2. 将栈顶的值保存到局部变量表第一个位置,对应的指令就是ISTORE_1
  3. 将2变量加载到操作数栈,对应的指令就是ICONST_2
  4. 将栈顶的值保存到局部变量表第二个位置,对应的指令就是ISTORE_2
  5. 取出局部变量表第一个元素到操作数栈(也就是变量a),对应的指令就是 ILOAD_1
  6. 取出局部变量表第二个元素到操作数栈(也就是变量b),对应的指令就是 ILOAD_2
  7. 此时操作数栈的栈顶就有a和b两个元素,执行指令IADD,就会把栈顶的两个元素相加并将结果压入栈顶
  8. 将栈顶的值保存到局部变量表第三个位置

visitMethodInsn方法

作用:用来对一个方法执行指令操作

他可以执行的指令如下:

visitInsn方法

作用:用来执行对操作数栈的指令

可以执行的指令如下:

NOP, ACONST_NULL, ICONST_M1, ICONST_0, ICONST_1, ICONST_2, ICONST_3, ICONST_4,
ICONST_5, LCONST_0, LCONST_1, FCONST_0, FCONST_1, FCONST_2, DCONST_0, DCONST_1,
IALOAD, LALOAD, FALOAD, DALOAD, AALOAD, BALOAD, CALOAD, SALOAD, IASTORE, LASTORE,
FASTORE, DASTORE, AASTORE, BASTORE, CASTORE, SASTORE, POP, POP2, DUP, DUP_X1, DUP_X2,
DUP2, DUP2_X1, DUP2_X2, SWAP, IADD, LADD, FADD, DADD, ISUB, LSUB, FSUB, DSUB, IMUL,
LMUL, FMUL, DMUL, IDIV, LDIV, FDIV, DDIV, IREM, LREM, FREM, DREM, INEG, LNEG, FNEG, DNEG,
ISHL, LSHL, ISHR, LSHR, IUSHR, LUSHR, IAND, LAND, IOR, LOR, IXOR, LXOR, I2L, I2F, I2D, L2I, L2F,
L2D, F2I, F2L, F2D, D2I, D2L, D2F, I2B, I2C, I2S, LCMP, FCMPL, FCMPG, DCMPL, DCMPG, IRETURN,
LRETURN, FRETURN, DRETURN, ARETURN, RETURN, ARRAYLENGTH, ATHROW, MONITORENTER, or
MONITOREXIT.

部分指令说明如下:
具体如下:

visitLocalVariable方法

作用:对局部变量设置

源代码如下:

public void visitLocalVariable(
    final String name,
    final String descriptor,
    final String signature,
    final Label start,
    final Label end,
    final int index)

参数解释:

  • name:局部变量名
  • descriptor:局部变量名的类型描述符
  • signature:局部变量名的签名,如果没有使用到泛型,则为null
  • start:第一个指令对应于这个局部变量的作用域(包括)。
  • end:最后一条指令对应于这个局部变量的作用域(排他)。
  • index:局部变量名的下标,也就是局部变量名的行号顺序(从1开始)。

例如代码:

public void test()
    int a = 1;
    int b = 2;
    int d = a + b;

对应的使用方法如下:

methodVisitor.visitLocalVariable("this", "Lasm/User;","Lasm/User<TT;>;", label0, label4, 0);
methodVisitor.visitLocalVariable("a", "I", null, label1, label4, 1);
methodVisitor.visitLocalVariable("b", "I", null, label2, label4, 2);
methodVisitor.visitLocalVariable("d", "I", null, label3, label4, 3);

注意:每个方法都会默认有一个this的引用

visitMaxs方法

作用:设置这个本地方法最大操作数栈和最大本地变量表

源代码如下:

public void visitMaxs(final int maxStack, final int maxLocals)

例如代码:

public void test()
    int a = 1;
    int b = 2;
    int d = a + b;

对应的使用方法如下:

methodVisitor.visitMaxs(2, 4);

其中

  • maxStack == 2,分别是,ICONST_1, IAdd操作

注意:maxStack == 2,不是代表只有两个对操作数栈的指令,而是操作数栈容量大小为2,可以满足上面代码,例如下面代码,操作数栈大小为2也可以满足。

public void test()
    int a = 1;
    int b = a + 2;
    int c = b * 2;
    int d = b * 2;
    int e = b * 2;
    int f = b * 2;

再来一个例子, 它的操作数栈大小必须为3。

分别是,DUP, INVOKESPECIAL,ASTORE操作

public void test()
    Object user = new Object();

  • maxLocals == 4,分别是局部变量this,a,b,d。

这些值的作用其实是用来决定操作数栈和本地变量表的大小,内存优化小知识😀

注意:visitMaxs方法的调用必须在所有的MethodVisitor指令结束后调用。

visitEnd方法

作用:通知MethodVisitor生成方法结束,表示结束生成字节码。

通常是作为MethodVisitor最后一个调用,固定格式。visitCode,一个最前一个最后。

二丶 如何使用生成的代码

以下代码可以直接复制,简单修改就可以测试自己的生成的Class文件。

public class ASMDemo 

    public static void main(String[] args) throws Exception 
        //1.生成Class字节码
        byte[] genClassByte = genClass();
        try 
            //输出Class字节码文件
            FileOutputStream fos = new FileOutputStream("src/asm/User.class");
            fos.write(genClassByte);
            fos.close();
         catch (Exception e) 
            e.printStackTrace();
        
        testClass("asm.User",genClassByte);
    

    public static byte[] genClass() 
        ClassWriter classWriter = new ClassWriter(0);
        classWriter.visit(
                V1_8,
                ACC_PUBLIC | ACC_SUPER,
                "asm/User",
                null,
                "java/lang/Object",
                null); 
            MethodVisitor methodVisitor = classWriter.visitMethod(
                    ACC_PUBLIC,
                    "<init>",
                    "()V",
                    null,
                    null);
            methodVisitor.visitCode();
            Label label0 = new Label();
            methodVisitor.visitLabel(label0);
            methodVisitor.visitLineNumber(3, label0);
            methodVisitor.visitVarInsn(ALOAD, 0);
            methodVisitor.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
            methodVisitor.visitInsn(RETURN);
            Label label1 = new Label();
            methodVisitor.visitLabel(label1);
            methodVisitor.visitLocalVariable("this", "Lasm/User;", null, label0,label1, 0);
            methodVisitor.visitMaxs(1, 1);
            methodVisitor.visitEnd();
      
      classWriter.visitEnd();
      return classWriter.toByteArray();
  

  /**
   * 测试运行Class
   *
   * @param name Class完整包名路径
   * @param b Class字节码
   * @throws Exception
   */
  public static void testClass(String name, byte[] b) throws Exception 
      MyClassLoader myClassLoader = new MyClassLoader();
      Class clazz = myClassLoader.defineClass(name, b);
      Object obj = clazz.newInstance();

      //仅为了测试
      for (java.lang.reflect.Method method : clazz.getMethods()) 
          System.out.println("Method Name:" + method.getName());
          if (method.getName().equals("main")) 
              method.invoke(obj, new String[1]);
              break;
          
          if (method.getName().equals("hashCode")) 
              Integer code = (Integer) method.invoke(obj, null);
              System.out.println("hashCode code:"+code);
          
      
  
  
  public static class MyClassLoader extends ClassLoader
      public Class defineClass(String name, byte[] b) 
          return defineClass(name, b, 0, b.length);
      
   

三丶如何快速方便生成Class文件

看到这里,相信你会有一个疑惑?如果不懂这些字节码那要如何编写,不用担心,有一个ASM Bytecode Viewer可以帮助我们解决这个问题。只要完成下面几步,你也能快速生成一个Class文件。

3.1.下载 ASM Bytecode Viewer 插件

这个就不过多描述了,在Plugins中自己搜索吧

3.2. 准备好你要写的Java文件

例如你想要生成如下代码:

public class User
    public int b;
    public User(int age) 
    

    public void test()
        Object user = new Object();
    

接下来就是使用 ASM Bytecode Viewer,步骤如下

就可以看到如下界面,下面是我们Java代码真正的字节码内容,但是这不是我们需要的。继续往下看

// class version 52.0(52)
// access flags 0x21
public class asm/User 

  // compiled from:User.java

  // access flags 0x1
  public I b
  
  // access flags 0x1
  public <init>(I)v
   L0
     LINENUMBER 5 L0
     ALOAD 0
     INVOKESPECIAL java/lang/object.<init> ()v
   L1
      LINENUMBER 6 L1
      RETURN
   L2
      LOCALVARIABLE this Lasm/User; L0 L2 0
      LOCALVARIABLE age I L0 L2 1
      MAXSTACK = 1
      MAXSTACK = 2

看到下面,这才是我们需要的。注意是ASMified,然后直接复制dump方法,调用就能生成一个Class的字节码数组,然后后面就是把字节码数组保存成本地,代码上面有。

pack asm.asm;

import org.objectweb.asm.Annotationvisitor;
import org.objectweb.asm.Attribute;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.ConstantDynamic;
import org.objectweb.asm.Fieldvisitor;
import org.objectweb.asm.Handle;
import org.objectweb.asm.Label;
import org.objectweb.asm.Methodvisitor;
import org.objectweb.asm.opcodes;
import org.objectweb.asm.Type;
import org.objectweb.Typepath;

public class UserDump implements opcodes 
    
    public static byte[] dump() throws Exception 
 
        ClassWriter classwriter = new classwriter(0);
        Fieldvisitor fieldvisitor;
        Methodvistor methodvisitor;
        Annotationvisitor annotationvisition0;

        classwriter.visit(V1_8,ACC_PUBLIC 丨 ACC_SUPER, ''asm/User'',null,''java/lang/object'',null);

       classwriter.visitsource(''User.java'',null); 
           fieldvisitor = classwriter.visitField(ACC_PUBLIC,''B'',''I'',null,null);
           fieldvisitor.visitEnd();
       

四丶android Framework源码-AMS

AMS(ActivityManagerService) 在SystemServer的进程中,是SystemServer中的一个对象;

作用:

  • 管理activity的生命周期

  • 启动activity

  • 与PMS进行交互

    Activity->AMS:

  • 调用activity.startActivity()

  • 通过ActivityManage.getService("activity")得到AMS的BpBinder;

  • 通过BpBinder发送请求,调用AMS的startActivity()

    AMS->PMS:

  • AMS和PMS都在SystemServer进程中,都是SystemServer中一个对象

  • 通过包名和PMS里的缓存mPackage查询到App对应的Package

  • 使用activity的类名通过PMS里的内部类PackageManagerInternalImpl查询到activity对应的包装类ResolveInfo; ps:ResolveInfo这个javabean里有activityInfo、ServiceInfo等变量,查询啥就给哪个变量赋值,再返回ResolveInfo;

  • 得到ResolveInfo里的activityInfo;

  • activityInfo返回给App进程的ActivityThread;`

  • ActivityThread中发送事件

  • ActivityThread中的Handler对象mH收到159事件,处理

  • 通过反射创建Activity对象

  • 将Activity对象放到activtes启动记录中

ActivityThread

  • 每个应用有一个ActivityThread;是应用的入口;

  • 在APP进程中

  • 是AMS的缓存中心

  • ActivityThread中的List activtes放了activity的启动记录

ActivityThread中重要的对象:

  • ApplicationThread:AMS回调给ActivityThread数据的桥梁

  • mInstrumentation:管理Application和Activity的生命周期(及创建)

  • mH:Handler,处理ApplicationThread里各种回调函数发送的各种消息

点击桌面App图标发生了什么?

  1. 点击的APP图标是在单独的Luancher进程,是一个系统App进程

  2. Luancher进程请求SystemServer进程中的AMS去创建应用的根Activity(AndroidMnifest.xml中initen-fifter为Luanche的activity)

  3. AMS通过包名让PMS查询到相关应用信息,得到应用的Package;

  4. AMS创建activity栈,根据Package拿到根activity的配置节点信息,放到栈中,此时栈中只有一个根activity的配置节点信息,也就是在栈顶;(此处的栈不是应用层的栈,这个栈只是用来放activity节点信息的)

  5. AMS请求zygote进程创建App进程;zygote进程比较特殊, 使用Socket通信,而不是binder;zygote是所有应用的孵化器,zygote进程挂掉时,手机会自动重启;

  6. zygote进程去fork出App进程;

  7. APP进程中的主线程调用ActivityThread.main()静态函数,main中创建 ActivityThread对象

  8. 接着在ActivityThread.attch()中创建了一个ApplicationThread对象,作为和AMS通信时,返回结果的桥梁;

  9. App进程通过AMS的binder调用attachApplication(thread)请求AMS获取应用对应的Applaction和栈顶中activity节点信息(步骤4),此时给AMS传过去了一个thread,这个thread就是ApplicationThread

  10. AMS将从PMS查到的application节点数据序列化后,调用thread.bindApplaction(data数据…)传给ActivityThread; (此时代码还会继续往下执行,去获取栈顶activity的节点信息)

  11. ActivityThread调用sendMessage发送消息BIND_APPLICATION(110)给Handler,Handler调用handleBindApplication(data)

  12. 通过反射实例化Instrumentation对象:负责application和activity的生命周期的管理

  13. 通过Instrumentation对象反射实例化new Applaction对象app

  14. 调用Instrumentation.callApplactionOnCreate(app)

  15. 执行Applaction.onCreate()

  16. 步骤10中AMS继续向下执行查找activity,AMS将查到的栈顶根Activity(LaunchActivity )信息封装到一个事务ClientTransaction中,提交事务并执行,在执行中,调用thread.scheduleTransaction(事务数据);(thread为ActivityThread中的ApplicationThread

  17. ApplicationThread回调scheduleTransaction函数中,发送`EXECUTE_TRANSACTION(159)消息

  18. Handler处理EXECUTE_TRANSACTION消息,从事务数据中取出LaunchActivity信息,并调用hanldeLaunchActivity(activity数据)

  19. 通过Instrumentation对象反射实例化newActivity()出对象activity

  20. 执行activity.attach(),在attach中创建WMS的桥接代理类;(绘制流程会用到)

  21. 通过Instrumentation调用callActivityOnCreate(activity)

  22. 执行Activty.onCreate();

  23. 至此启动页根Activity启动完成;

下图中4-5中少了上面7-23的步骤:

7-15创建并启动了Application;

16-22创建并启动了Activity;

应用内activity与activity的跳转是跨进程通信,还是同一个进程内通信?

是跨进程通信;

跳转流程参考上面的:省去了application的创建过程;

步骤3 +步骤16-23;

关注公众号:初一十五a
解锁 《Android十三大板块文档》,让学习更贴近未来实战。已形成PDF版

内容如下

1.2022最新Android11位大厂面试专题,128道附答案
2.音视频大合集,从初中高到面试应有尽有
3.Android车载应用大合集,从零开始一起学
4.性能优化大合集,告别优化烦恼
5.Framework大合集,从里到外分析的明明白白
6.Flutter大合集,进阶Flutter高级工程师
7.compose大合集,拥抱新技术
8.Jetpack大合集,全家桶一次吃个够
9.架构大合集,轻松应对工作需求
10.Android基础篇大合集,根基稳固高楼平地起
11.Flutter番外篇:Flutter面试+Flutter项目实战+Flutter电子书
12.高级Android组件化强化实战
13.十二模块之补充部分:其他Android十一大知识体系

敲代码不易,关注一下吧。ღ( ´・ᴗ・` ) 🤔

以上是关于Android Framework:关于AMS的那点事的主要内容,如果未能解决你的问题,请参考以下文章

Android Framework——AMS、PMS的启动流程

Android FrameWork(AMS,WMS,PMS等)的概念及解析,获取系统服务

Android进阶——WMS与AMS浅析

android 11/12的 framework 框架systemserver源码中的AMS和WMS部分ProtoLog相关log的开放命令

android 11/12的 framework 框架systemserver源码中的AMS和WMS部分ProtoLog相关log的开放命令

Android四大组件framework层