文章目录
- 一、Java之基本知识
- 二、设计模式相关
- 三、Android环境相关
- 四、Android基础
- 4.1 Activity 使用详解
- 4.2 Fragment 使用详解
- 4.3 Service 使用详解
- 4.4 android 数据传递详解(Serialization、Parcelable、Parcel、Intent、Bundle)
- 4.5 自定义View详解
- 4.6 Animation 使用详解
- 4.7 Jni使用详解
- 4.8 HTTP\Socket\SOAP详解
- 4.9 进程保活
- 4.10 AOP
- 4.11 Android动态更换应用Icon之玩转桌面图标
- 五、Android Framework
- 5.1 Message\Handle 源码解析
- 5.2 异步任务AsyncTask 源码解析
- 5.3 Android控件事件转发流程全解析
- 5.4 深入理解setContentView过程和View绘制过程
- 5.5 启动流程详解
- 5.7 从Android源码到apk——apk打包过程
- 5.8 关于Android 64K引发的MultiDex你想知道的都在这里:一场由启动黑屏引发的惨案
- 六、Android Third
- 6.1 RXjava
- 6.2 Volley || ImageLoader
- 6.3 OkHttp
- 6.4 美团页面切分框架Shield源码分析
- 6.5 Push
- 6.6 Cydia Substrate的Hook
- 七、组件化
- 八、插件化
一、Java之基本知识
JVM相关
-
内存区域:
- 【程序计数区,虚拟机栈,本地方法栈】
- 程序计数器:用于指示当前线程所执行的字节码执行到了第几行
- 虚拟机栈:存储的有局部变量表、操作站、动态链接、方法出口等。当方法被调用时,栈帧在JVM栈中入栈,当方法执行完成时,栈帧出栈。
- 本地方法栈:虚拟机栈是执行Java方法的,而本地方法栈是用来执行native方法的
- 【堆区】
- 堆区的存在是为了存储对象实例,原则上讲,所有的对象都在堆区上分配内存
- 堆区是最大的一块,堆区也是Java GC机制所管理的主要内存区域
- 【方法区】:方法区是各个线程共享的区域,用于存储已经被虚拟机加载的类信息(即加载类时需要加载的信息,包括版本、field、方法、接口等信息)、final常量、静态变量、编译器即时编译的代码等。
- 【程序计数区,虚拟机栈,本地方法栈】
-
内存溢出
- 主要是由于代码编写时对某些方法、类应用不合理,或者没有预估到临时对象会占用很大内存量,或者把过多的数据放入JVM缓存,或者性能压力大导致消息堆积而占用内存,以至于在性能测试时,生成庞大数量的临时对象,GC时没有做出有效回收甚至根本就不能回收,造成内存空间不足,内存溢出
-
内存泄漏
- 1、静态集合类像HashMap、Vector等的使用最容易出现内存泄露,这些静态变量的生命周期和应用程序一致,所有的对象Object也不能被释放,因为他们也将一直被Vector等应用着。
- 2、内部类和外部类的引用容易出现内存泄露的问题
- 3、监听器的使用,java中往往会使用到监听器,在释放对象的同时没有相应删除监听器的时候也可能导致内存泄露。
- 4、大量临时变量的使用,没有及时将对象设置为null也可能导致内存的泄露
- 5、数据库的连接没有关闭情况,包括连接池方法连接数据库,如果没有关闭ResultSet等也都可能出现内存泄露的问题。
-
自动GC?:在Java虚拟机中,存在自动内存管理和垃圾清扫机制
- 该机制对 JVM(Java Virtual Machine)中的内存进行标记,并确定哪些内存需要回收,根据一定的回收策略,自动的回收内存,永不停息(Nerver Stop)的保证JVM中的内存空间,防止出现内存泄露和溢出问题。
-
Java GC机制主要完成3件事:
- 确定哪些内存需要回收
- 确定什么时候需要执行GC
- 如何执行GC
-
Java内存分配机制
- 对象将根据存活的时间被分为:年轻代(Young Generation)、年老代(Old Generation)、永久代(Permanent Generation,也就是方法区)
- 年轻代(Young Generation):对象被创建时,内存的分配首先发生在年轻代(大对象可以直接 被创建在年老代),大部分的对象在创建后很快就不再使用,因此很快变得不可达,于是被年轻代的GC机制清理掉
- 年轻代上的内存分配是这样的,年轻代可以分为3个区域:Eden区(伊甸园,亚当和夏娃偷吃禁果生娃娃的地方,用来表示内存首次分配的区域,再 贴切不过)和两个存活区(Survivor 0 、Survivor 1)。
- 绝大多数刚创建的对象会被分配在Eden区,其中的大多数对象很快就会消亡。Eden区是连续的内存空间,因此在其上分配内存极快;
- 当Eden区满的时候,执行Minor GC,将消亡的对象清理掉,并将剩余的对象复制到一个存活区Survivor0(此时,Survivor1是空白的,两个Survivor总有一个是空白的);
- 此后,每次Eden区满了,就执行一次Minor GC,并将剩余的对象都添加到Survivor0;
- 当Survivor0也满的时候,将其中仍然活着的对象直接复制到Survivor1,以后Eden区执行Minor GC后,就将剩余的对象添加Survivor1(此时,Survivor0是空白的)。
- 当两个存活区切换了几次(HotSpot虚拟机默认15次,用-XX:MaxTenuringThreshold控制,大于该值进入老年代)之后,仍然存活的对象(其实只有一小部分,比如,我们自己定义的对象),将被复制到老年代。
- 年老代(Old Generation):对象如果在年轻代存活了足够长的时间而没有被清理掉(即在几次 Young GC后存活了下来),则会被复制到年老代,年老代的空间一般比年轻代大,能存放更多的对象,在年老代上发生的GC次数也比年轻代少。当年老代内存不足时, 将执行Major GC,也叫 Full GC
- 永久代的回收有两种:常量池中的常量,无用的类信息。
- 常量的回收很简单,没有引用了就可以被回收。对于无用的类进行回收,必须保证3点:
- 类的所有实例都已经被回收
- 加载类的ClassLoader已经被回收
3.类对象的Class对象没有被引用(即没有通过反射引用该类的地方)
- 常量的回收很简单,没有引用了就可以被回收。对于无用的类进行回收,必须保证3点:
-
常见回收算法:
- 【数组法】引用技术算法:每个对象都有一个引用计数器。无法解决相互引用
- 【图法】追踪回收算法:维持对象引用图,根节点遍历,孤点被回收。
- 压缩回收算法:将堆中活动对象移动到堆中的一端。
- 复制回收算法:堆分成两个大小相同的区域,只有一个被使用,满了就停止,并把活动对象复制到另一个区域
- 按代回收算法
-
Java代码相关
- finalize在对象被回收前调用
- system.gc()方法通知垃圾回收期运行
ClassLoader相关
Java ClassLoader
-
动态加载?
- Java程序在运行时并不一定被完整加载,只有当发现该类还没有加载时,才去本地或远程查找类的.class文件并验证和加载;
- 当程序创建了第一个对类的静态成员的引用(如类的静态变量、静态方法、构造方法——构造方法也是静态的)时,才会加载该类。Java的这个特性叫做:动态加载。
-
一个类的初始化包括3个步骤:
- 加载(Loading),由类加载器执行,查找字节码,并创建一个Class对象(只是创建);
- 链接(Linking),验证字节码,为静态域分配存储空间(只是分配,并不初始化该存储空间),解析该类创建所需要的对其它类的应用;
- 初始化(Initialization),首先执行静态初始化块static{},初始化静态变量,执行静态方法(如构造方法)。
-
根据java虚拟机规范,所有java虚拟机实现必须在每个类或接口被java程序首次主动使用时才初始化。主动使用有以下6种:
-
- 创建类的实例
-
- 访问某个类或者接口的静态变量,或者对该静态变量赋值(如果访问静态编译时常量(即编译时可以确定值的常量)不会导致类的初始化)
-
- 调用类的静态方法
-
- 反射(Class.forName(xxx.xxx.xxx))
-
- 初始化一个类的子类(相当于对父类的主动使用),不过直接通过子类引用父类元素,不会引起子类的初始化(参见示例6)
-
- Java虚拟机被标明为启动类的类(包含main方法的)
-
-
Bootstrap CLassloder是由C++编写的,它本身是虚拟机的一部分,所以它并不是一个JAVA类,也就是无法在java代码中获取它的引用
- JVM启动时通过Bootstrap类加载器加载rt.jar等核心jar包中的class文件,之前的int.class,String.class都是由它加载
- Bootstrap没有父加载器,但是它却可以作为任何一个ClassLoader的父加载器。比如ExtClassLoader
- 这句话的理解,必须结合后文中的 loadClass()过程,也就是在双亲委托模型中向上迭代父加载器查找时,如果父加载器为null,则jvm内置的加载器去替代,也就是Bootstrap ClassLoader
-
Extention ClassLoader:加载目录%JRE_HOME%\lib\ext目录下的jar包和class文件。
-
AppClassLoader:加载当前应用的classpath的所有类
-
加载器的实例化顺序 源码解析
- java虚拟机的入口应用,sun.misc.Launcher
-
双亲委托:loadClass的实现
- 一个AppClassLoader查找资源时,先看看缓存是否有,缓存有从缓存中获取,否则委托给父加载器。
- 递归,重复第1步的操作。
- 如果ExtClassLoader也没有加载过,则由Bootstrap ClassLoader出面。
- Bootstrap ClassLoader首先查找缓存,如果没有找到的话,就去找自己的规定的路径下,也就是sun.mic.boot.class下面的路径。找到就返回,没有找到,让子加载器自己去找
- Bootstrap ClassLoader如果没有查找成功,则ExtClassLoader自己在java.ext.dirs路径中去查找,查找成功就返回,查找不成功,再向下让子加载器找
- ExtClassLoader查找不成功,AppClassLoader就自己查找,在java.class.path路径下查找。找到就返回。如果没有找到就让子类找
- 如果没有子类会抛出各种异常
-
为什么要使用这种双亲委托模式呢?
- 【重复】因为这样可以避免重复加载,当父亲已经加载了该类的时候,就没有必要子ClassLoader再加载一次。
- 【安全】考虑到安全因素,我们试想一下,如果不使用这种委托模式,那我们就可以随时使用自定义的String.class来动态替代java核心api中定义类型,这样会存在非常大的安全隐患,而双亲委托的方式,就可以避免这种情况,因为String.class已经在启动时被加载,所以用户自定义类是无法加载一个自定义的ClassLoade
-
自定义一个ClassLoader,默认加载路径为D:\lib下的jar包和资源
-
NoClassDefFoundError和ClassNotFoundException
- NoClassDefFoundError:当java源文件已编译成.class文件,但是ClassLoader在运行期间在其搜寻路径load某个类时,没有找到.class文件则报这个错
- ClassNotFoundException:试图通过一个String变量来创建一个Class类时不成功则抛出这个异常
Android ClassLoader
Android 中的 Dalvik/ART 无法像 JVM 那样 直接 加载 class 文件和 jar 文件中的 class,需要通过 dx 工具来优化转换成 Dalvik byte code 才行,只能通过 dex 或者 包含 dex 的jar、apk 文件来加载(注意 odex 文件后缀可能是 .dex 或 .odex,也属于 dex 文件)
因此 Android 中的 ClassLoader 工作就主要交给了 BaseDexClassLoader 来处理
-
系统内置
ClassLoader
是一个抽象类,其中定义了ClassLoader的主要功能。BootClassLoader
是它的内部类,用于预加载preload()常用类,加载一些系统Framework层级需要的类,我们的Android应用里也需要用到一些系统的类等- 与Java中的BootClassLoader不同,它并不是由C/C++代码实现,而是由Java实现的
SecureClassLoader
类和JDK8中的SecureClassLoader类的代码是一样的,它继承了抽象类ClassLoader。SecureClassLoader并不是ClassLoader的实现类,而是拓展了ClassLoader类加入了权限方面的功能,加强了ClassLoader的安全性。URLClassLoader
类和JDK8中的URLClassLoader类的代码是一样的,它继承自SecureClassLoader,用来通过URl路径从jar文件和文件夹中加载类和资源。在Android中基本无法使用
- BaseDexClassLoader继承自ClassLoader,是抽象类ClassLoader的具体实现类,PathClassLoader和DexClassLoader都继承它。
PathClassLoader
加载系统类和应用程序的类,如果是加载非系统应用程序类,则会加载data/app/目录下的dex文件以及包含dex的apk文件或jar文件DexClassLoader
可以加载自定义的dex文件以及包含dex的apk文件或jar文件,也支持从SD卡进行加载InMemoryDexClassLoader
是Android8.0新增的类加载器,继承自BaseDexClassLoader,用于加载内存中的dex文件。
-
用户自定义
- …
-
BaseDexClassLader 寻找 class 的路线就清晰了:
- 当传入一个完整的类名,调用 BaseDexClassLader 的 findClass(String name) 方法
- BaseDexClassLader 的 findClass 方法会交给 DexPathList 的 findClass(String name, List suppressed 方法处理
- 在 DexPathList 方法的内部,会遍历 dexFile ,通过 DexFile 的 dex.loadClassBinaryName(name, definingContext, suppressed) 来完成类的加载
- loadClassBinaryName中调用了Native方法defineClass()加载类
- 标准JVM中,ClassLoader是用defineClass加载类的,而Android中defineClass被弃用了,改用了loadClass方法,而且加载类的过程也挪到了DexFile中,在DexFile中加载类的具体方法也叫defineClass,相信这也是维护代码可读性
-
DexPathList
- BaseDexClassLoader中有个pathList对象,pathList中包含一个DexFile的数组dexElements。dexElements数组就是odex文件的集合
- odex文件是 dexPath指向的原始dex(.apk,.zip,.jar等)文件在optimizedDirectory文件夹中生成相应的优化后的文件
- 如果不分包一般这个数组只有一个Element元素,也就只有一个DexFile文件
-
defineClass()
- 不同于java,
Android ClassLoader#defineClass(String name)
该方法被废弃使用,改为使用DexPathList#findClass(String name)
- 不同于java,
-
Resource的双亲委托模型
-
实例化顺序
- BootClassLoader的创建:main方法是ZygoteInit入口方法,其中调用了ZygoteInit的preload方法,preload方法中又调用了ZygoteInit的preloadClasses方法
- PathClassLoader的创建:ZygoteInit的startSystemServer()方法
进程相关
线程相关
-
新建、就绪、运行和死亡状态
-
不能对已经启动的线程再次调用start()方法,否则会出现java.lang.IllegalThreadStateException异常。
-
如果在一个死去的线程上调用start()方法,会抛出java.lang.IllegalThreadStateException异常。
-
sleep是静态方法,最好不要用Thread的实例对象调用它,因为它睡眠的始终是当前正在运行的线程,而不是调用它的线程对象,它只对正在运行状态的线程对象有效
-
yield()方法它也可以让当前正在执行的线程暂停,让出cpu资源给其他的线程。但是和sleep()方法不同的是,它不会进入到阻塞状态,而是进入到就绪状态
-
join [调用处的当前线程]加入[调用者线程]后面,等待[调用者线程]终止。
- 线程的合并的含义就是将几个并行线程的线程合并为一个单线程执行,应用场景是当一个线程必须等待另一个线程执行完毕才能执行时,Thread类提供了join方法来完成这个功能
-
线程的使用应该在线程运行完后自动结束,但是可以使用interrupt结束一个线程
-
suspend()函数让当前线程进入停滞状态,除非收到resume()信号,否则不唤醒
并发相关
clone相关
注解相关
异常相关
二、设计模式相关
三、Android环境相关
3.1 Gradle
3.2 Proguard混淆
3.3 CI平台 JenKins
四、Android基础
4.1 Activity 使用详解
4.2 Fragment 使用详解
4.3 Service 使用详解
4.4 android 数据传递详解(Serialization、Parcelable、Parcel、Intent、Bundle)
4.5 自定义View详解
4.6 Animation 使用详解
4.7 Jni使用详解
4.8 HTTP\Socket\SOAP详解
4.9 进程保活
4.10 AOP
4.11 Android动态更换应用Icon之玩转桌面图标
五、Android Framework
5.1 Message\Handle 源码解析
5.2 异步任务AsyncTask 源码解析
- 设置当前AsyncTask的状态为RUNNING,上面的switch也可以看出,每个异步任务在完成前只能执行一次。
- 执行了onPreExecute(),当前依然在UI线程,所以我们可以在其中做一些准备工作。
- 将我们传入的参数赋值给了mWorker.mParams ,mWorker为一个Callable的子类,且在内部的call()方法中,调用了doInBackground(mParams),然后得到的返回值作为postResult的参数进行执行;postResult中通过sHandler发送消息,最终sHandler的handleMessage中完成onPostExecute的调用。
- exec.execute(mFuture),mFuture为真正的执行任务的单元,将mWorker进行封装,然后由sDefaultExecutor交给线程池进行执行。
AsyncTask的缺陷,主要就是内部线程池的调度问题。可以分为两个部分说,在3.0以前,最大支持128个线程的并发,10个任务的等待。在3.0以后,无论有多少任务,都会在其内部单线程执行;
5.3 Android控件事件转发流程全解析
-
事件分发链只触及点击位置穿透的控件
-
主要涉及到的函数只有三个:onInterceptTouchEvent、dispatchTouchEvent、onTouchEvent。这个三个方法均有一个 boolean(布尔) 类型的返回值,通过返回 true 和 false 来控制事件传递的流程
-
boolean dispatchTouchEvent(event)实现了整个迭代回调过程,其中调用onInterceptTouchEvent、onTouchEvent和child.dispatchTouchEvent
- Down方式通过dispatchTouchEvent分发,分发的目的是为了找到真正需要处理完整Touch请求的View。当某个View或者ViewGroup的onTouchEvent事件返回true时,便表示它是真正要处理这次请求的View,之后的Aciton_UP和Action_MOVE将由它处理
-
ViewGroup#dispatchTouchEvent 实现 整个分发链和消费链的串联过程
- 事件分发链只触及点击位置穿透的控件,由父到子,由上到下
- 消费链中一旦被消费(返回true)就终止整个事件分发流程
- ViewGroup 和 ChildView 同时注册了事件监听器(onClick等),事件优先给 ChildView,会被 ChildView消费掉,ViewGroup 不会响应。因为 ChildView位于消费链的前端
- onInterceptTouchEvent有两个作用:1.拦截Down事件的分发。2.中止Up和Move事件向目标View传递,使得目标View所在的ViewGroup捕获Up和Move事件
-
View#dispatchTouchEvent 处理单击事件(onClick)、长按事件(onLongClick)、触摸事件(onTouch),和View自身 onTouchEvent 方法的调度流程
- 调度顺序应该是 onTouchListener > onTouchEvent > onLongClickListener > onClickListener
- 给 View 注册 OnTouchListener 不会影响 View 的可点击状态。即使给 View 注册 OnTouchListener ,只要不返回 true 就不会消费事件
- 只要View是CLICKABLE,LONG_CLICKABLE,CONTEXT_CLICKABLE就会消费该点击事件。无论点击回调和长按回调中如何处理,都会消费点击事件(返回true)
- 点击包括很多种情况:譬如给View注册了 onClickListener、onLongClickListener、OnContextClickListener 其中的任何一个监听器或者设置了 android:clickable=”true”
- 某些 View 默认就是可点击的,例如,Button,CheckBox 等
- 所有事件都应该被同一 View 消费
- 安卓对第一次的事件( ACTION_DOWN )进行了特殊判断,View 只有消费了 ACTION_DOWN 事件,才能接收到后续的事件(可点击控件会默认消费所有事件),并且会将后续所有事件传递过来,不会再传递给其他 View,除非上层 View 进行了拦截
- 调度顺序应该是 onTouchListener > onTouchEvent > onLongClickListener > onClickListener
5.4 深入理解setContentView过程和View绘制过程
5.5 启动流程详解
主要涉及进程:
- init进程:linux的根进程,android系统是基于linux系统的,因此可以算作是整个android操作系统的第一个进程
- Zygote(孵化)进程:android系统的根进程,主要作用:可以作用Zygote进程fork出SystemServer进程和各种应用进程;
- SystemServer进程
- 主要是在这个进程中启动系统的各项服务,比如ActivityManagerService,PackageManagerService,WindowManagerService服务等等;
- 各种应用进程
- 启动自己编写的客户端应用时,一般都是重新启动一个应用进程,有自己的虚拟机与运行环境;
Zygote孵化进程启动流程
Zygote(孵化)进程是
- 所有的android进程的父进程,包括SystemServer和各种应用进程都是通过Zygote进程fork出来的,而Zygote进程则是通过linux系统的init进程启动的。
- init进程在启动Zygote进程时一般都会调用ZygoteInit类的main方法
因此我们这里看一下该方法的具体实现(基于android23源码)。
Zygote进程main方法主要执行逻辑:
- 由init进程fork出来的,并且执行其main方法启动
- 初始化DDMS;
- 注册Zygote进程的socket通讯;
- 初始化Zygote中的各种类,资源文件,OpenGL,类库,Text资源等等;
- preloadClasses()用于初始化Zygote中需要的class类;内部涉及 ClassLoader的初始化
- preloadResources()用于初始化系统资源;
- preloadOpenGL()用于初始化OpenGL;
- preloadSharedLibraries()用于初始化系统libraries;
- preloadTextResources()用于初始化文字资源;
- prepareWebViewInZygote()用于初始化webview;
- fork出SystemServer进程,并执行其main()方法启动
- fork出SystemServer进程之后,关闭socket连接;
SystemServer进程启动流程
SystemServer进程主要的作用是
- 启动各种系统服务,比如ActivityManagerService,PackageManagerService,WindowManagerService等服务,我们平时熟知的各种系统性的服务其实都是在SystemServer进程中启动的
- 当我们的应用需要使用各种系统服务的时候其实也是通过与SystemServer进程通讯获取各种服务对象的句柄的进而执行相应的操作的
主要任务:
- 由Zygote进程fork出来的,并且执行其main方法启动
- 设置系统的语言环境等;
- 设置虚拟机运行内存,加载运行库,设置SystemServer的异步消息
- [3.1]主要是优化设置systemserver的内存环境,进程优先级等
- [3.2]创建main looper thread
Looper.prepareMainLooper();
- [3.3]初始化native service
- 创建上下文Context对象mSystemContext
- 使用上下文对象,创建一个新的SystemServiceManager对象,并保存在LocalServices数据结构中(系统各种service服务的实际管理者)
- 可以理解为一个map对象 <XX.Class, instance>
- SystemServer进程在尝试启动服务之前会首先尝试与Zygote建立socket通讯,只有通讯成功之后才会开始尝试启动服务;
- 使用SystemServiceManager实例注册相关SystemService,并执行其onStart()方法开启(每一个SystemService都有自己的异步消息对象,并运行在单独的线程中)
- startBootstrapService()启动系统Boot级服务过程
- 利用反射,实例、注册并开启[安装服务Installer SystemService]
- 实例注册启动ActivityManagerService服务,并为其设置SysServiceManager和Installer
- 实例注册启动PowerManagerService服务,并与mActivityManagerService关联
- 实例注册启动DisplayManagerService服务,负责管理手机显示方面
- 实例注册启动PackageManagerService,负责管理多apk文件的安装,解析,删除,卸载等等操作
- 实例注册启动UserManagerService,负责创建和删除用户,以及查询用户信息
- 实例注册启动SensorServic,负责各种感应器的状态和数值
- 启动系统进程的Application
- startCoreServices()启动系统核心服务
- 实例注册启动LightsService服务,负责管理手机中关于闪光灯,LED等相关的服务
- 实例注册启动BatteryService,负责电池相关服务
- 实例注册启动UsageStatsManagerInternal,负责收集用户使用每一个APP的频率、使用时常
- 实例注册启动WebViewUpdateService,用于WebView的更新
- startOtherServices() 启动一些非紧要或者是非需要及时启动的服务,并启动Launcher app进程
- 实例注册启动一些非紧要或者是非需要及时启动的服务
- 实例注册启动ContentService,负责数据更新通知的管理者,是数据同步服务的管理中枢
- 实例注册启动ContentService,负责手机震动
- 实例注册启动ContentService,负责数据库等提供解决方法的服务
- 实例注册启动AlarmManagerService,负责定时服务
- …
- …
- 实例注册启动一些非紧要或者是非需要及时启动的服务
- 启动Launcher app进程的各种预备动作
- vibrator.systemReady();
- lockSettings.systemReady();
- wm.systemReady();
- mPowerManagerService.systemReady
- mPackageManagerService.systemReady();
- mDisplayManagerService.systemReady
- mActivityManagerService.systemReady
7.1. startSystemUi(context);
7.2 执行各种SystemService的启动方法,各种SystemService的systemReady方法
- startBootstrapService()启动系统Boot级服务过程
- 开启主线程Looper
Looper.loop();
Launcher启动流程
LauncherActivity中是以ListView来显示我们的应用图标列表的,并且为每个Item保存了应用的包名和启动Activity类名,这样点击某一项应用图标的时候就可以根据应用包名和启动Activity名称启动我们的App了
一般系统的启动页面Activity都会在androidmanifest一般系统的启动页面Activity都会在androidmanifest.xml中配置Intent.CATEGORY_HOME常量
Launcher 进程启动过程分析
- Zygote进程
- –> SystemServer进程
- –> startOtherService方法
- SystemServer进程在《startOtherService()启动一些非紧要或者是非需要及时启动的服务,并启动Launcher app进程》的第一阶段【n步启动服务过程】中,会启动LauncherAppService,该服务负责启动Launcher。
- 而后,在《startOtherService()启动一些非紧要或者是非需要及时启动的服务,并启动Launcher app进程》的第二阶段中会调用:mActivityManagerService#systemReady
- –> ActivityManagerService的systemReady方法
- –> startHomeActivityLocked方法
- –> ActivityStackSupervisor的startHomeActivity方法
- –> 执行Activity的启动逻辑,执行scheduleResumeTopActivities()方法
- startActivityLocked启动launcer进程
- scheduleResumeTopActivities启动Activity
应用进程启动流程
- 每一个android应用默认都是在他自己的linux进程中运行。
- android操作系统会在这个android应用中的组件需要被执行的时候启动这个应用进程,并且会在这个应用进程没有任何组件执行或者是系统需要为其他应用申请更多内存的时候杀死这个应用进程。
所以当我们需要启动这个应用的四大组件之一的时候如果这个应用的进程还没有启动,那么就会先启动这个应用程序进程。
Binder
!!!实际上,我们获取的Service对象并不是实际的对象类,而是其代理。运用了代理模式:在触发原对象的同时,还会触发Binder驱动的相关操作
** 代理的对象,是客户端持有的远程服务引用, 实际上甚至这个引用也并不是真正的远程Binder对象,下文讲到**
- getSystemService(getApplication().WINDOW_SERVICE);函数内部原理就是向ServiceManager查询标识符为getApplication().WINDOW_SERVICE的远程对象的引用,该对象继承Binder类
- 客户端持有远程服务的引用(并不是实际真实的远程Binder对象,这个引用实质上是WindowManager的某个代理)
- 客户端调用引用的函数,代理把参数打包到Parcel对象中,然后调用transact函数(该函数继承自Binder)
- Binder驱动转发
- 远程进程服务处理
- Binder驱动转发
- 客户端响应
内部核心源码:
//【核心】向ServiceManager拿到一个远程对象的 Binder驱动
IBinder b = ServiceManager.getService(ACCOUNT_SERVICE);
//【核心】将驱动封装为代理对象
IAccountManager service = IAccountManager.Stub.asInterface(b);
从Activity启动看应用启动
- Instrumentation负责客户端的处理
- 持有 ActivityManagerNative 是服务端ActivityManagerService的本地应用,内部有代理实现binder
- ActvityiManagerService负责服务端的处理
- 内部持有ActivityThread向客户端通信
- ActivityStackSupervisor 栈管理员
- ActivityStack栈
- 【总入口】Activiyt#startActivity()
- 【内部调用】Activiyt#startActivityForResult()
- ps: 经测试requestCode的值小于0的时候都是不起作用的,所以当我们调用startActivityForResult的时候需要注意这一点
- 【分支判断】判断是否有ParentActivity
- 【分支2:有父】Activity#startActivityFromChild() 方法:有,则调用mParent.startActivityFromChild方法
- 【分支1:无父】Instrumentation#execStartActivity() 方法:没有,则启动Binder通信机制,ActivityManagerNative.getDefault().startActivity,远程调用ActivitManagerService
以下 为 进入服务端进程:
- 【分支1入口】ActivityManagerNative.getDefault().startActivity:Binder通信
- 【步骤1】ActivityManagerService#startActivity():被跨进程调用
- 【步骤1内部】ActvityiManagerService#startActivityAsUser()
- 【步骤1内部】ActivityStackSupervisor#startActivityMayWait()
- 【步骤2】ActivityStackSupervisor#startActivityLocked() 方法:相关初始化后,触发该步骤
- 【步骤3】ActivityStackSupervisor#startActivityUncheckedLocked()方法
- 【步骤4】ActivityStack#startActivityLocked()方法:主要执行初始化了windowManager服务,然后调用resumeTopActivitiesLocked方法
- 【步骤5】ActivityStackSupervisor#resumeTopActivitiesLocked()方法:经过循环逻辑判断之后,最终调用了resumeTopActivityLocked方法
- 【步骤6】ActivityStack#resumeTopActivitiesLocked() 方法
- 【步骤6重载调用】ActivityStack.resumeTopActivitiesLocked()
- 【步骤7 核心】** ActivityStack.resumeTopActivityInnerLocked() **:执行当前栈顶Activity的生命周期 或者 判断需要开启进程等
- 【其他步骤】。。。。。
- 【步骤8】调用了ActivityStackSupervisor#startSpecificActivityLocked()方法
- 【分支判断】判断一下需要启动的Activity所需要的应用进程是否已经启动
- 【分支2:进程已启动】ActivityStackSupervisor#realStartAtivityLocked()
- 【分支1:进程未启动】ActivityManagerService#startProcessLocked():启动应用进程
以下 为 进入应用进程:
- 【步骤1】Process#start():传入启动的进程的名称“android.app.ActivityThread”
- 【步骤2】Process#startViaZygote()
- 【步骤3】ActivityThread#main():启动了AcitivtyThread进程并执行了方法
- 【步骤4】 Process#startViaZygote()
- 【步骤5】 Process#zygoteSendArgsAndGetResult():最终调用了Zygote并通过socket通信的方式让Zygote进程fork除了一个新的进程,并根据我们刚刚传递的”android.app.ActivityThread”字符串,反射出该对象并执行ActivityThread的main方法
- 【步骤6】 ActivityThread#main():开启主线程Looper循环等其他操作
- 【步骤7】 ActivityThread#attach():Binder跨进程通信ActivityManagerNative.getDefault().attachApplication()
以下 为 进入服务端进程:
- 【步骤8】ActivityManagerService#attachApplication():跨进程通信被调用
- 【步骤9】ActivityManagerService#attachApplicationLocked():该方法执行了一系列的初始化操作,这样我们整个应用进程已经启动起来了,以确保Activity可以启动
以下 为 进入应用进程:
- 【A.步骤10】thread.bindApplication:biner机制,跨进程通信
- 【A.步骤11】ApplicationThread#bindApplication:biner机制,跨进程通信被调用
- 【A.步骤12】ActivityThread.sendMessage()
- 【A.步骤13】ActivityThread.handleMessage()
- 【A.步骤14】ActivityThread.handleBindApplication():反射机制创建了Instrumentation对象,并执行了init方法,执行了Insrtumentation对象的初始化;调用了LockedApk.makeApplication方法反射创建了Application对象
- 【A.步骤15】Instrumentation#callApplicationOnCreate
- 【A.步骤16】Application#onCreate
以下 为 服务端进程的继续:
- 【B.步骤10】 ActivityStackSupervisor#attachApplicationLocked()
- 【B.步骤11】 ActivityStackSupervisor#realStartActivityLocked():启动Acitivity
Activity启动流程
在Actvity启动过程中,其实是应用进程与SystemServer进程相互配合启动Activity的过程,涉及到多个进程之间的通讯这里主要是ActivityThread与ActivityManagerService之间的通讯,其中:
- 应用进程主要用于执行具体的Activity的启动过程,回调生命周期方法等操作
ActivityThread响应ActivityManagerService的ActivityStack等的远程调用,并触发Instrumentation 进行Activity生命周期管理
使用ActivityManagerNative向ActivityManagerService申请远端通信,并远程调用ActivityStack、ActivityStackSupervisor进行Activity进出栈等操作 - SystemServer进程则主要是调用其中的各种服务,将Activity保存在栈中,协调各种系统资源等操作
ActivityManagerService响应ActivityManagerNative的远程调用,并使用ActivityStack、ActivityStackSupervisor进行管理
使用ActivityThread.IApplicationThread申请远端通信,并远程调用Instrumentation 进行Activity生命周期管理
执行栈顶Activity的onPause方法ActivityStack#startPausingLocked
在《【步骤8】调用了[ActivityStackSupervisor#startSpecificActivityLocked]》前触发
以下为 服务端进程:
- 【第1步】ActivityStack#startPausingLocked():pre.app.thread.schedulePauseActivity方法跨进程调用ActivityThread
以下为 应用进程:
- 【第2步】ActivityThread#schedulePauseActivity():跨进程通信中被调用
- 【第3步】ActivityThread#sendMessage():handler机制触发,发送PAUSE_ACTIVITY_FINISHING消息
- 【第4步】ActivityThread#handleMessage():handler机制响应 响应PAUSE_ACTIVITY_FINISHING
- 【第5步】ActivityThread#handlePauseActivity()
- 【第6步】ActivityThread#performPauseActivity()实现对栈顶Activity的onPause生命周期方法的回调
- 【第6.1步】Instrumentation#callActivityOnPuase()
- 【第6.2步】Activity#performPause() :最终回调到了该方法
- 【第6.3步】Activity#onPause()方法
- 【第7步】ActivityManagerNative.getDefault().activityPaused(token):ActivityThread#handlePauseActivity中会执行,应用进程远程通信告诉服务进程,栈顶Activity已经执行完成onPause方法了
以下为 服务端进程:
- 【第8步】ActivityManagerService#activityPaused()
- 【第9步】ActivityStack#activityPausedLocked()
- 【第10步】ActivityStack#completePauseLocked()
- 【第11步】ActivityStack#resumeTopActivitiesLocked():经过了一系列的逻辑之后,又调用了该方法
- 【第12步】** ActivityStack.resumeTopActivityInnerLocked() **
- 【第13步】ActivityStackSupervisor.startSpecificActivityLocked()
判断一下需要启动的Activity所需要的应用进程是否已经启动- 已启动,realStartAtivityLocked()
- 未启动,ActivityManagerService#startProcessLocked()
Process.start()
Process.startViaZygote()
启动了AcitivtyThread进程并执行了ActivityThread的main方法
ActivityStackSupervisor#startSpecificActivityLocked()启动进程和启动Activity
《【B.步骤11】 ActivityStackSupervisor#realStartActivityLocked():启动Acitivity》
以下为 服务端进程:
- 【第1步】ActivityStackSupervisor#realStartActivityLocked() : Binder机制跨进程通信ActivityThread
以下为 应用进程:
- 【第2步】IApplicationThread#scheduleLauncherActivity() :Binder机制跨进程调用响应,内部handler发送信息
- 【第3步】ActivityThread#sendMessage() :内部handler发送信息,H.LAUNCH_ACTIVITY
- 【第4步】ActivityThread#handleMessage()
- 【第5步】ActivityThread#handleLauncherActivity()
- 【第6步】ActivityThread#performLauncherActivity() :执行Activity的启动操作,需要的Activity对象以反射方式启动,并触发Create和Start
- 【第6.1步】Instrumentation#callActivityOnCreate()
- 【第6.1.1步】 Activity#performCreate()
- 【第6.1.2步】 Activity#onCreate() :第二个生命周期方法出来了,onCreate方法
- 【第6.2步】Activity#performStart():内部调用 Instrumentation
- 【第6.2.1步】 Instrumentation#callActivityOnStart()
- 【第6.2.2步】Activity#onStart() :第三个生命周期方法出来了,onStart方法
- 【第6.1步】Instrumentation#callActivityOnCreate()
- 【第7步】ActivityThread#handleResumeActivity() :触发Resume生命周期、界面绘制流程、原栈顶Activity的onStop
- 【第8步】ActivityThread#performResumeActivity()
- 【第8.1步】Activity#performResume()
- 【第8.2步】Instrumentation#callActivityOnResume()
- 【第8.3步】Activity#onResume() :第四个生命周期方法出现了,onResume方法
- 【第9步】《深入理解setContentView过程和View绘制过程》View绘制前过程
- 【第10步】Looper.myQueue().addIdleHandler(new Idler()):Handle机制,触发空闲消息,用于触发原栈顶Activity的stop
- 【第11步】ActivityManagerNative.getDefault().activityResumed(token) :跨进程通信
- 【第12步】ActivityManagerNative.getDefault().finishActivity() :跨进程通信
栈顶Activity执行onStop方法
以下为 应用进程:
- 【入口】【第7步】ActivityThread#handleResumeActivity() :触发Resume生命周期、界面绘制流程、原栈顶Activity的onStop
- 【第1步】Looper.myQueue().addIdleHandler(new Idler()) :空闲任务,当Messagequeue执行add方法之后就会回调其queueIdle()方法
- 【第2步】ActivityManagerNative.getDefault()#activityIdle() :binder机制,跨进程通信启动
以下为 服务端进程:
- 【第3步】ActivityManagerService#activityIdle() :binder机制,跨进程通信被调用
- 【第4步】ActivityStackSupervisor#activityIdleInternalLocked()
- 【第5步】ActivityStack#stopActivityLocked()
- 【第6步】IApplicationThread#scheduleStopActivity() :Binder机制,跨进程通信
以下为 应用进程:
- 【第7步】ActivityThread#scheduleStopActivity() :Binder机制,跨进程通信被调用
- 【第8步】ActivityThread#sendMessage() :handle机制发送Message
- 【第9步】ActivityThread#handleMessage() :handle机制,响应Message
- 【第10步】ActivityThread#handleStopActivity()
- 【第11步】ActivityThread#performStopActivityInner()
- 【第12步】ActivityThread#callCallActivityOnSaveInstanceState()
- 【第12.1步】 Instrumentation#callActivityOnSaveInstanceState()
- 【第12.2步】 Activity#performSaveInstanceState()
- 【第12.3步】 Activity#onSaveInstanceState()
- 【第13步】Activity#performStop()
- 【第13.1步】Instrumentation#callActivityOnStop()
- 【第13.2步】Activity#onStop()
5.7 从Android源码到apk——apk打包过程
5.8 关于Android 64K引发的MultiDex你想知道的都在这里:一场由启动黑屏引发的惨案
六、Android Third
6.1 RXjava
6.2 Volley || ImageLoader
6.3 OkHttp
6.4 美团页面切分框架Shield源码分析
6.5 Push
6.6 Cydia Substrate的Hook
七、组件化
7.1 阿里开源路由框架ARouter的源码分析
八、插件化
Activity
- hook ActivityStackSupervisor实现:在ActivityStackSupervisor的校验注册环节,把PlugActivity替换为已注册HasRegActivity,实现绕过
- Activity的启动是要经过AMS进行验证的,要判断其是否在manifest里注册过。所以,我们可以事先在manifest里注册一个备用的HasRegActivity,然后在应用进程传递给AMS信息是把里面的Intent掉包,把启动的插件PlugActivity信息替换为manifest里的HasRegActivity,欺骗AMS
- 为在应用进程本地启动Activity最终是调用ActivityManagerNative里的gDefault里的IActivityManager对象的startActivity方法来把信息交给AMS的
- 那我们可以把这个IActivityManager对象给hook掉,替换为我们自己的代理对象,然后修改startActivity方法
- hook ActivityThread的Handler实现:在收到启动HasRegActivity的信号时,反射替换为启动 PlugActivity,从而具备一样的生命周期
- 启动流程分析,这里会到达ActivityThread里来,然后发送一个异步消息给Handler,我们可以hook掉该Handler,修改它里面启动Activity的方法,把真正要启动的Activity换回去
broadcast
注册过程
BroadcastReceiver的注册也是通过AMS完成的:
- 动态:Context类的registerReceiver的真正实现在ContextImpl里面,而这个方法间接调用了registerReceiverInternal
- 对发送者的身份和权限做出一定的校检
- 把这个BroadcastReceiver以BroadcastFilter的形式存储在AMS的mReceiverResolver变量中,供后续使用。
- 静态:系统会通过PackageParser解析Apk中的AndroidManifest.xml文件,因此我们有理由认为,系统会在解析AndroidMafest.xml的标签(也即静态注册的广播)的时候保存相应的信息
发送过程context#sendBroadcast
发送广播也是通过AMS进行的:Context中方法的调用都会委托到ContextImpl这个类,我们直接看ContextImpl对这个方法的实现
receivers = collectReceiverComponents(intent, resolvedType, callingUid, users);
registeredReceivers = mReceiverResolver.queryIntent(intent,
resolvedType, false, userId);
```
1. 广播的匹配AMS#broadcastIntentLocked
1. receivers是对这个广播感兴趣的静态BroadcastReceiver列表;
2. mReceiverResolver存储了动态注册的BroadcastReceiver的信息;
现在系统通过PMS拿到了所有符合要求的静态BroadcastReceiver,然后从AMS中获取了符合要求的动态BroadcastReceiver
### 唤醒过程
唤醒这些广播接受者。简单来说就是回调它们的onReceive方法
AMS跨进程通信
1. 首先创建了一个BroadcastRecord代表此次发送的这条广播,然后把它丢进一个队列,最后通过scheduleBroadcastsLocked通知队列对广播进行处理
2. 在BroadcastQueue中通过Handle调度了对于广播处理的消息,调度过程由processNextBroadcast方法完成,而这个方法通过performReceiveLocked最终调用了IIntentReceiver的performReceive方法
1. 这个IIntentReceiver正是在广播注册过程中由**App进程提供给AMS进程的Binder对象**,现在AMS通过这个Binder对象进行IPC调用通知广播接受者所在进程完成余下操作
2. 在上文我们分析广播的注册过程中提到过,这个IItentReceiver的实现是LoadedApk.ReceiverDispatcher
### hook方式
- 首先调用parsePackage解析到apk对象对应的Package对象Map<ActivityInfo, List<? extends IntentFilter>>
- 读取Package对象里面的receivers字段,注意这是一个 List<Activity> (没错,底层把<receiver>当作<activity>处理)
- 接下来要做的就是根据这个List<Activity> 获取到Receiver对应的 ActivityInfo (依然是把receiver信息用activity处理了)
- 按动态广播方式注册
## Service
过程与Activity基本一样,无非是ActivityThread创建实例时,Service类的创建过程与Activity是略微有点不同的,虽然都是通过ClassLoader通过反射创建,但是Activity却把创建过程委托给了Instrumentation类而Service则是直接进行
### hook方式
我们可以注册一个真正的Service组件ProxyService,让这个Service承载一个真正的Service组件所具备的能力(进程优先级等);当启动插件的服务比如PluginService的时候,我们统一启动这个ProxyService,当这个ProxyService运行起来之后,再在它的onStartCommand等方法里面进行分发,执行PluginService的onStartCommond等对应的方法;
我们把这种方案形象地称为「代理分发技术」
1. 注册代理Service
2. 拦截AMS#startService等调用过程
- 手动控制Service组件的生命周期,需要拦截startService,stopService等调用,并且把启动插件Service全部重定向为启动ProxyService(保留原始插件Service信息);这个拦截过程需要Hook ActvityManagerNative
3. 编写并注册代理ProxyService实现Service匹配和创建分发
- 上文中我们把启动插件Service重定向为启动ProxyService,现在ProxyService已经启动,因此必须把控制权交回原始的PluginService
- 要执行特定插件Service的任务,我们必须把这个任务分发到真正要启动的PluginService上去;
- 在一个map中获取实例,并调用对应方法
我们还是要按照Service的方式实例化 Service,以保证上下文对象的活力
1. 匹配过程
1. 预处理:读取插件中的Service组件信息并存储
- private Map<ComponentName, ServiceInfo> mServiceInfoMap = new HashMap<ComponentName, ServiceInfo>();
2. 匹配本地的mServiceInfoMap缓存确认目标PluginService
2. 创建以及分发
- private Map<String, Service> mServiceMap = new HashMap<String, Service>();
1. 预处理:系统BaseDexClassLoader支持自动加载插件中的Service
2. 模仿`ActivityThread类的handleCreateService`
## ContentProvider
- 应用内部
- 全局系统:借助代理分发
## 使用插件中的R资源
### 具体实现
getResources()获取的就是当前应用的全局Resource对象,然而,插件无论是apk还是so格式的,插件的R.java并没有注册到当前主app的上下文环境
那么我们getResources()所获得全局Resource对象的getXXX(resid)自然就找不到对应的资源路径
- 创建新的Resource对象方式
- 在ResourcesManager的getTopLevelResources方法中创建的
- 获取一个AssetManager实例,使用其“addAssetPath”方法加载APK(里的资源),再使用DisplayMetrics、Configuration、CompatibilityInfo实例一起创建我们想要的Resources实例
- 使用插件的Resources对象,获取资源时,传递的ID必须是离线apk中R文件对应的资源的ID
#### 加载离线apk中的layout资源
```
View view = LayoutInflater.from(context).inflate(R.layout.main_fragment, null);
```
直接传入当前的context是不行的,因为这是两个不同的上下文对象,当前app的context中是找不到这个插件layout的id的
- 1. 创建一个自己的ContextImpl,Override其方法。
- 2. 通过反射,直接替换当前context的mResources私有成员变量
### Resource分发:处理插件资源与宿主资源的处突
AssetManager的addAssetPath()方法调用native层AssetManager对象的addAssetPath()方法,通过查看c++代码可以知道,该方法可以被调用多次,**每次调用都会把对应资源添加起来,而后来添加的在使用资源是会被首先搜索到**
- 插件和宿主的id冲突
- 修改aapt,插件和宿主的R资源的生成规则要不同
- 修改aapt,在插件中如果有添加新的资源,则其命名要安装字典排序在原有的资源下递增
- 隔离使用
### Resource全局置换:确保插件和宿主使用到的是被修改过的资源
- [方法1]:hook ResourceManager
在Resources中定位到getString(int id)方法,最终寻找资源的调用是有AssetManager来执行的,这个AssetManager是 ResourceManager#getTopLevelResources()创建Resources时来的
需要注意的是:`getTopLevelResources()`中具备缓存逻辑
由于ResourceManager是一个单例类,并且持有了当前App的Resource缓存,那么我们直接在App启动时手动替换掉ResourceManager中的Resource缓存,就可以在当前App中添加插件的资源,并且全局有效
- [方法2]:hook 基础组件的生命周期函数
替换掉Activity里Context里的Resources最好要早,基于上面的观察,我们可以在调用Instrumentation的callActivityOnCreate()方法时把Resources替换掉
# 九、性能监控
## 9.1 内存泄漏检测LeakCanary源码分析
## 9.2 AndroidGodEye源码整体结构分析
## 9.3 AndServer手机服务器